diff --git c/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php w/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php index 590b80f..fa7a9d9 100644 --- c/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php +++ w/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php @@ -7,6 +7,8 @@ namespace Drupal\Core\Entity; +use Drupal\Core\Form\FormStateInterface; + /** * Provides a generic base class for a content entity deletion form. */ @@ -14,4 +16,32 @@ class ContentEntityDeleteForm extends ContentEntityConfirmFormBase { use EntityDeleteFormTrait; + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ + $entity = $this->getEntity(); + + // Make sure that deleting a translation does not delete the whole entity. + $entity_untranslated = $entity->getUntranslated(); + if ($entity->language()->getId() != $entity_untranslated->language()->getId()) { + $language_name = $entity->language()->getName(); + $entity_untranslated->removeTranslation($entity->language()->getId()); + $entity_untranslated->save(); + drupal_set_message(t('The @language translation has been removed.', array( + '@language' => $language_name, + ))); + $form_state->setRedirectUrl($entity_untranslated->urlInfo('canonical')); + $this->logger('content')->notice('@type: @language translation deleted %title.', ['@type' => $entity->getType(), '%title' => $entity->label(), '@language' => $language_name]); + } + else { + $entity->delete(); + drupal_set_message($this->getDeletionMessage()); + $form_state->setRedirect(''); + $this->logDeletionMessage(); + } + + } + } diff --git c/core/lib/Drupal/Core/Entity/Entity.php w/core/lib/Drupal/Core/Entity/Entity.php index a5c3d0f..a11ed15 100644 --- c/core/lib/Drupal/Core/Entity/Entity.php +++ w/core/lib/Drupal/Core/Entity/Entity.php @@ -124,6 +124,19 @@ public function enforceIsNew($value = TRUE) { } /** + * Checks if entity is a translation. + * + * @return boolean + * true if it is a translation. + */ + public function isTranslation() { + if ($this->language()->getId() != $this->getUntranslated()->language()->getId()) { + return true; + } + return false; + } + + /** * {@inheritdoc} */ public function getEntityTypeId() { diff --git c/core/modules/content_translation/src/ContentTranslationHandler.php w/core/modules/content_translation/src/ContentTranslationHandler.php index 0bd26f4..dd24f3c 100644 --- c/core/modules/content_translation/src/ContentTranslationHandler.php +++ w/core/modules/content_translation/src/ContentTranslationHandler.php @@ -9,6 +9,7 @@ use Drupal\Core\Access\AccessResult; use Drupal\Core\DependencyInjection\DependencySerializationTrait; +use Drupal\Core\Entity\ContentEntityDeleteForm; use Drupal\Core\Entity\EntityHandlerInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeInterface; @@ -225,6 +226,15 @@ public function getSourceLangcode(FormStateInterface $form_state) { * {@inheritdoc} */ public function entityFormAlter(array &$form, FormStateInterface $form_state, EntityInterface $entity) { + // Only alter entity delete forms. + if ($form_state->getFormObject() instanceof ContentEntityDeleteForm) { + $this->entityFormDelete($form, $form_state); + return; + } + elseif (!($form_state->getFormObject()->getOperation() == 'edit' || $form_state->getFormObject()->getOperation() == 'default')) { + return; + } + $form_object = $form_state->getFormObject(); $form_langcode = $form_object->getFormLangcode($form_state); $entity_langcode = $entity->getUntranslated()->language()->getId(); @@ -593,11 +603,19 @@ public function entityFormSourceChange($form, FormStateInterface $form_state) { * * Takes care of entity deletion. */ - function entityFormDelete($form, FormStateInterface $form_state) { - $form_object = $form_state->getFormObject()->getEntity(); - $entity = $form_object->getEntity(); - if (count($entity->getTranslationLanguages()) > 1) { - drupal_set_message(t('This will delete all the translations of %label.', array('%label' => $entity->label())), 'warning'); + protected function entityFormDelete(&$form, FormStateInterface $form_state) { + $entity = $form_state->getFormObject()->getEntity(); + $languages = []; + foreach ($entity->getTranslationLanguages() as $language) { + array_push($languages, t('@language', array('@language' => $language->getName()))); + } + if (count($entity->getTranslationLanguages()) > 1 && !$entity->isTranslation()) { + + $form['text']['#markup'] = '

' . t('This will delete the following translations:') . '

'; + $form['languages'] = array( + '#theme' => 'item_list', + '#items' => $languages, + ); } } diff --git c/core/modules/node/src/Form/NodeDeleteForm.php w/core/modules/node/src/Form/NodeDeleteForm.php index 16c86e6..41d6383 100644 --- c/core/modules/node/src/Form/NodeDeleteForm.php +++ w/core/modules/node/src/Form/NodeDeleteForm.php @@ -18,13 +18,22 @@ class NodeDeleteForm extends ContentEntityDeleteForm { /** * {@inheritdoc} */ - public function submitForm(array &$form, FormStateInterface $form_state) { - $this->entity->delete(); - $this->logger('content')->notice('@type: deleted %title.', array('@type' => $this->entity->bundle(), '%title' => $this->entity->label())); + protected function getDeletionMessage() { $node_type_storage = $this->entityManager->getStorage('node_type'); $node_type = $node_type_storage->load($this->entity->bundle())->label(); - drupal_set_message(t('@type %title has been deleted.', array('@type' => $node_type, '%title' => $this->entity->label()))); - $form_state->setRedirect(''); + return $this->t('@type %title has been deleted.', array( + '@type' => $node_type, + '%title' => $this->getEntity()->label(), + )); + } + + /** + * {@inheritdoc} + */ + protected function logDeletionMessage() { + /** @var \Drupal\node\NodeInterface $entity */ + $entity = $this->getEntity(); + $this->logger('content')->notice('@type: deleted %title.', ['@type' => $entity->getType(), '%title' => $entity->label()]); } } diff --git c/core/modules/node/src/NodeTranslationHandler.php w/core/modules/node/src/NodeTranslationHandler.php index 3692c8b..2f5ad6c 100644 --- c/core/modules/node/src/NodeTranslationHandler.php +++ w/core/modules/node/src/NodeTranslationHandler.php @@ -7,6 +7,7 @@ namespace Drupal\node; +use Drupal\Core\Entity\ContentEntityDeleteForm; use Drupal\Core\Entity\EntityInterface; use Drupal\content_translation\ContentTranslationHandler; use Drupal\Core\Form\FormStateInterface; @@ -44,6 +45,7 @@ public function entityFormAlter(array &$form, FormStateInterface $form_state, En $form_langcode = $form_object->getFormLangcode($form_state); $translations = $entity->getTranslationLanguages(); $status_translatable = NULL; + $is_delete_form = $form_state->getFormObject() instanceof ContentEntityDeleteForm; // Change the submit button labels if there was a status field they affect // in which case their publishing / unpublishing may or may not apply // to all translations. @@ -53,13 +55,20 @@ public function entityFormAlter(array &$form, FormStateInterface $form_state, En $status_translatable = $definition->isTranslatable(); } } - if (isset($status_translatable)) { + if (isset($status_translatable) && !$is_delete_form) { foreach (array('publish', 'unpublish', 'submit') as $button) { if (isset($form['actions'][$button])) { $form['actions'][$button]['#value'] .= ' ' . ($status_translatable ? t('(this translation)') : t('(all translations)')); } } } + + // Change the submit button label for delete forms. + if ($is_delete_form) { + if ($entity->isTranslation()) { + $form['actions']['submit']['#value'] = t('Delete @language translation', array('@language' => $entity->language()->getName())); + } + } } } diff --git c/core/modules/system/src/Tests/Entity/EntityFormTest.php w/core/modules/system/src/Tests/Entity/EntityFormTest.php index 307b69a..d5f21de 100644 --- c/core/modules/system/src/Tests/Entity/EntityFormTest.php +++ w/core/modules/system/src/Tests/Entity/EntityFormTest.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\Entity; +use Drupal\language\Entity\ConfigurableLanguage; use Drupal\simpletest\WebTestBase; /** @@ -27,6 +28,9 @@ protected function setUp() { parent::setUp(); $web_user = $this->drupalCreateUser(array('administer entity_test content')); $this->drupalLogin($web_user); + + // Add a language. + ConfigurableLanguage::createFromLangcode('ro')->save(); } /** @@ -40,6 +44,16 @@ function testFormCRUD() { } /** + * Tests basic multilingual form CRUD functionality. + */ + public function testMultilingualFormCRUD() { + // All entity variations have to have the same results. + foreach (entity_test_entity_types(ENTITY_TEST_TYPES_MULTILINGUAL) as $entity_type) { + $this->doTestMultilingualFormCRUD($entity_type); + } + } + + /** * Tests hook_entity_form_display_alter(). * * @see entity_test_entity_form_display_alter() @@ -85,12 +99,54 @@ protected function doTestFormCRUD($entity_type) { } /** + * Executes the multilingual form CRUD tests for the given entity type ID. + * + * @param string $entity_type_id + * The ID of entity type to run the tests with. + */ + protected function doTestMultilingualFormCRUD($entity_type_id) { + $name1 = $this->randomMachineName(8); + $name1_ro = $this->randomMachineName(9); + $name2_ro = $this->randomMachineName(11); + + $edit = array( + 'name[0][value]' => $name1, + 'field_test_text[0][value]' => $this->randomMachineName(16), + ); + + $this->drupalPostForm($entity_type_id . '/add', $edit, t('Save')); + $entity = $this->loadEntityByName($entity_type_id, $name1); + $this->assertTrue($entity, format_string('%entity_type: Entity found in the database.', array('%entity_type' => $entity_type_id))); + + // Add a translation to the newly created entity without using the Content + // translation module. + $entity->addTranslation('ro', ['name' => $name1_ro])->save(); + $translated_entity = $this->loadEntityByName($entity_type_id, $name1)->getTranslation('ro'); + $this->assertEqual($translated_entity->name->value, $name1_ro, format_string('%entity_type: The translation has been added.', array('%entity_type' => $entity_type_id))); + + $edit['name[0][value]'] = $name2_ro; + $this->drupalPostForm('ro/' . $entity_type_id . '/manage/' . $entity->id(), $edit, t('Save')); + $translated_entity = $this->loadEntityByName($entity_type_id, $name1)->getTranslation('ro'); + $this->assertTrue($translated_entity, format_string('%entity_type: Modified translation found in the database.', array('%entity_type' => $entity_type_id))); + $this->assertEqual($translated_entity->name->value, $name2_ro, format_string('%entity_type: The name of the translation has been modified.', array('%entity_type' => $entity_type_id))); + + $this->drupalGet('ro/' . $entity_type_id . '/manage/' . $entity->id()); + $this->clickLink(t('Delete')); + $this->drupalPostForm(NULL, array(), t('Delete')); + $entity = $this->loadEntityByName($entity_type_id, $name1); + $this->assertNotNull($entity, format_string('%entity_type: The original entity still exists.', array('%entity_type' => $entity_type_id))); + $this->assertFalse($entity->hasTranslation('ro'), format_string('%entity_type: Entity translation does not exist anymore.', array('%entity_type' => $entity_type_id))); + } + + /** * Loads a test entity by name always resetting the storage cache. */ protected function loadEntityByName($entity_type, $name) { // Always load the entity from the database to ensure that changes are // correctly picked up. - $this->container->get('entity.manager')->getStorage($entity_type)->resetCache(); - return current(entity_load_multiple_by_properties($entity_type, array('name' => $name))); + $entity_storage = $this->container->get('entity.manager')->getStorage($entity_type); + $entity_storage->resetCache(); + $entities = $entity_storage->loadByProperties($entity_type, array('name' => $name)); + return $entities ? current($entities) : NULL; } }