diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php b/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php index 590b80f..0b868cd 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php @@ -7,11 +7,128 @@ 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 buildForm(array $form, FormStateInterface $form_state) { + $form = parent::buildForm($form, $form_state); + + /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ + $entity = $this->getEntity(); + if ($entity->isDefaultTranslation()) { + if (count($entity->getTranslationLanguages()) > 1) { + $languages = []; + foreach ($entity->getTranslationLanguages() as $language) { + $languages[] = $language->getName(); + } + + $form['deleted_translations'] = 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()));; + } + + return $form; + } + + /** + * {@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 a/core/lib/Drupal/Core/Entity/EntityDeleteFormTrait.php b/core/lib/Drupal/Core/Entity/EntityDeleteFormTrait.php index d35bcea..433945c 100644 --- a/core/lib/Drupal/Core/Entity/EntityDeleteFormTrait.php +++ b/core/lib/Drupal/Core/Entity/EntityDeleteFormTrait.php @@ -9,6 +9,7 @@ use Drupal\Core\Config\Entity\ConfigDependencyDeleteFormTrait; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Url; /** * Provides a trait for an entity deletion form. @@ -85,8 +86,8 @@ public function getCancelUrl() { return $entity->urlInfo('collection'); } else { - // Otherwise fall back to the default link template. - return $entity->urlInfo(); + // Otherwise fall back to the front page. + return Url::fromRoute(''); } } diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module index 1f1a0eb..8f64974 100644 --- a/core/modules/content_translation/content_translation.module +++ b/core/modules/content_translation/content_translation.module @@ -279,7 +279,7 @@ function content_translation_form_alter(array &$form, FormStateInterface $form_s } $entity = $form_object->getEntity(); - if ($entity instanceof ContentEntityInterface && $entity->isTranslatable() && count($entity->getTranslationLanguages()) > 1) { + if ($entity instanceof ContentEntityInterface && $entity->isTranslatable() && count($entity->getTranslationLanguages()) > 1 && $form_object->getOperation() != 'delete') { $controller = \Drupal::entityManager()->getHandler($entity->getEntityTypeId(), 'translation'); $controller->entityFormAlter($form, $form_state, $entity); diff --git a/core/modules/content_translation/src/ContentTranslationHandlerInterface.php b/core/modules/content_translation/src/ContentTranslationHandlerInterface.php index 12f59c1..d21e287 100644 --- a/core/modules/content_translation/src/ContentTranslationHandlerInterface.php +++ b/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; diff --git a/core/modules/node/src/Form/NodeDeleteForm.php b/core/modules/node/src/Form/NodeDeleteForm.php index 16c86e6..80a12f6 100644 --- a/core/modules/node/src/Form/NodeDeleteForm.php +++ b/core/modules/node/src/Form/NodeDeleteForm.php @@ -18,13 +18,34 @@ 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} + */ + 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 a/core/modules/node/src/NodeTranslationHandler.php b/core/modules/node/src/NodeTranslationHandler.php index 3692c8b..d720ebe 100644 --- a/core/modules/node/src/NodeTranslationHandler.php +++ b/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 a/core/modules/system/src/Tests/Entity/EntityFormTest.php b/core/modules/system/src/Tests/Entity/EntityFormTest.php index 307b69a..e4365eb 100644 --- a/core/modules/system/src/Tests/Entity/EntityFormTest.php +++ b/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 Romanian translation')); + $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 a/core/modules/taxonomy/src/Form/TermDeleteForm.php b/core/modules/taxonomy/src/Form/TermDeleteForm.php index fdbdf68..a51aab7 100644 --- a/core/modules/taxonomy/src/Form/TermDeleteForm.php +++ b/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 a/core/modules/taxonomy/src/Tests/VocabularyPermissionsTest.php b/core/modules/taxonomy/src/Tests/VocabularyPermissionsTest.php index f6c7480..00df789 100644 --- a/core/modules/taxonomy/src/Tests/VocabularyPermissionsTest.php +++ b/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'));