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..4b52f33 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Form/RevisionableContentEntityForm.php @@ -0,0 +1,140 @@ +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 + * The bundle entity. + */ + 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. + $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], + ], + ]; + } + + 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->getEntityTypeId()); + $bundle_entity = $this->getBundleEntity(); + $t_args = ['@type' => $bundle_entity ? $bundle_entity->label() : $this->entity->getEntityType()->getLabel(), '%label' => $this->entity->label()]; + + if ($insert) { + $logger->notice('@type: added %info.', $context); + drupal_set_message($this->t('@type %label has been created.', $t_args)); + } + else { + $logger->notice('@type: updated %info.', $context); + drupal_set_message($this->t('@type %label has been updated.', $t_args)); + } + + if ($this->entity->getEntityType()->hasLinkTemplate('collection')) { + $form_state->setRedirectUrl($this->entity->toUrl('collection')); + } + else { + $form_state->setRedirectUrl($this->entity->toUrl('canonical')); + } + } + +} diff --git a/core/lib/Drupal/Core/Entity/RevisionableEntityBundleInterface.php b/core/lib/Drupal/Core/Entity/RevisionableEntityBundleInterface.php new file mode 100644 index 0000000..caf44f2 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/RevisionableEntityBundleInterface.php @@ -0,0 +1,20 @@ +blockContentStorage = $block_content_storage; - $this->blockContentTypeStorage = $block_content_type_storage; - $this->languageManager = $language_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - $entity_manager = $container->get('entity.manager'); - return new static( - $entity_manager, - $entity_manager->getStorage('block_content'), - $entity_manager->getStorage('block_content_type'), - $container->get('language_manager') - ); - } - - /** - * Overrides \Drupal\Core\Entity\EntityForm::prepareEntity(). - * - * Prepares the custom block object. - * - * Fills in a few default values, and then invokes - * hook_block_content_prepare() on all modules. - */ - protected function prepareEntity() { - $block = $this->entity; - // Set up default values, if required. - $block_type = $this->blockContentTypeStorage->load($block->bundle()); - if (!$block->isNew()) { - $block->setRevisionLogMessage(NULL); - } - // Always use the default revision setting. - $block->setNewRevision($block_type->shouldCreateNewRevision()); - } - - /** * {@inheritdoc} */ public function form(array $form, FormStateInterface $form_state) { $block = $this->entity; - $account = $this->currentUser(); + + $form = parent::form($form, $form_state); if ($this->operation == 'edit') { $form['#title'] = $this->t('Edit custom block %label', array('%label' => $block->label())); @@ -109,56 +34,12 @@ public function form(array $form, FormStateInterface $form_state) { // names. $form['#attributes']['class'][0] = 'block-' . Html::getClass($block->bundle()) . '-form'; - $form['advanced'] = array( - '#type' => 'vertical_tabs', - '#weight' => 99, - ); - // Add a 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', - '#title' => $this->t('Revision information'), - // Open by default when "Create new revision" is checked. - '#open' => $block->isNewRevision(), - '#group' => 'advanced', - '#attributes' => array( - 'class' => array('block-content-form-revision-information'), - ), - '#attached' => array( - 'library' => array('block_content/drupal.block_content'), - ), - '#weight' => 20, - '#access' => $block->isNewRevision() || $account->hasPermission('administer blocks'), - ); - - $form['revision_information']['revision'] = array( - '#type' => 'checkbox', - '#title' => $this->t('Create new revision'), - '#default_value' => $block->isNewRevision(), - '#access' => $account->hasPermission('administer blocks'), - ); - - // 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 (!$block->isNewRevision()) { - $form['revision_information']['revision']['#states'] = array( - 'checked' => array( - 'textarea[name="revision_log"]' => array('empty' => FALSE), - ), - ); - } + $form['revision_information']['#attributes']['class'][] = 'block-content-form-revision-information'; + $form['revision_information']['#attached']['library'][] = 'block_content/drupal.block_content'; - $form['revision_information']['revision_log'] = array( - '#type' => 'textarea', - '#title' => $this->t('Revision log message'), - '#rows' => 4, - '#default_value' => $block->getRevisionLog(), - '#description' => $this->t('Briefly describe the changes you have made.'), - ); - - return parent::form($form, $form_state, $block); + return $form; } /** @@ -166,55 +47,21 @@ public function form(array $form, FormStateInterface $form_state) { */ public function save(array $form, FormStateInterface $form_state) { $block = $this->entity; - - // Save as a new revision if requested to do so. - if (!$form_state->isValueEmpty('revision')) { - $block->setNewRevision(); - // If a new revision is created, save the current user as revision author. - $block->setRevisionCreationTime(REQUEST_TIME); - $block->setRevisionUserId(\Drupal::currentUser()->id()); - } - $insert = $block->isNew(); - $block->save(); - $context = array('@type' => $block->bundle(), '%info' => $block->label()); - $logger = $this->logger('block_content'); - $block_type = $this->blockContentTypeStorage->load($block->bundle()); - $t_args = array('@type' => $block_type->label(), '%info' => $block->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)); - } + parent::save($form, $form_state); - if ($block->id()) { - $form_state->setValue('id', $block->id()); - $form_state->set('id', $block->id()); - if ($insert) { - if (!$theme = $block->getTheme()) { - $theme = $this->config('system.theme')->get('default'); - } - $form_state->setRedirect( - 'block.admin_add', - array( - 'plugin_id' => 'block_content:' . $block->uuid(), - 'theme' => $theme, - ) - ); - } - else { - $form_state->setRedirectUrl($block->urlInfo('collection')); + if ($insert) { + if (!$theme = $block->getTheme()) { + $theme = $this->config('system.theme')->get('default'); } - } - else { - // In the unlikely case something went wrong on save, the block will be - // rebuilt and block form redisplayed. - drupal_set_message($this->t('The block could not be saved.'), 'error'); - $form_state->setRebuild(); + $form_state->setRedirect( + 'block.admin_add', + array( + 'plugin_id' => 'block_content:' . $block->uuid(), + 'theme' => $theme, + ) + ); } } diff --git a/core/modules/block_content/src/Entity/BlockContent.php b/core/modules/block_content/src/Entity/BlockContent.php index 13af342..0f50fbc 100644 --- a/core/modules/block_content/src/Entity/BlockContent.php +++ b/core/modules/block_content/src/Entity/BlockContent.php @@ -182,7 +182,14 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['revision_log'] = BaseFieldDefinition::create('string_long') ->setLabel(t('Revision log message')) ->setDescription(t('The log entry explaining the changes in this revision.')) - ->setRevisionable(TRUE); + ->setRevisionable(TRUE) + ->setDisplayOptions('form', array( + 'type' => 'string_textarea', + 'weight' => 25, + 'settings' => array( + 'rows' => 4, + ), + )); $fields['changed'] = BaseFieldDefinition::create('changed') ->setLabel(t('Changed')) diff --git a/core/modules/field/src/Tests/FormTest.php b/core/modules/field/src/Tests/FormTest.php index 16625a9..f16886e 100644 --- a/core/modules/field/src/Tests/FormTest.php +++ b/core/modules/field/src/Tests/FormTest.php @@ -609,11 +609,11 @@ function testHiddenField() { $this->drupalPostForm(NULL, array(), t('Save')); preg_match('|' . $entity_type . '/manage/(\d+)|', $this->url, $match); $id = $match[1]; - $this->assertText(t('entity_test_rev @id has been created.', array('@id' => $id)), 'Entity was created'); $storage = $this->container->get('entity_type.manager') ->getStorage($entity_type); $entity = $storage->load($id); + $this->assertRaw(t('@type %label has been created.', ['@type' => 'Test entity - revisions', '%label' => $entity->label()])); $this->assertEqual($entity->{$field_name}->value, 99, 'Default value was saved'); // Update the field to remove the default value, and switch to the default @@ -634,7 +634,7 @@ function testHiddenField() { $value = mt_rand(1, 127); $edit = array("{$field_name}[0][value]" => $value); $this->drupalPostForm(NULL, $edit, t('Save')); - $this->assertText(t('entity_test_rev @id has been updated.', array('@id' => $id)), 'Entity was updated'); + $this->assertRaw(t('@type %label has been updated.', ['@type' => 'Test entity - revisions', '%label' => $entity->label()])); $storage->resetCache([$id]); $entity = $storage->load($id); $this->assertEqual($entity->{$field_name}->value, $value, 'Field value was updated'); diff --git a/core/modules/node/src/Entity/NodeType.php b/core/modules/node/src/Entity/NodeType.php index 91d8a90..23f24a4 100644 --- a/core/modules/node/src/Entity/NodeType.php +++ b/core/modules/node/src/Entity/NodeType.php @@ -205,4 +205,11 @@ public static function postDelete(EntityStorageInterface $storage, array $entiti $storage->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..efd1346 100644 --- a/core/modules/node/src/NodeForm.php +++ b/core/modules/node/src/NodeForm.php @@ -2,8 +2,8 @@ namespace Drupal\node; -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 +11,7 @@ /** * Form handler for the node edit forms. */ -class NodeForm extends ContentEntityForm { +class NodeForm extends RevisionableContentEntityForm { /** * The tempstore factory. @@ -26,7 +26,7 @@ class NodeForm extends ContentEntityForm { protected $hasBeenPreviewed = FALSE; /** - * Constructs a ContentEntityForm object. + * Constructs a NodeForm object. * * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The entity manager. @@ -51,19 +51,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 +85,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,46 +93,24 @@ 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, - ); + if ($this->operation == 'edit') { + $form['#title'] = $this->t('Edit @type @title', array('@type' => $node->type->entity->label(), '@title' => $node->label())); + } - $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_information']['#attributes']['class'][] = 'node-form-revision-information'; + $form['revision_information']['#attached']['library'][] = 'node/drupal.node'; + + $form['revision']['#access'] = $current_user->hasPermission('administer nodes') && !$node->isNew(); $form['revision_log'] += array( + '#group' => 'revision_information', '#states' => array( 'visible' => array( ':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..1aab64d 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. @@ -28,7 +29,7 @@ public function isNewRevision(); /** * Sets whether a new revision should be created by default. * - * @param bool $new_revision_ + * @param bool $new_revision * TRUE if a new revision should be created by default. */ public function setNewRevision($new_revision); 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..e49ee7e 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.'); @@ -797,7 +800,7 @@ public function testBaseFieldEntityKeyUpdateWithExistingData() { /** * Check that field schema is correctly handled with long-named fields. */ - function testLongNameFieldIndexes() { + public function testLongNameFieldIndexes() { $this->addLongNameBaseField(); $entity_type_id = 'entity_test_update'; $entity_type = $this->entityManager->getDefinition($entity_type_id);