diff --git a/core/lib/Drupal/Core/Entity/Form/RevisionableContentEntityForm.php b/core/lib/Drupal/Core/Entity/Form/RevisionableContentEntityForm.php new file mode 100644 index 0000000..2d551d6 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Form/RevisionableContentEntityForm.php @@ -0,0 +1,160 @@ +entity->isNew()) { + $this->entity->setRevisionLogMessage(NULL); + } + } + + /** + * Returns the bundle entity of the entity, or NULL if there is none. + * + * @return \Drupal\Core\Entity\EntityInterface|null + */ + protected function getBundleEntity() { + if ($bundle_entity_type = $this->entity->getEntityType()->getBundleEntityType()) { + return $this->entityTypeManager->getStorage($bundle_entity_type)->load($this->entity->bundle()); + } + return NULL; + } + + /** + * {@inheritdoc} + */ + public function form(array $form, FormStateInterface $form_state) { + $entity_type = $this->entity->getEntityType(); + $bundle_entity = $this->getBundleEntity(); + $account = $this->currentUser(); + if ($this->operation == 'edit') { + $form['#title'] = $this->t('Edit %bundle_label @label', [ + '%bundle_label' => $bundle_entity ? $bundle_entity->label() : '', + '@label' => $this->entity->label(), + ]); + } + $form['advanced'] = [ + '#type' => 'vertical_tabs', + '#attributes' => array('class' => array('entity-meta')), + '#weight' => 99, + ]; + + $new_revision_default = FALSE; + $bundle_entity = $this->getBundleEntity(); + if ($bundle_entity instanceof RevisionableEntityBundleInterface) { + // Always use the default revision setting. + $new_revision_default = $bundle_entity->shouldCreateNewRevision(); + } + + // Add a log field if the "Create new revision" option is checked, or if the + // current user has the ability to check that option. + // @todo Could we autogenerate this form by using some widget on the + // revision info field. + $form['revision_information'] = [ + '#type' => 'details', + '#title' => $this->t('Revision information'), + // Open by default when "Create new revision" is checked. + '#open' => $new_revision_default, + '#group' => 'advanced', + '#weight' => 20, + '#access' => $new_revision_default || ($entity_type->get('admin_permission')) && $account->hasPermission($entity_type->get('admin_permission')), + '#optional' => TRUE, + ]; + $form['revision'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Create new revision'), + '#default_value' => $new_revision_default, + '#access' => !$this->entity->isNew() && ($entity_type->get('admin_permission') || $account->hasPermission($entity_type->get('admin_permission'))), + '#group' => 'revision_information', + ]; + + // Check the revision log checkbox when the log textarea is filled in. + // This must not happen if "Create new revision" is enabled by default, + // since the state would auto-disable the checkbox otherwise. + if (!$this->entity->isNewRevision()) { + $form['revision']['#states'] = [ + 'checked' => [ + 'textarea[name="revision_log"]' => ['empty' => FALSE], + ], + ]; + } + + if (isset($form['revision_log'])) { + $form['revision_log']['#group'] = 'revision_information'; + } + + return parent::form($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function save(array $form, FormStateInterface $form_state) { + // Save as a new revision if requested to do so. + if (!$form_state->isValueEmpty('revision')) { + $this->entity->setNewRevision(); + } + + $insert = $this->entity->isNew(); + $this->entity->save(); + + $context = ['@type' => $this->entity->bundle(), '%info' => $this->entity->label()]; + $logger = $this->logger($this->entity->id()); + $bundle_entity = $this->getBundleEntity(); + $t_args = ['@type' => $bundle_entity ? $bundle_entity->label() : 'None', '%info' => $this->entity->label()]; + + if ($insert) { + $logger->notice('@type: added %info.', $context); + drupal_set_message($this->t('@type %info has been created.', $t_args)); + } + else { + $logger->notice('@type: updated %info.', $context); + drupal_set_message($this->t('@type %info has been updated.', $t_args)); + } + + if ($this->entity->id()) { + $form_state->setValue('id', $this->entity->id()); + $form_state->set('id', $this->entity->id()); + if ($this->entity->getEntityType()->hasLinkTemplate('collection')) { + $form_state->setRedirectUrl($this->entity->toUrl('collection')); + } + else { + $form_state->setRedirectUrl($this->entity->toUrl('canonical')); + } + } + else { + // In the unlikely case something went wrong on save, the entity will be + // rebuilt and entity form redisplayed. + drupal_set_message($this->t('The entity could not be saved.'), 'error'); + $form_state->setRebuild(); + } + } + +} diff --git a/core/lib/Drupal/Core/Entity/RevisionableEntityBundleInterface.php b/core/lib/Drupal/Core/Entity/RevisionableEntityBundleInterface.php new file mode 100644 index 0000000..9d7ef07 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/RevisionableEntityBundleInterface.php @@ -0,0 +1,22 @@ +resetCache(array_keys($entities)); } + /** + * {@inheritdoc} + */ + public function shouldCreateNewRevision() { + return $this->isNewRevision(); + } + } diff --git a/core/modules/node/src/NodeForm.php b/core/modules/node/src/NodeForm.php index 2828030..3789ea3 100644 --- a/core/modules/node/src/NodeForm.php +++ b/core/modules/node/src/NodeForm.php @@ -4,6 +4,7 @@ use Drupal\Core\Entity\ContentEntityForm; use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\Form\RevisionableContentEntityForm; use Drupal\Core\Form\FormStateInterface; use Drupal\user\PrivateTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -11,7 +12,7 @@ /** * Form handler for the node edit forms. */ -class NodeForm extends ContentEntityForm { +class NodeForm extends RevisionableContentEntityForm { /** * The tempstore factory. @@ -51,19 +52,6 @@ public static function create(ContainerInterface $container) { /** * {@inheritdoc} */ - protected function prepareEntity() { - /** @var \Drupal\node\NodeInterface $node */ - $node = $this->entity; - - if (!$node->isNew()) { - // Remove the revision log message from the original node entity. - $node->revision_log = NULL; - } - } - - /** - * {@inheritdoc} - */ public function form(array $form, FormStateInterface $form_state) { // Try to restore from temp store, this must be done before calling // parent::form(). @@ -98,10 +86,6 @@ public function form(array $form, FormStateInterface $form_state) { /** @var \Drupal\node\NodeInterface $node */ $node = $this->entity; - if ($this->operation == 'edit') { - $form['#title'] = $this->t('Edit @type @title', array('@type' => node_get_type_label($node), '@title' => $node->label())); - } - $current_user = $this->currentUser(); // Changed must be sent to the client, for later overwrite error checking. @@ -110,38 +94,12 @@ public function form(array $form, FormStateInterface $form_state) { '#default_value' => $node->getChangedTime(), ); - $form['advanced'] = array( - '#type' => 'vertical_tabs', - '#attributes' => array('class' => array('entity-meta')), - '#weight' => 99, - ); $form = parent::form($form, $form_state); - // Add a revision_log field if the "Create new revision" option is checked, - // or if the current user has the ability to check that option. - $form['revision_information'] = array( - '#type' => 'details', - '#group' => 'advanced', - '#title' => t('Revision information'), - // Open by default when "Create new revision" is checked. - '#open' => $node->isNewRevision(), - '#attributes' => array( - 'class' => array('node-form-revision-information'), - ), - '#attached' => array( - 'library' => array('node/drupal.node'), - ), - '#weight' => 20, - '#optional' => TRUE, - ); + $form['revision_information']['#attributes']['class'][] = 'node-form-revision-information'; + $form['revision_information']['#attached']['library'][] = 'node/drupal.node'; - $form['revision'] = array( - '#type' => 'checkbox', - '#title' => t('Create new revision'), - '#default_value' => $node->type->entity->isNewRevision(), - '#access' => $current_user->hasPermission('administer nodes') && !$node->isNew(), - '#group' => 'revision_information', - ); + $form['revision']['#access'] = $current_user->hasPermission('administer nodes') && !$node->isNew(); $form['revision_log'] += array( '#states' => array( @@ -149,7 +107,6 @@ public function form(array $form, FormStateInterface $form_state) { ':input[name="revision"]' => array('checked' => TRUE), ), ), - '#group' => 'revision_information', ); // Node author information for administrators. diff --git a/core/modules/node/src/NodeTypeInterface.php b/core/modules/node/src/NodeTypeInterface.php index c034ffb..882c080 100644 --- a/core/modules/node/src/NodeTypeInterface.php +++ b/core/modules/node/src/NodeTypeInterface.php @@ -3,11 +3,12 @@ namespace Drupal\node; use Drupal\Core\Config\Entity\ConfigEntityInterface; +use Drupal\Core\Entity\RevisionableEntityBundleInterface; /** * Provides an interface defining a node type entity. */ -interface NodeTypeInterface extends ConfigEntityInterface { +interface NodeTypeInterface extends ConfigEntityInterface, RevisionableEntityBundleInterface { /** * Determines whether the node type is locked. diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php index 47035ea..4291ce6 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php @@ -15,7 +15,7 @@ * "access" = "Drupal\entity_test\EntityTestAccessControlHandler", * "view_builder" = "Drupal\entity_test\EntityTestViewBuilder", * "form" = { - * "default" = "Drupal\entity_test\EntityTestForm", + * "default" = "\Drupal\entity_test\EntityTestRevisionForm", * "delete" = "Drupal\entity_test\EntityTestDeleteForm" * }, * "view_builder" = "Drupal\entity_test\EntityTestViewBuilder", @@ -53,13 +53,6 @@ class EntityTestRev extends EntityTest { public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields = parent::baseFieldDefinitions($entity_type); - $fields['revision_id'] = BaseFieldDefinition::create('integer') - ->setLabel(t('Revision ID')) - ->setDescription(t('The version id of the test entity.')) - ->setReadOnly(TRUE) - ->setSetting('unsigned', TRUE); - - $fields['langcode']->setRevisionable(TRUE); $fields['name']->setRevisionable(TRUE); $fields['user_id']->setRevisionable(TRUE); diff --git a/core/modules/system/tests/modules/entity_test/src/EntityTestRevisionForm.php b/core/modules/system/tests/modules/entity_test/src/EntityTestRevisionForm.php new file mode 100644 index 0000000..180f137 --- /dev/null +++ b/core/modules/system/tests/modules/entity_test/src/EntityTestRevisionForm.php @@ -0,0 +1,40 @@ +entity->name->value)) { + // Assign a random name to new EntityTest entities, to avoid repetition in + // tests. + $random = new Random(); + $this->entity->name->value = $random->name(); + } + } + + /** + * {@inheritdoc} + */ + public function save(array $form, FormStateInterface $form_state) { + try { + parent::save($form, $form_state); + } + catch (\Exception $e) { + \Drupal::state()->set('entity_test.form.save.exception', get_class($e) . ': ' . $e->getMessage()); + } + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php index 23cf034..befa0f0 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php @@ -102,6 +102,9 @@ public function testEntityTypeUpdateWithoutData() { $expected = array( 'entity_test_update' => array( t('The %entity_type entity type needs to be updated.', ['%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel()]), + // The revision key is now defined, so the revision field needs to be + // created. + t('The %field_name field needs to be installed.', ['%field_name' => 'Revision ID']), ), ); $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected); //, 'EntityDefinitionUpdateManager reports the expected change summary.');