diff --git a/core/modules/system/tests/modules/taxonomy_test/taxonomy_test.module b/core/modules/system/tests/modules/taxonomy_test/taxonomy_test.module index 36a08af..63f6406 100644 --- a/core/modules/system/tests/modules/taxonomy_test/taxonomy_test.module +++ b/core/modules/system/tests/modules/taxonomy_test/taxonomy_test.module @@ -3,6 +3,8 @@ /** * @file * Test module for Taxonomy hooks and functions not used in core. + * + * @see Drupal\taxonomy\Tests\TaxonomyHooksTestCase::testTaxonomyTermHooks() */ use Drupal\taxonomy\Term; @@ -57,6 +59,34 @@ function taxonomy_test_taxonomy_term_delete(Term $term) { } /** + * Implements hook_taxonomy_term_view(). + */ +function taxonomy_test_taxonomy_term_view($term, $view_mode, $langcode) { + if ($view_mode == 'full') { + $term->content['taxonomy_test_term_view_check'] = array( + '#prefix' => '
', + '#markup' => t('The antonym is %antonym', array('%antonym' => $term->antonym)), + '#suffix' => '
', + '#weight' => 10, + ); + } +} + +/** + * Implements hook_entity_view(). + */ +function taxonomy_test_entity_view($entity, $view_mode, $langcode) { + if ($entity->entityType() == 'taxonomy_term' && $view_mode == 'full') { + $entity->content['taxonomy_test_entity_view_check'] = array( + '#prefix' => '
', + '#markup' => t('The antonym is %antonym', array('%antonym' => $entity->antonym)), + '#suffix' => '
', + '#weight' => 20, + ); + } +} + +/** * Implements hook_form_FORM_ID_alter(). */ function taxonomy_test_form_taxonomy_term_form_alter(&$form, $form_state, $form_id) { diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/HooksTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/HooksTest.php index 3dd43b1..e9bb9f6 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/HooksTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/HooksTest.php @@ -35,7 +35,12 @@ class HooksTest extends TaxonomyTestBase { } /** - * Test that hooks are run correctly on creating, editing and deleting a term. + * Test hooks on CRUD of terms. + * + * Test that hooks are run correctly on creating, editing, viewing, + * and deleting a term. + * + * @see taxonomy_test.module */ function testTaxonomyTermHooks() { $vocabulary = $this->createVocabulary(); @@ -60,6 +65,14 @@ class HooksTest extends TaxonomyTestBase { $term = taxonomy_term_load($term->tid); $this->assertEqual($edit['antonym'], $term->antonym, 'Antonym was successfully edited.'); + // View the term and ensure that hook_taxonomy_term_view() and + // hook_entity_view() are invoked. + $term = taxonomy_term_load($term->tid); + module_load_include('inc', 'taxonomy', 'taxonomy.pages'); + $term_build = taxonomy_term_page($term); + $this->assertFalse(empty($term_build['taxonomy_terms'][$term->tid]['taxonomy_test_term_view_check']), 'hook_taxonomy_term_view() was invoked when viewing the term.'); + $this->assertFalse(empty($term_build['taxonomy_terms'][$term->tid]['taxonomy_test_entity_view_check']), 'hook_entity_view() was invoked when viewing the term.'); + // Delete the term. taxonomy_term_delete($term->tid); $antonym = db_query('SELECT tid FROM {taxonomy_term_antonym} WHERE tid = :tid', array(':tid' => $term->tid))->fetchField(); diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index 3bd18c8..adc48b2 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -569,13 +569,108 @@ function taxonomy_term_delete_multiple(array $tids) { } /** + * Generates an array which displays a term detail page. + * + * @param Drupal\taxonomy\Term $term + * A taxonomy term object. + * @return array + * A $page element suitable for use by drupal_page_render(). + */ +function taxonomy_term_show(Term $term) { + return taxonomy_term_view_multiple(array($term->tid => $term), 'full'); +} + +/** + * Constructs a drupal_render() style array from an array of loaded terms. + * + * @param array $terms + * An array of taxonomy terms as returned by taxonomy_term_load_multiple(). + * @param string $view_mode + * View mode, e.g. 'full', 'teaser'... + * @param int $weight + * An integer representing the weight of the first node in the list. + * @param string $langcode + * (optional) A language code to use for rendering. Defaults to the global + * content language of the current request. + * + * @return array + * An array in the format expected by drupal_render(). + */ +function taxonomy_term_view_multiple(array $terms, $view_mode = 'teaser', $weight = 0, $langcode = NULL) { + field_attach_prepare_view('taxonomy_term', $terms, $view_mode, $langcode); + entity_prepare_view('taxonomy_term', $terms, $langcode); + $build = array(); + foreach ($terms as $term) { + $build['taxonomy_terms'][$term->tid] = taxonomy_term_view($term, $view_mode, $langcode); + $build['taxonomy_terms'][$term->tid]['#weight'] = $weight; + $weight++; + } + $build['taxonomy_terms']['#sorted'] = TRUE; + return $build; +} + +/** + * Builds a structured array representing the term's content. + * + * The content built for the taxonomy term (field values, file attachments or + * other term components) will vary depending on the $view_mode parameter. + * + * Drupal core defines the following view modes for terms, with the following + * default use cases: + * - full (default): term is displayed on its own page (taxonomy/term/123) + * Contributed modules might define additional view modes, or use existing + * view modes in additional contexts. + * + * @param Drupal\taxonomy\Term $term + * A taxonomy term object. + * @param string $view_mode + * View mode, e.g. 'full', 'teaser'... + * @param string $langcode + * (optional) A language code to use for rendering. Defaults to the global + * content language of the current request. + */ +function taxonomy_term_build_content(Term $term, $view_mode = 'full', $langcode = NULL) { + if (!isset($langcode)) { + $langcode = language(LANGUAGE_TYPE_CONTENT)->langcode; + } + + // Remove previously built content, if exists. + $term->content = array(); + + // Try to add in the core taxonomy pieces like description and nodes + $type = 'taxonomy_term'; + $settings = field_view_mode_settings($term->entityType(), $term->bundle()); + $fields = field_extra_fields_get_display($term->entityType(), $term->bundle(), $view_mode); + if (!empty($term->description) && isset($fields['description']) && $fields['description']['visible']) { + $term->content['description'] = array( + '#markup' => check_markup($term->description, $term->format, '', TRUE), + '#weight' => $fields['description']['weight'], + '#prefix' => '
', + '#suffix' => '
', + ); + } + + // Build fields content. + // In case of a multiple view, taxonomy_term_view_multiple() already ran the + // 'prepare_view' step. An internal flag prevents the operation from running + // twice. + field_attach_prepare_view('taxonomy_term', array($term->tid => $term), $view_mode, $langcode); + entity_prepare_view('taxonomy_term', array($term->tid => $term), $langcode); + $term->content += field_attach_view('taxonomy_term', $term, $view_mode, $langcode); + + // Allow modules to make their own additions to the taxonomy term. + module_invoke_all('taxonomy_term_view', $term, $view_mode, $langcode); + module_invoke_all('entity_view', $term, $view_mode, $langcode); +} + +/** * Generate an array for rendering the given term. * * @param Drupal\taxonomy\Term $term * A taxonomy term entity. - * @param $view_mode + * @param string $view_mode * View mode, e.g. 'full', 'teaser'... - * @param $langcode + * @param string $langcode * (optional) A language code to use for rendering. Defaults to the global * content language of the current request. * @@ -587,28 +682,20 @@ function taxonomy_term_view(Term $term, $view_mode = 'full', $langcode = NULL) { $langcode = language(LANGUAGE_TYPE_CONTENT)->langcode; } - field_attach_prepare_view('taxonomy_term', array($term->tid => $term), $view_mode, $langcode); - entity_prepare_view('taxonomy_term', array($term->tid => $term), $langcode); + // Populate $term->content with a render() array. + taxonomy_term_build_content($term, $view_mode, $langcode); + $build = $term->content; + + // We don't need duplicate rendering info in $term->content. + unset($term->content); - $build = array( + $build += array( '#theme' => 'taxonomy_term', '#term' => $term, '#view_mode' => $view_mode, '#language' => $langcode, ); - $build += field_attach_view('taxonomy_term', $term, $view_mode, $langcode); - - // Add term description if the term has one. - if (!empty($term->description)) { - $build['description'] = array( - '#markup' => check_markup($term->description, $term->format, '', TRUE), - '#weight' => 0, - '#prefix' => '
', - '#suffix' => '
', - ); - } - $build['#attached']['css'][] = drupal_get_path('module', 'taxonomy') . '/taxonomy.css'; // Allow modules to modify the structured term. diff --git a/core/modules/taxonomy/taxonomy.pages.inc b/core/modules/taxonomy/taxonomy.pages.inc index 597b95d..7c77716 100644 --- a/core/modules/taxonomy/taxonomy.pages.inc +++ b/core/modules/taxonomy/taxonomy.pages.inc @@ -35,21 +35,24 @@ function taxonomy_term_page(Term $term) { drupal_set_breadcrumb($breadcrumb); drupal_add_feed('taxonomy/term/' . $term->tid . '/feed', 'RSS - ' . $term->label()); - $build = array(); + // If there is a menu link to this term, the link becomes the last part + // of the active trail, and the link name becomes the page title. + // Thus, we must explicitly set the page title to be the node title. + $uri = $term->uri(); - $build['term_heading'] = array( - '#prefix' => '
', - '#suffix' => '
', - 'term' => taxonomy_term_view($term, 'full'), - ); + // Set the term path as the canonical URL to prevent duplicate content. + drupal_add_html_head_link(array('rel' => 'canonical', 'href' => url($uri['path'], $uri['options'])), TRUE); + // Set the non-aliased path as a default shortlink. + drupal_add_html_head_link(array('rel' => 'shortlink', 'href' => url($uri['path'], array_merge($uri['options'], array('alias' => TRUE)))), TRUE); + $build = taxonomy_term_show($term); if ($nids = taxonomy_select_nodes($term->tid, TRUE, variable_get('default_nodes_main', 10))) { $nodes = node_load_multiple($nids); $build += node_view_multiple($nodes); $build['pager'] = array( '#theme' => 'pager', '#weight' => 5, - ); + ); } else { $build['no_content'] = array(