--- /dev/null
+<?php
+
+namespace Drupal\node;
+
+use Drupal\Component\Datetime\TimeInterface;
+use Drupal\Core\Entity\ContentEntityForm;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\user\PrivateTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Form handler for the node edit forms.
+ */
+class NodeForm extends ContentEntityForm {
+
+ /**
+ * The tempstore factory.
+ *
+ * @var \Drupal\user\PrivateTempStoreFactory
+ */
+ protected $tempStoreFactory;
+
+ /**
+ * Constructs a NodeForm object.
+ *
+ * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+ * The entity manager.
+ * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
+ * The factory for the temp store object.
+ * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
+ * The entity type bundle service.
+ * @param \Drupal\Component\Datetime\TimeInterface $time
+ * The time service.
+ */
+ public function __construct(EntityManagerInterface $entity_manager, PrivateTempStoreFactory $temp_store_factory, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL) {
+ parent::__construct($entity_manager, $entity_type_bundle_info, $time);
+ $this->tempStoreFactory = $temp_store_factory;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('entity.manager'),
+ $container->get('user.private_tempstore'),
+ $container->get('entity_type.bundle.info'),
+ $container->get('datetime.time')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function form(array $form, FormStateInterface $form_state) {
+ // Try to restore from temp store, this must be done before calling
+ // parent::form().
+ $store = $this->tempStoreFactory->get('node_preview');
+
+ // Attempt to load from preview when the uuid is present unless we are
+ // rebuilding the form.
+ $request_uuid = \Drupal::request()->query->get('uuid');
+ if (!$form_state->isRebuilding() && $request_uuid && $preview = $store->get($request_uuid)) {
+ /** @var $preview \Drupal\Core\Form\FormStateInterface */
+
+ $form_state->setStorage($preview->getStorage());
+ $form_state->setUserInput($preview->getUserInput());
+
+ // Rebuild the form.
+ $form_state->setRebuild();
+
+ // The combination of having user input and rebuilding the form means
+ // that it will attempt to cache the form state which will fail if it is
+ // a GET request.
+ $form_state->setRequestMethod('POST');
+
+ $this->entity = $preview->getFormObject()->getEntity();
+ $this->entity->in_preview = NULL;
+
+ $form_state->set('has_been_previewed', TRUE);
+ }
+
+ /** @var \Drupal\node\NodeInterface $node */
+ $node = $this->entity;
+
+ if ($this->operation == 'edit') {
+ $form['#title'] = $this->t('<em>Edit @type</em> @title', ['@type' => node_get_type_label($node), '@title' => $node->label()]);
+ }
+
+ // Changed must be sent to the client, for later overwrite error checking.
+ $form['changed'] = [
+ '#type' => 'hidden',
+ '#default_value' => $node->getChangedTime(),
+ ];
+
+ $form = parent::form($form, $form_state);
+
+ $form['advanced']['#attributes']['class'][] = 'entity-meta';
+
+ // Node author information for administrators.
+ $form['author'] = [
+ '#type' => 'details',
+ '#title' => t('Authoring information'),
+ '#group' => 'advanced',
+ '#attributes' => [
+ 'class' => ['node-form-author'],
+ ],
+ '#attached' => [
+ 'library' => ['node/drupal.node'],
+ ],
+ '#weight' => 90,
+ '#optional' => TRUE,
+ ];
+
+ if (isset($form['uid'])) {
+ $form['uid']['#group'] = 'author';
+ }
+
+ if (isset($form['created'])) {
+ $form['created']['#group'] = 'author';
+ }
+
+ // Node options for administrators.
+ $form['options'] = [
+ '#type' => 'details',
+ '#title' => t('Promotion options'),
+ '#group' => 'advanced',
+ '#attributes' => [
+ 'class' => ['node-form-options'],
+ ],
+ '#attached' => [
+ 'library' => ['node/drupal.node'],
+ ],
+ '#weight' => 95,
+ '#optional' => TRUE,
+ ];
+
+ if (isset($form['promote'])) {
+ $form['promote']['#group'] = 'options';
+ }
+
+ if (isset($form['sticky'])) {
+ $form['sticky']['#group'] = 'options';
+ }
+
+ $form['#attached']['library'][] = 'node/form';
+
+ $form['#entity_builders']['update_status'] = '::updateStatus';
+
+ return $form;
+ }
+
+ /**
+ * Entity builder updating the node status with the submitted value.
+ *
+ * @param string $entity_type_id
+ * The entity type identifier.
+ * @param \Drupal\node\NodeInterface $node
+ * The node updated with the submitted values.
+ * @param array $form
+ * The complete form array.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The current state of the form.
+ *
+ * @see \Drupal\node\NodeForm::form()
+ */
+ public function updateStatus($entity_type_id, NodeInterface $node, array $form, FormStateInterface $form_state) {
+ $element = $form_state->getTriggeringElement();
+ if (isset($element['#published_status'])) {
+ $node->setPublished($element['#published_status']);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function actions(array $form, FormStateInterface $form_state) {
+ $element = parent::actions($form, $form_state);
+ $node = $this->entity;
+ $preview_mode = $node->type->entity->getPreviewMode();
+
+ $element['submit']['#access'] = $preview_mode != DRUPAL_REQUIRED || $form_state->get('has_been_previewed');
+
+ // If saving is an option, privileged users get dedicated form submit
+ // buttons to adjust the publishing status while saving in one go.
+ // @todo This adjustment makes it close to impossible for contributed
+ // modules to integrate with "the Save operation" of this form. Modules
+ // need a way to plug themselves into 1) the ::submit() step, and
+ // 2) the ::save() step, both decoupled from the pressed form button.
+ if ($element['submit']['#access'] && \Drupal::currentUser()->hasPermission('administer nodes')) {
+ // isNew | prev status » default & publish label & unpublish label
+ // 1 | 1 » publish & Save and publish & Save as unpublished
+ // 1 | 0 » unpublish & Save and publish & Save as unpublished
+ // 0 | 1 » publish & Save and keep published & Save and unpublish
+ // 0 | 0 » unpublish & Save and keep unpublished & Save and publish
+
+ // Add a "Publish" button.
+ $element['publish'] = $element['submit'];
+ // If the "Publish" button is clicked, we want to update the status to "published".
+ $element['publish']['#published_status'] = TRUE;
+ $element['publish']['#dropbutton'] = 'save';
+ if ($node->isNew()) {
+ $element['publish']['#value'] = t('Save and publish');
+ }
+ else {
+ $element['publish']['#value'] = $node->isPublished() ? t('Save and keep published') : t('Save and publish');
+ }
+ $element['publish']['#weight'] = 0;
+
+ // Add a "Unpublish" button.
+ $element['unpublish'] = $element['submit'];
+ // If the "Unpublish" button is clicked, we want to update the status to "unpublished".
+ $element['unpublish']['#published_status'] = FALSE;
+ $element['unpublish']['#dropbutton'] = 'save';
+ if ($node->isNew()) {
+ $element['unpublish']['#value'] = t('Save as unpublished');
+ }
+ else {
+ $element['unpublish']['#value'] = !$node->isPublished() ? t('Save and keep unpublished') : t('Save and unpublish');
+ }
+ $element['unpublish']['#weight'] = 10;
+
+ // If already published, the 'publish' button is primary.
+ if ($node->isPublished()) {
+ unset($element['unpublish']['#button_type']);
+ }
+ // Otherwise, the 'unpublish' button is primary and should come first.
+ else {
+ unset($element['publish']['#button_type']);
+ $element['unpublish']['#weight'] = -10;
+ }
+
+ // Remove the "Save" button.
+ $element['submit']['#access'] = FALSE;
+ }
+
+ $element['preview'] = [
+ '#type' => 'submit',
+ '#access' => $preview_mode != DRUPAL_DISABLED && ($node->access('create') || $node->access('update')),
+ '#value' => t('Preview'),
+ '#weight' => 20,
+ '#submit' => ['::submitForm', '::preview'],
+ ];
+
+ $element['delete']['#access'] = $node->access('delete');
+ $element['delete']['#weight'] = 100;
+
+ return $element;
+ }
+
+ /**
+ * Form submission handler for the 'preview' action.
+ *
+ * @param $form
+ * An associative array containing the structure of the form.
+ * @param $form_state
+ * The current state of the form.
+ */
+ public function preview(array $form, FormStateInterface $form_state) {
+ $store = $this->tempStoreFactory->get('node_preview');
+ $this->entity->in_preview = TRUE;
+ $store->set($this->entity->uuid(), $form_state);
+
+ $route_parameters = [
+ 'node_preview' => $this->entity->uuid(),
+ 'view_mode_id' => 'full',
+ ];
+
+ $options = [];
+ $query = $this->getRequest()->query;
+ if ($query->has('destination')) {
+ $options['query']['destination'] = $query->get('destination');
+ $query->remove('destination');
+ }
+ $form_state->setRedirect('entity.node.preview', $route_parameters, $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save(array $form, FormStateInterface $form_state) {
+ $node = $this->entity;
+ $insert = $node->isNew();
+ $node->save();
+ $node_link = $node->link($this->t('View'));
+ $context = ['@type' => $node->getType(), '%title' => $node->label(), 'link' => $node_link];
+ $t_args = ['@type' => node_get_type_label($node), '%title' => $node->link($node->label())];
+
+ if ($insert) {
+ $this->logger('content')->notice('@type: added %title.', $context);
+ drupal_set_message(t('@type %title has been created.', $t_args));
+ }
+ else {
+ $this->logger('content')->notice('@type: updated %title.', $context);
+ drupal_set_message(t('@type %title has been updated.', $t_args));
+ }
+
+ if ($node->id()) {
+ $form_state->setValue('nid', $node->id());
+ $form_state->set('nid', $node->id());
+ if ($node->access('view')) {
+ $form_state->setRedirect(
+ 'entity.node.canonical',
+ ['node' => $node->id()]
+ );
+ }
+ else {
+ $form_state->setRedirect('<front>');
+ }
+
+ // Remove the preview entry from the temp store, if any.
+ $store = $this->tempStoreFactory->get('node_preview');
+ $store->delete($node->uuid());
+ }
+ else {
+ // In the unlikely case something went wrong on save, the node will be
+ // rebuilt and node form redisplayed the same way as in preview.
+ drupal_set_message(t('The post could not be saved.'), 'error');
+ $form_state->setRebuild();
+ }
+ }
+
+}