diff --git c/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php w/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php index 590b80f..6116520 100644 --- c/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php +++ w/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php @@ -7,11 +7,95 @@ namespace Drupal\Core\Entity; +use Drupal\Core\Form\FormStateInterface; + /** * Provides a generic base class for a content entity deletion form. */ class ContentEntityDeleteForm extends ContentEntityConfirmFormBase { - use EntityDeleteFormTrait; + use EntityDeleteFormTrait { + getQuestion as traitGetQuestion; + logDeletionMessage as traitLogDeletionMessage; + getDeletionMessage as traitGetDeletionMessage; + } + + /** + * {@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. + if (!$entity->isDefaultTranslation()) { + $untranslated_entity = $entity->getUntranslated(); + $untranslated_entity->removeTranslation($entity->language()->getId()); + $untranslated_entity->save(); + $form_state->setRedirectUrl($untranslated_entity->urlInfo('canonical')); + } + else { + $entity->delete(); + $form_state->setRedirectUrl($this->getCancelUrl()); + } + + drupal_set_message($this->getDeletionMessage()); + $this->logDeletionMessage(); + } + + /** + * {@inheritdoc} + */ + protected function getDeletionMessage() { + /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ + $entity = $this->getEntity(); + + if (!$entity->isDefaultTranslation()) { + return $this->t('The @entity-type %label @language translation has been deleted.', [ + '@entity-type' => $entity->getEntityType()->getLowercaseLabel(), + '%label' => $entity->label(), + '@language' => $entity->language()->getName(), + ]); + } + + return $this->traitGetDeletionMessage(); + } + + /** + * {@inheritdoc} + */ + protected function logDeletionMessage() { + /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ + $entity = $this->getEntity(); + + if (!$entity->isDefaultTranslation()) { + $this->logger($entity->getEntityType()->getProvider())->notice('The @entity-type %label @language translation has been deleted.', [ + '@entity-type' => $entity->getEntityType()->getLowercaseLabel(), + '%label' => $entity->label(), + '@language' => $entity->language()->getName(), + ]); + } + else { + $this->traitLogDeletionMessage(); + } + } + + /** + * {@inheritdoc} + */ + public function getQuestion() { + /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ + $entity = $this->getEntity(); + + if (!$entity->isDefaultTranslation()) { + return $this->t('Are you sure you want to delete @language translation of the @entity-type %label?', array( + '@language' => $entity->language()->getName(), + '@entity-type' => $this->getEntity()->getEntityType()->getLowercaseLabel(), + '%label' => $this->getEntity()->label(), + )); + } + + return $this->traitGetQuestion(); + } } diff --git c/core/modules/content_translation/content_translation.module w/core/modules/content_translation/content_translation.module index 1f1a0eb..5448046 100644 --- c/core/modules/content_translation/content_translation.module +++ w/core/modules/content_translation/content_translation.module @@ -281,7 +281,12 @@ function content_translation_form_alter(array &$form, FormStateInterface $form_s if ($entity instanceof ContentEntityInterface && $entity->isTranslatable() && count($entity->getTranslationLanguages()) > 1) { $controller = \Drupal::entityManager()->getHandler($entity->getEntityTypeId(), 'translation'); - $controller->entityFormAlter($form, $form_state, $entity); + if ($form_object->getOperation() == 'delete') { + $controller->entityDeleteFormAlter($form, $form_state, $entity); + } + else { + $controller->entityFormAlter($form, $form_state, $entity); + } // @todo Move the following lines to the code generating the property form // elements once we have an official #multilingual FAPI key. diff --git c/core/modules/content_translation/src/ContentTranslationHandler.php w/core/modules/content_translation/src/ContentTranslationHandler.php index 0bd26f4..3f82a78 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\ContentEntityInterface; use Drupal\Core\Entity\EntityHandlerInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeInterface; @@ -17,6 +18,7 @@ use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Render\Element; +use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\user\Entity\User; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -27,6 +29,7 @@ */ class ContentTranslationHandler implements ContentTranslationHandlerInterface, EntityHandlerInterface { use DependencySerializationTrait; + use StringTranslationTrait; /** * The type of the entity being translated. @@ -602,6 +605,33 @@ function entityFormDelete($form, FormStateInterface $form_state) { } /** + * {@inheritdoc} + */ + public function entityDeleteFormAlter(array &$form, FormStateInterface $form_state, ContentEntityInterface $entity) { + if ($entity->isDefaultTranslation()) { + $languages = []; + foreach ($entity->getTranslationLanguages() as $language) { + $languages[] = $language->getName(); + } + + if (count($languages) > 1) { + $form['entity_deletes'] = array( + '#theme' => 'item_list', + '#title' => $this->t('The following @entity-type translations will be deleted:', [ + '@entity-type' => $entity->getEntityType()->getLowercaseLabel() + ]), + '#items' => $languages, + ); + + $form['actions']['submit']['#value'] = $this->t('Delete all translations'); + } + } + else { + $form['actions']['submit']['#value'] = $this->t('Delete @language translation', array('@language' => $entity->language()->getName()));; + } + } + + /** * Form submission handler for ContentTranslationHandler::entityFormAlter(). * * Takes care of content translation deletion. diff --git c/core/modules/content_translation/src/ContentTranslationHandlerInterface.php w/core/modules/content_translation/src/ContentTranslationHandlerInterface.php index 12f59c1..759c3b7 100644 --- c/core/modules/content_translation/src/ContentTranslationHandlerInterface.php +++ w/core/modules/content_translation/src/ContentTranslationHandlerInterface.php @@ -7,6 +7,7 @@ namespace Drupal\content_translation; +use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; @@ -76,4 +77,16 @@ public function retranslate(EntityInterface $entity, $langcode = NULL); */ public function entityFormAlter(array &$form, FormStateInterface $form_state, EntityInterface $entity); + /** + * Performs the needed alterations to the entity delete form. + * + * @param array $form + * The entity delete form to be altered to provide the translation workflow. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * @param \Drupal\Core\Entity\ContentEntityInterface $entity + * The entity being deleted. + */ + public function entityDeleteFormAlter(array &$form, FormStateInterface $form_state, ContentEntityInterface $entity); + } diff --git c/core/modules/node/src/Form/NodeDeleteForm.php w/core/modules/node/src/Form/NodeDeleteForm.php index 16c86e6..cd36d98 100644 --- c/core/modules/node/src/Form/NodeDeleteForm.php +++ w/core/modules/node/src/Form/NodeDeleteForm.php @@ -18,13 +18,44 @@ 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() { + /** @var \Drupal\node\NodeInterface $entity */ + $entity = $this->getEntity(); + $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(''); + $node_type = $node_type_storage->load($entity->bundle())->label(); + + if (!$entity->isDefaultTranslation()) { + return $this->t('@language translation of the @type %label has been deleted.', [ + '@language' => $entity->language()->getName(), + '@type' => $node_type, + '%label' => $entity->label(), + ]); + } + + return $this->t('The @type %title has been deleted.', array( + '@type' => $node_type, + '%title' => $this->getEntity()->label(), + )); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + parent::submitForm($form, $form_state); + if ($this->getEntity()->isDefaultTranslation()) { + $form_state->setRedirect(''); + } + } + + /** + * {@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..d720ebe 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; diff --git c/core/modules/system/src/Tests/Entity/EntityFormTest.php w/core/modules/system/src/Tests/Entity/EntityFormTest.php index 307b69a..7edde9f 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(array('name' => $name)); + return $entities ? current($entities) : NULL; } } diff --git c/core/modules/taxonomy/src/Form/TermDeleteForm.php w/core/modules/taxonomy/src/Form/TermDeleteForm.php index fdbdf68..a51aab7 100644 --- c/core/modules/taxonomy/src/Form/TermDeleteForm.php +++ w/core/modules/taxonomy/src/Form/TermDeleteForm.php @@ -19,20 +19,6 @@ class TermDeleteForm extends ContentEntityDeleteForm { /** * {@inheritdoc} */ - public function getFormId() { - return 'taxonomy_term_confirm_delete'; - } - - /** - * {@inheritdoc} - */ - public function getQuestion() { - return $this->t('Are you sure you want to delete the term %title?', array('%title' => $this->entity->getName())); - } - - /** - * {@inheritdoc} - */ public function getCancelUrl() { // The cancel URL is the vocabulary collection, terms have no global // list page. diff --git c/core/modules/taxonomy/src/Tests/VocabularyPermissionsTest.php w/core/modules/taxonomy/src/Tests/VocabularyPermissionsTest.php index f6c7480..00df789 100644 --- c/core/modules/taxonomy/src/Tests/VocabularyPermissionsTest.php +++ w/core/modules/taxonomy/src/Tests/VocabularyPermissionsTest.php @@ -51,7 +51,7 @@ function testVocabularyPermissionsTaxonomyTerm() { // Delete the vocabulary. $this->drupalGet('taxonomy/term/' . $term->id() . '/delete'); - $this->assertRaw(t('Are you sure you want to delete the term %name?', array('%name' => $edit['name[0][value]'])), 'Delete taxonomy term form opened successfully.'); + $this->assertRaw(t('Are you sure you want to delete the @entity-type %label?', array('@entity-type' => 'taxonomy term', '%label' => $edit['name[0][value]'])), 'Delete taxonomy term form opened successfully.'); // Confirm deletion. $this->drupalPostForm(NULL, NULL, t('Delete')); @@ -98,7 +98,7 @@ function testVocabularyPermissionsTaxonomyTerm() { // Delete the vocabulary. $this->drupalGet('taxonomy/term/' . $term->id() . '/delete'); - $this->assertRaw(t('Are you sure you want to delete the term %name?', array('%name' => $term->getName())), 'Delete taxonomy term form opened successfully.'); + $this->assertRaw(t('Are you sure you want to delete the @entity-type %label?', array('@entity-type' => 'taxonomy term', '%label' => $term->getName())), 'Delete taxonomy term form opened successfully.'); // Confirm deletion. $this->drupalPostForm(NULL, NULL, t('Delete'));