moduleExists('field_ui') ? Url::fromRoute('help.page', ['name' => 'field_ui'])->toString() : '#'; $output = ''; $output .= '
' . t('The Taxonomy module allows users who have permission to create and edit content to categorize (tag) content of that type. Users who have the Administer vocabularies and terms permission can add vocabularies that contain a set of related terms. The terms in a vocabulary can either be pre-set by an administrator or built gradually as content is added and edited. Terms may be organized hierarchically if desired.', [':permissions' => Url::fromRoute('user.admin_permissions.module', ['modules' => 'taxonomy'])->toString()]) . '
'; $output .= '' . t('For more information, see the online documentation for the Taxonomy module.', [':taxonomy' => 'https://www.drupal.org/documentation/modules/taxonomy/']) . '
'; $output .= '' . t('Taxonomy is for categorizing content. Terms are grouped into vocabularies. For example, a vocabulary called "Fruit" would contain the terms "Apple" and "Banana".') . '
'; return $output; } } /** * Implements hook_theme(). */ function taxonomy_theme() { return [ 'taxonomy_term' => [ 'render element' => 'elements', ], ]; } /** * Implements hook_theme_suggestions_HOOK(). */ function taxonomy_theme_suggestions_taxonomy_term(array $variables) { $suggestions = []; /** @var \Drupal\taxonomy\TermInterface $term */ $term = $variables['elements']['#taxonomy_term']; $suggestions[] = 'taxonomy_term__' . $term->bundle(); $suggestions[] = 'taxonomy_term__' . $term->id(); return $suggestions; } /** * Prepares variables for taxonomy term templates. * * Default template: taxonomy-term.html.twig. * * By default this function performs special preprocessing to move the name * base field out of the elements array into a separate variable. This * preprocessing is skipped if: * - a module makes the field's display configurable via the field UI by means * of BaseFieldDefinition::setDisplayConfigurable() * - AND the additional entity type property * 'enable_base_field_custom_preprocess_skipping' has been set using * hook_entity_type_build(). * * @param array $variables * An associative array containing: * - elements: An associative array containing the taxonomy term and any * fields attached to the term. Properties used: * - #taxonomy_term: A \Drupal\taxonomy\TermInterface object. * - #view_mode: The current view mode for this taxonomy term, e.g. * 'full' or 'teaser'. * - attributes: HTML attributes for the containing element. */ function template_preprocess_taxonomy_term(&$variables) { $variables['view_mode'] = $variables['elements']['#view_mode']; $variables['term'] = $variables['elements']['#taxonomy_term']; /** @var \Drupal\taxonomy\TermInterface $term */ $term = $variables['term']; $variables['url'] = !$term->isNew() ? $term->toUrl()->toString() : NULL; // Make name field available separately. Skip this custom preprocessing if // the field display is configurable and skipping has been enabled. // @todo https://www.drupal.org/project/drupal/issues/3015623 // Eventually delete this code and matching template lines. Using // $variables['content'] is more flexible and consistent. $skip_custom_preprocessing = $term->getEntityType()->get('enable_base_field_custom_preprocess_skipping'); if (!$skip_custom_preprocessing || !$term->getFieldDefinition('name')->isDisplayConfigurable('view')) { // We use name here because that is what appears in the UI. $variables['name'] = $variables['elements']['name']; unset($variables['elements']['name']); } $variables['page'] = $variables['view_mode'] == 'full' && taxonomy_term_is_page($term); // Helpful $content variable for templates. $variables['content'] = []; foreach (Element::children($variables['elements']) as $key) { $variables['content'][$key] = $variables['elements'][$key]; } } /** * Implements hook_entity_operation(). */ function taxonomy_entity_operation(EntityInterface $term) { $operations = []; if ($term instanceof Term && $term->access('create')) { $operations['add-child'] = [ 'title' => t('Add child'), 'weight' => 10, 'url' => Url::fromRoute( 'entity.taxonomy_term.add_form', ['taxonomy_vocabulary' => $term->bundle()], ['query' => ['parent' => $term->id()]], ), ]; } return $operations; } /** * Returns whether the current page is the page of the passed-in term. * * @param \Drupal\taxonomy\Entity\Term $term * A taxonomy term entity. */ function taxonomy_term_is_page(Term $term) { if (\Drupal::routeMatch()->getRouteName() == 'entity.taxonomy_term.canonical' && $page_term_id = \Drupal::routeMatch()->getRawParameter('taxonomy_term')) { return $page_term_id == $term->id(); } return FALSE; } /** * @defgroup taxonomy_index Taxonomy indexing * @{ * Functions to maintain taxonomy indexing. * * Taxonomy uses default field storage to store canonical relationships * between terms and fieldable entities. However its most common use case * requires listing all content associated with a term or group of terms * sorted by creation date. To avoid slow queries due to joining across * multiple node and field tables with various conditions and order by criteria, * we maintain a denormalized table with all relationships between terms, * published nodes and common sort criteria such as status, sticky and created. * When using other field storage engines or alternative methods of * denormalizing this data you should set the * taxonomy.settings:maintain_index_table to '0' to avoid unnecessary writes in * SQL. */ /** * Implements hook_ENTITY_TYPE_insert() for node entities. */ function taxonomy_node_insert(EntityInterface $node) { // Add taxonomy index entries for the node. taxonomy_build_node_index($node); } /** * Builds and inserts taxonomy index entries for a given node. * * The index lists all terms that are related to a given node entity, and is * therefore maintained at the entity level. * * @param \Drupal\node\Entity\Node $node * The node entity. */ function taxonomy_build_node_index($node) { // We maintain a denormalized table of term/node relationships, containing // only data for current, published nodes. if (!\Drupal::config('taxonomy.settings')->get('maintain_index_table') || !(\Drupal::entityTypeManager()->getStorage('node') instanceof SqlContentEntityStorage)) { return; } $status = $node->isPublished(); $sticky = (int) $node->isSticky(); // We only maintain the taxonomy index for published nodes. if ($status && $node->isDefaultRevision()) { // Collect a unique list of all the term IDs from all node fields. $tid_all = []; $entity_reference_class = 'Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem'; foreach ($node->getFieldDefinitions() as $field) { $field_name = $field->getName(); $class = $field->getItemDefinition()->getClass(); $is_entity_reference_class = ($class === $entity_reference_class) || is_subclass_of($class, $entity_reference_class); if ($is_entity_reference_class && $field->getSetting('target_type') == 'taxonomy_term') { foreach ($node->getTranslationLanguages() as $language) { foreach ($node->getTranslation($language->getId())->$field_name as $item) { if (!$item->isEmpty()) { $tid_all[$item->target_id] = $item->target_id; } } } } } // Insert index entries for all the node's terms. if (!empty($tid_all)) { $connection = \Drupal::database(); foreach ($tid_all as $tid) { $connection->merge('taxonomy_index') ->keys(['nid' => $node->id(), 'tid' => $tid, 'status' => $node->isPublished()]) ->fields(['sticky' => $sticky, 'created' => $node->getCreatedTime()]) ->execute(); } } } } /** * Implements hook_ENTITY_TYPE_update() for node entities. */ function taxonomy_node_update(EntityInterface $node) { // If we're not dealing with the default revision of the node, do not make any // change to the taxonomy index. if (!$node->isDefaultRevision()) { return; } taxonomy_delete_node_index($node); taxonomy_build_node_index($node); } /** * Implements hook_ENTITY_TYPE_predelete() for node entities. */ function taxonomy_node_predelete(EntityInterface $node) { // Clean up the {taxonomy_index} table when nodes are deleted. taxonomy_delete_node_index($node); } /** * Deletes taxonomy index entries for a given node. * * @param \Drupal\Core\Entity\EntityInterface $node * The node entity. */ function taxonomy_delete_node_index(EntityInterface $node) { if (\Drupal::config('taxonomy.settings')->get('maintain_index_table')) { \Drupal::database()->delete('taxonomy_index')->condition('nid', $node->id())->execute(); } } /** * Implements hook_ENTITY_TYPE_delete() for taxonomy_term entities. */ function taxonomy_taxonomy_term_delete(Term $term) { if (\Drupal::config('taxonomy.settings')->get('maintain_index_table')) { // Clean up the {taxonomy_index} table when terms are deleted. \Drupal::database()->delete('taxonomy_index')->condition('tid', $term->id())->execute(); } } /** * @} End of "defgroup taxonomy_index". */