diff --git a/core/modules/block_content/src/BlockContentTranslationHandler.php b/core/modules/block_content/src/BlockContentTranslationHandler.php index 241a1c8fbc..920c21f226 100644 --- a/core/modules/block_content/src/BlockContentTranslationHandler.php +++ b/core/modules/block_content/src/BlockContentTranslationHandler.php @@ -5,12 +5,28 @@ use Drupal\block_content\Entity\BlockContentType; use Drupal\Core\Entity\EntityInterface; use Drupal\content_translation\ContentTranslationHandler; +use Drupal\Core\Form\FormStateInterface; /** * Defines the translation handler for custom blocks. */ class BlockContentTranslationHandler extends ContentTranslationHandler { + /** + * {@inheritdoc} + */ + public function entityFormAlter(array &$form, FormStateInterface $form_state, EntityInterface $entity) { + parent::entityFormAlter($form, $form_state, $entity); + + if (isset($form['content_translation'])) { + // Block content entities do not expose the status field. Make the content + // translation status field visible instead. + // @todo This will not be necessary once this issue lands: + // https://www.drupal.org/project/drupal/issues/2834546 + $form['content_translation']['status']['#access'] = TRUE; + } + } + /** * {@inheritdoc} */ diff --git a/core/modules/block_content/tests/src/Functional/BlockContentTranslationUITest.php b/core/modules/block_content/tests/src/Functional/BlockContentTranslationUITest.php index 3d765ac95a..4eb352e991 100644 --- a/core/modules/block_content/tests/src/Functional/BlockContentTranslationUITest.php +++ b/core/modules/block_content/tests/src/Functional/BlockContentTranslationUITest.php @@ -101,6 +101,21 @@ protected function getEditValues($values, $langcode, $new = FALSE) { return $edit; } + /** + * {@inheritdoc} + */ + protected function hasMetadataField($field_name) { + $result = parent::hasMetadataField($field_name); + // Block content entities have a status field but do not expose it. + // @todo This will not be necessary once this issue lands: + // https://www.drupal.org/project/drupal/issues/2834546 + if ($field_name === 'status') { + return FALSE; + } + + return $result; + } + /** * {@inheritdoc} */ diff --git a/core/modules/comment/src/CommentTranslationHandler.php b/core/modules/comment/src/CommentTranslationHandler.php index a0abe4b710..5adf1057b9 100644 --- a/core/modules/comment/src/CommentTranslationHandler.php +++ b/core/modules/comment/src/CommentTranslationHandler.php @@ -4,28 +4,12 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\content_translation\ContentTranslationHandler; -use Drupal\Core\Form\FormStateInterface; /** * Defines the translation handler for comments. */ class CommentTranslationHandler extends ContentTranslationHandler { - /** - * {@inheritdoc} - */ - public function entityFormAlter(array &$form, FormStateInterface $form_state, EntityInterface $entity) { - parent::entityFormAlter($form, $form_state, $entity); - - if (isset($form['content_translation'])) { - // We do not need to show these values on comment forms: they inherit the - // basic comment property values. - $form['content_translation']['status']['#access'] = FALSE; - $form['content_translation']['name']['#access'] = FALSE; - $form['content_translation']['created']['#access'] = FALSE; - } - } - /** * {@inheritdoc} */ @@ -33,17 +17,4 @@ protected function entityFormTitle(EntityInterface $entity) { return t('Edit comment @subject', ['@subject' => $entity->label()]); } - /** - * {@inheritdoc} - */ - public function entityFormEntityBuild($entity_type, EntityInterface $entity, array $form, FormStateInterface $form_state) { - if ($form_state->hasValue('content_translation')) { - $translation = &$form_state->getValue('content_translation'); - /** @var \Drupal\comment\CommentInterface $entity */ - $translation['status'] = $entity->isPublished(); - $translation['name'] = $entity->getAuthorName(); - } - parent::entityFormEntityBuild($entity_type, $entity, $form, $form_state); - } - } diff --git a/core/modules/content_translation/src/ContentTranslationHandler.php b/core/modules/content_translation/src/ContentTranslationHandler.php index ff1938f264..d0386f8c1c 100644 --- a/core/modules/content_translation/src/ContentTranslationHandler.php +++ b/core/modules/content_translation/src/ContentTranslationHandler.php @@ -475,6 +475,8 @@ public function entityFormAlter(array &$form, FormStateInterface $form_state, En '#default_value' => $status, '#description' => $description, '#disabled' => !$enabled, + // Show status field if it is not natively provided by the entity type. + '#access' => !$this->hasPublishedStatus(), ]; $translate = !$new_translation && $metadata->isOutdated(); @@ -521,6 +523,7 @@ public function entityFormAlter(array &$form, FormStateInterface $form_state, En '#validate_reference' => FALSE, '#maxlength' => 60, '#description' => t('Leave blank for %anonymous.', ['%anonymous' => \Drupal::config('user.settings')->get('anonymous')]), + '#access' => !$this->hasAuthor(), ]; $date = $new_translation ? REQUEST_TIME : $metadata->getCreatedTime(); @@ -533,6 +536,7 @@ public function entityFormAlter(array &$form, FormStateInterface $form_state, En '%timezone' => $this->dateFormatter->format(REQUEST_TIME, 'custom', 'O'), ]), '#default_value' => $new_translation || !$date ? '' : $this->dateFormatter->format($date, 'custom', 'Y-m-d H:i:s O'), + '#access' => !$this->hasCreatedTime(), ]; $form['#process'][] = [$this, 'entityFormSharedElements']; @@ -682,9 +686,15 @@ public function entityFormEntityBuild($entity_type, EntityInterface $entity, arr $values = &$form_state->getValue('content_translation', []); $metadata = $this->manager->getTranslationMetadata($entity); - $metadata->setAuthor(!empty($values['uid']) ? User::load($values['uid']) : User::load(0)); - $metadata->setPublished(!empty($values['status'])); - $metadata->setCreatedTime(!empty($values['created']) ? strtotime($values['created']) : REQUEST_TIME); + if (isset($form['content_translation']['uid']) && $form['content_translation']['uid']['#access']) { + $metadata->setAuthor(!empty($values['uid']) ? User::load($values['uid']) : User::load(0)); + } + if (isset($form['content_translation']['status']) && $form['content_translation']['status']['#access']) { + $metadata->setPublished(!empty($values['status'])); + } + if (isset($form['content_translation']['created']) && $form['content_translation']['created']['#access']) { + $metadata->setCreatedTime(!empty($values['created']) ? strtotime($values['created']) : REQUEST_TIME); + } $metadata->setOutdated(!empty($values['outdated'])); if (!empty($values['retranslate'])) { diff --git a/core/modules/content_translation/tests/src/Functional/ContentTranslationMetadataTrait.php b/core/modules/content_translation/tests/src/Functional/ContentTranslationMetadataTrait.php new file mode 100644 index 0000000000..4b49327dcc --- /dev/null +++ b/core/modules/content_translation/tests/src/Functional/ContentTranslationMetadataTrait.php @@ -0,0 +1,77 @@ +hasMetadataField($field_name)) { + $edit["content_translation[$field_name]"] = $field_value; + if ($field_name === 'created') { + $edit['content_translation[created]'] = $field_value; + if (is_numeric($field_value)) { + $edit["content_translation[created]"] = \Drupal::service('date.formatter')->format($field_value, 'custom', 'Y-m-d H:i:s O'); + } + // Assert there is no native meta data field. + $this->assertSession()->fieldNotExists('created[0][value][date]'); + } + elseif ($this->entityTypeId !== 'user') { + $this->assertSession()->fieldNotExists($field_name); + } + } + else { + if ($field_name === 'created') { + $edit['created[0][value][date]'] = $field_value; + if (is_numeric($field_value)) { + $edit['created[0][value][date]'] = \Drupal::service('date.formatter')->format($field_value, 'custom', 'Y-m-d'); + $edit['created[0][value][time]'] = \Drupal::service('date.formatter')->format($field_value, 'custom', 'H:i:s'); + } + } + else { + // Try a few different field name patterns. + if ($this->getSession()->getPage()->findField($field_name . '[value]')) { + $edit[$field_name . '[value]'] = $field_value; + } + elseif ($this->getSession()->getPage()->findField($field_name . '[0][target_id]')) { + $edit[$field_name . '[0][target_id]'] = $field_value; + } + else { + $edit[$field_name] = $field_value; + } + } + + // Assert there is no content translation meta data field. + $this->assertSession()->fieldNotExists("content_translation[$field_name]"); + } + + return $edit; + } + + /** + * Returns whether a given field name exists in the field storage definitions. + * + * @see \Drupal\content_translation\ContentTranslationHandler::checkFieldStorageDefinitionTranslatability + */ + protected function hasMetadataField($field_name) { + $field_storage_definitions = \Drupal::service('entity_field.manager')->getFieldStorageDefinitions($this->entityTypeId); + $has_metadata_field = array_key_exists($field_name, $field_storage_definitions) && $field_storage_definitions[$field_name]->isTranslatable(); + // Check if the entity type implements EntityOwnerInterface for uid field. + if ($field_name === 'uid') { + return $has_metadata_field && $this->container->get('entity_type.manager')->getDefinition($this->entityTypeId)->entityClassImplements(EntityOwnerInterface::class); + } + + return $has_metadata_field; + } + +} diff --git a/core/modules/content_translation/tests/src/Functional/ContentTranslationUITestBase.php b/core/modules/content_translation/tests/src/Functional/ContentTranslationUITestBase.php index 0641bb0b32..0d678a80ca 100644 --- a/core/modules/content_translation/tests/src/Functional/ContentTranslationUITestBase.php +++ b/core/modules/content_translation/tests/src/Functional/ContentTranslationUITestBase.php @@ -18,6 +18,7 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase { use AssertPageCacheContextsAndTagsTrait; + use ContentTranslationMetadataTrait; /** * The id of the entity being translated. @@ -292,8 +293,9 @@ protected function doTestPublishedStatus() { foreach ($this->langcodes as $index => $langcode) { if ($index > 0) { $url = $entity->toUrl('edit-form', ['language' => ConfigurableLanguage::load($langcode)]); - $edit = ['content_translation[status]' => FALSE]; - $this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode)); + $this->drupalGet($url); + $edit = $this->getMetadataValueAndAssertNoField('status', FALSE); + $this->submitForm($edit, $this->getFormSubmitAction($entity, $langcode)); $storage = $this->container->get('entity_type.manager') ->getStorage($this->entityTypeId); $storage->resetCache([$this->entityId]); @@ -302,10 +304,13 @@ protected function doTestPublishedStatus() { } } - // Check that the last published translation cannot be unpublished. - $this->drupalGet($entity->toUrl('edit-form')); - $this->assertSession()->fieldDisabled('content_translation[status]'); - $this->assertSession()->fieldValueEquals('content_translation[status]', TRUE); + // Check that the last published translation cannot be unpublished, this + // only applies to the translation specific status field. + if (!$this->hasMetadataField('status')) { + $this->drupalGet($entity->toUrl('edit-form')); + $this->assertSession()->fieldDisabled('content_translation[status]'); + $this->assertSession()->fieldValueEquals('content_translation[status]', TRUE); + } } /** @@ -321,16 +326,15 @@ protected function doTestAuthoringInfo() { // Post different authoring information for each translation. foreach ($this->langcodes as $index => $langcode) { $user = $this->drupalCreateUser(); + $url = $entity->toUrl('edit-form', ['language' => ConfigurableLanguage::load($langcode)]); + $this->drupalGet($url); $values[$langcode] = [ 'uid' => $user->id(), 'created' => REQUEST_TIME - mt_rand(0, 1000), ]; - $edit = [ - 'content_translation[uid]' => $user->getAccountName(), - 'content_translation[created]' => $this->container->get('date.formatter')->format($values[$langcode]['created'], 'custom', 'Y-m-d H:i:s O'), - ]; - $url = $entity->toUrl('edit-form', ['language' => ConfigurableLanguage::load($langcode)]); - $this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode)); + $edit = $this->getMetadataValueAndAssertNoField('uid', $user->getAccountName()); + $edit += $this->getMetadataValueAndAssertNoField('created', $values[$langcode]['created']); + $this->submitForm($edit, $this->getFormSubmitAction($entity, $langcode)); } $storage = $this->container->get('entity_type.manager') @@ -345,12 +349,10 @@ protected function doTestAuthoringInfo() { // Try to post non valid values and check that they are rejected. $langcode = 'en'; - $edit = [ - // User names have by default length 8. - 'content_translation[uid]' => $this->randomMachineName(12), - 'content_translation[created]' => '19/11/1978', - ]; - $this->drupalPostForm($entity->toUrl('edit-form'), $edit, $this->getFormSubmitAction($entity, $langcode)); + $this->drupalGet($entity->toUrl('edit-form')); + $edit = $this->getMetadataValueAndAssertNoField('uid', $this->randomMachineName(12)); + $edit += $this->getMetadataValueAndAssertNoField('created', '19/11/1978'); + $this->submitForm($edit, $this->getFormSubmitAction($entity, $langcode)); $this->assertSession()->elementExists('xpath', '//div[contains(@class, "error")]//ul'); $metadata = $this->manager->getTranslationMetadata($entity->getTranslation($langcode)); $this->assertEqual($values[$langcode]['uid'], $metadata->getAuthor()->id(), 'Translation author correctly kept.'); diff --git a/core/modules/content_translation/tests/src/Functional/ContentTranslationWorkflowsTest.php b/core/modules/content_translation/tests/src/Functional/ContentTranslationWorkflowsTest.php index 3c37a70842..cad1b7dbfe 100644 --- a/core/modules/content_translation/tests/src/Functional/ContentTranslationWorkflowsTest.php +++ b/core/modules/content_translation/tests/src/Functional/ContentTranslationWorkflowsTest.php @@ -170,7 +170,7 @@ protected function setupEntity(UserInterface $user = NULL) { $add_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_add", [$this->entityTypeId => $id, 'source' => $default_langcode, 'target' => $this->langcodes[2]]); $edit = [ 'name[0][value]' => 'translation name', - 'content_translation[status]' => FALSE, + 'status[value]' => FALSE, ]; $this->drupalPostForm($add_translation_url, $edit, 'Save'); diff --git a/core/modules/node/src/NodeTranslationHandler.php b/core/modules/node/src/NodeTranslationHandler.php index 244a0535a2..b339fc7756 100644 --- a/core/modules/node/src/NodeTranslationHandler.php +++ b/core/modules/node/src/NodeTranslationHandler.php @@ -17,14 +17,6 @@ class NodeTranslationHandler extends ContentTranslationHandler { public function entityFormAlter(array &$form, FormStateInterface $form_state, EntityInterface $entity) { parent::entityFormAlter($form, $form_state, $entity); - if (isset($form['content_translation'])) { - // We do not need to show these values on node forms: they inherit the - // basic node property values. - $form['content_translation']['status']['#access'] = FALSE; - $form['content_translation']['name']['#access'] = FALSE; - $form['content_translation']['created']['#access'] = FALSE; - } - $form_object = $form_state->getFormObject(); $form_langcode = $form_object->getFormLangcode($form_state); $translations = $entity->getTranslationLanguages(); @@ -54,18 +46,4 @@ protected function entityFormTitle(EntityInterface $entity) { return t('Edit @type @title', ['@type' => $type_name, '@title' => $entity->label()]); } - /** - * {@inheritdoc} - */ - public function entityFormEntityBuild($entity_type, EntityInterface $entity, array $form, FormStateInterface $form_state) { - if ($form_state->hasValue('content_translation')) { - $translation = &$form_state->getValue('content_translation'); - $translation['status'] = $entity->isPublished(); - $account = $entity->uid->entity; - $translation['uid'] = $account ? $account->id() : 0; - $translation['created'] = $this->dateFormatter->format($entity->created->value, 'custom', 'Y-m-d H:i:s O'); - } - parent::entityFormEntityBuild($entity_type, $entity, $form, $form_state); - } - } diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulChanged.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulChanged.php index 4cede02bca..23938dff08 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulChanged.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulChanged.php @@ -23,6 +23,7 @@ * "route_provider" = { * "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider", * }, + * "translation" = "Drupal\entity_test\EntityTestTranslationHandler", * "views_data" = "Drupal\views\EntityViewsData" * }, * base_table = "entity_test_mul_changed", @@ -54,6 +55,12 @@ class EntityTestMulChanged extends EntityTestMul implements EntityChangedInterfa public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields = parent::baseFieldDefinitions($entity_type); + // Show the created field in the edit form. + $fields['created']->setDisplayOptions('form', [ + 'type' => 'datetime_timestamp', + 'weight' => 10, + ]); + $fields['changed'] = BaseFieldDefinition::create('changed_test') ->setLabel(t('Changed')) ->setDescription(t('The time that the entity was last edited.')) diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevPub.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevPub.php index 8b8f5d6167..38f5a7e6c3 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevPub.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevPub.php @@ -59,7 +59,17 @@ class EntityTestMulRevPub extends EntityTestMulRev implements EntityPublishedInt * {@inheritdoc} */ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { - return parent::baseFieldDefinitions($entity_type) + EntityPublishedTrait::publishedBaseFieldDefinitions($entity_type); + $fields = parent::baseFieldDefinitions($entity_type) + EntityPublishedTrait::publishedBaseFieldDefinitions($entity_type); + + $fields['status'] + ->setDisplayOptions('form', [ + 'type' => 'boolean_checkbox', + 'settings' => [ + 'display_label' => TRUE, + ], + ]) + ->setDisplayConfigurable('form', TRUE); + return $fields; } } diff --git a/core/modules/system/tests/modules/entity_test/src/EntityTestTranslationHandler.php b/core/modules/system/tests/modules/entity_test/src/EntityTestTranslationHandler.php new file mode 100644 index 0000000000..7847c67734 --- /dev/null +++ b/core/modules/system/tests/modules/entity_test/src/EntityTestTranslationHandler.php @@ -0,0 +1,25 @@ +hasValue('content_translation')) { + $translation = &$form_state->getValue('content_translation'); + $translation['created'] = $this->dateFormatter->format($entity->get('created')->value, 'custom', 'Y-m-d H:i:s O'); + } + parent::entityFormEntityBuild($entity_type, $entity, $form, $form_state); + } + +} diff --git a/core/modules/taxonomy/src/TermTranslationHandler.php b/core/modules/taxonomy/src/TermTranslationHandler.php index 8ef804bc00..a803327887 100644 --- a/core/modules/taxonomy/src/TermTranslationHandler.php +++ b/core/modules/taxonomy/src/TermTranslationHandler.php @@ -16,9 +16,6 @@ class TermTranslationHandler extends ContentTranslationHandler { */ public function entityFormAlter(array &$form, FormStateInterface $form_state, EntityInterface $entity) { parent::entityFormAlter($form, $form_state, $entity); - - $form['content_translation']['status']['#access'] = !isset($form['content_translation']); - $form['actions']['submit']['#submit'][] = [$this, 'entityFormSave']; }