diff --git a/core/modules/taxonomy/src/Form/TermDeleteForm.php b/core/modules/taxonomy/src/Form/TermDeleteForm.php index 1701946..7be872a 100644 --- a/core/modules/taxonomy/src/Form/TermDeleteForm.php +++ b/core/modules/taxonomy/src/Form/TermDeleteForm.php @@ -7,9 +7,11 @@ namespace Drupal\taxonomy\Form; +use Drupal\Component\Render\FormattableMarkup; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Entity\ContentEntityDeleteForm; use Drupal\Core\Url; +use Drupal\taxonomy\TermStorageInterface; /** * Provides a deletion confirmation form for taxonomy term. @@ -17,6 +19,14 @@ class TermDeleteForm extends ContentEntityDeleteForm { /** + * The list of all terms to be will deleted should this form action be + * confirmed. Will be undefined until getDeletedTerms() is called. + * + * @var \Drupal\taxonomy\TermInterface[] + */ + private $_deletedTerms; + + /** * {@inheritdoc} */ public function getCancelUrl() { @@ -36,20 +46,115 @@ protected function getRedirectUrl() { * {@inheritdoc} */ public function getDescription() { - return $this->t('Deleting a term will delete all its children if there are any. This action cannot be undone.'); + $term = $this->getEntity(); + $descendants = $this->getDeletedTerms(); + unset($descendants[$term->id()]); + if ($descendants) { + $descendants_last = end($descendants); + $descendants_head = array_slice($descendants, 0, -1); + return $this->formatPlural(count($descendants), + 'Deleting @entity-type %label will also delete its descendant %descendant. This action cannot be undone.', + 'Deleting @entity-type %label will also delete its descendants @descendants and %descendant. This action cannot be undone.', + array( + '@entity-type' => $term->getEntityType()->getLowercaseLabel(), + '%label' => $term->label(), + '%descendant' => $descendants_last->label(), + '@descendants' => new FormattableMarkup( + '%' . implode(', %', array_keys($descendants_head)), + array_combine( + array_map(function ($id) { + return "%$id"; + }, array_keys($descendants_head)), + array_map(function ($term) { + return $term->label(); + }, $descendants_head) + ) + ) + ) + ); + } + else { + return $this->t('Deleting @entity-type %label cannot be undone.', array( + '@entity-type' => $term->getEntityType()->getLowercaseLabel(), + '%label' => $term->label(), + )); + } } /** * {@inheritdoc} */ protected function getDeletionMessage() { - return $this->t('Deleted term %name.', array('%name' => $this->entity->label())); + $entity = $this->getEntity(); + $deleted = $this->getDeletedTerms(); + $deleted_last = end($deleted); + $deleted_head = array_slice($deleted, 0, -1); + return $this->formatPlural(count($deleted), + 'The @entity-type %label has been deleted.', + 'The @entity-type-plural @labels and %label have been deleted.', + array( + '@entity-type' => $entity->getEntityType()->getLowercaseLabel(), + // @todo: Use pluralized entity type label once available, see https://www.drupal.org/node/1850080 + '@entity-type-plural' => $this->t('taxonomy terms'), + '%label' => $deleted_last->label(), + '@labels' => new FormattableMarkup( + '%' . implode(', %', array_keys($deleted_head)), + array_combine( + array_map(function ($id) { + return "%$id"; + }, array_keys($deleted_head)), + array_map(function ($term) { + return $term->label(); + }, $deleted_head) + ) + ) + ) + ); + + } + + /** + * {@inheritdoc} + */ + protected function logDeletionMessage() { + $entity = $this->getEntity(); + $deleted = $this->getDeletedTerms(); + if (count($deleted) > 1) { + $deleted_last = end($deleted); + $deleted_head = array_slice($deleted, 0, -1); + $this->logger($entity->getEntityType()->getProvider())->notice('The @entity-type @labels and %label have been deleted.', array( + // @todo: Use pluralized entity type label once available, see https://www.drupal.org/node/1850080 + '@entity-type' => 'taxonomy terms', + '%label' => $deleted_last->label(), + '@labels' => new FormattableMarkup( + '%' . implode(', %', array_keys($deleted_head)), + array_combine( + array_map(function ($id) { + return "%$id"; + }, array_keys($deleted_head)), + array_map(function ($term) { + return $term->label(); + }, $deleted_head) + ) + ) + )); + } + else { + $this->logger($entity->getEntityType()->getProvider())->notice('The @entity-type %label has been deleted.', array( + '@entity-type' => $entity->getEntityType()->getLowercaseLabel(), + '%label' => $entity->label(), + )); + } } /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { + // Ensure the list of deleted terms is usable during and after the + // processing of the form submission. + $this->getDeletedTerms(); + parent::submitForm($form, $form_state); /** @var \Drupal\Core\Entity\ContentEntityInterface $term */ @@ -63,4 +168,47 @@ public function submitForm(array &$form, FormStateInterface $form_state) { } } + /** + * Finds all terms to be will deleted should this form action be confirmed. + * + * When a taxonomy term is deleted, all its descendants without a non-deleted + * ancestor are also deleted. + * + * Once retrieved, the list of deleted terms is cached. This can be used to + * ensure the list of deleted terms is usable after they have been actually + * deleted. + * + * @return \Drupal\taxonomy\TermInterface[] + * An array of term objects, indexed by id. + */ + public function getDeletedTerms() { + if (!isset($this->_deletedTerms)) { + /** @var \Drupal\Core\Entity\ContentEntityInterface $term */ + $term = $this->getEntity(); + /** @var TermStorageInterface $termStorage */ + $termStorage = $this->entityManager->getStorage('taxonomy_term'); + + $this->_deletedTerms = array($term->id() => $term); + + // Get all the terms below the form's term, indexed by id and ordered by depth. + $children = $termStorage->loadTree($term->bundle(), $term->id(), NULL, TRUE); + $children = array_combine(array_map(function ($term) { return $term->id();}, $children), $children); + uasort($children, function ($a, $b) { + if( $a->depth = $b->depth) { + return 0; + } + return ($a->depth < $b->depth) ? -1 : 1; + }); + + // Find all future orphans. + foreach ($children as $child) { + // An term is a future orphan if all its parent are also deleted. + if (empty(array_diff_key($termStorage->loadParents($child->id()), $this->_deletedTerms))) { + $this->_deletedTerms[$child->id()] = $child; + } + } + } + return $this->_deletedTerms; + } + }