diff --git a/core/modules/node/node.module b/core/modules/node/node.module index d8882cc..4e1a3e6 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -919,100 +919,6 @@ function node_get_recent($number = 10) { } /** - * Page callback: Generates and prints an RSS feed. - * - * Generates an RSS feed from an array of node IDs, and prints it with an HTTP - * header, with Content Type set to RSS/XML. - * - * @param $nids - * (optional) An array of node IDs (nid). Defaults to FALSE so empty feeds can - * be generated with passing an empty array, if no items are to be added - * to the feed. - * @param $channel - * (optional) An associative array containing 'title', 'link', 'description', - * and other keys, to be parsed by format_rss_channel() and - * format_xml_elements(). A list of channel elements can be found at the - * @link http://cyber.law.harvard.edu/rss/rss.html RSS 2.0 Specification. @endlink - * The link should be an absolute URL. - * - * @todo Convert taxonomy_term_feed() to a view, so this method is not needed - * anymore. - * - * @return Symfony\Component\HttpFoundation\Response - * A response object. - * - * @see node_menu() - */ -function node_feed($nids = FALSE, $channel = array()) { - global $base_url; - $language_content = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT); - $rss_config = \Drupal::config('system.rss'); - - if ($nids === FALSE) { - $nids = \Drupal::entityQuery('node') - ->condition('status', 1) - ->condition('promote', 1) - ->sort('created', 'DESC') - ->range(0, $rss_config->get('items.limit')) - ->addTag('node_access') - ->execute(); - } - - $view_mode = $rss_config->get('items.view_mode'); - $namespaces = array('xmlns:dc' => 'http://purl.org/dc/elements/1.1/'); - - // Load all nodes to be rendered. - /** @var \Drupal\node\NodeInterface[] $nodes */ - $nodes = node_load_multiple($nids); - $items = ''; - foreach ($nodes as $node) { - $item_text = ''; - - $node->link = url('node/' . $node->id(), array('absolute' => TRUE)); - $node->rss_namespaces = array(); - $node->rss_elements = array( - array('key' => 'pubDate', 'value' => gmdate('r', $node->getCreatedTime())), - array('key' => 'dc:creator', 'value' => $node->getOwner()->label()), - array('key' => 'guid', 'value' => $node->id() . ' at ' . $base_url, 'attributes' => array('isPermaLink' => 'false')) - ); - - // The node gets built and modules add to or modify $node->rss_elements - // and $node->rss_namespaces. - $build = node_view($node, 'rss'); - unset($build['#theme']); - - if (!empty($node->rss_namespaces)) { - $namespaces = array_merge($namespaces, $node->rss_namespaces); - } - - if ($view_mode != 'title') { - // We render node contents and force links to be last. - $build['links']['#weight'] = 1000; - $item_text .= drupal_render($build); - } - - $items .= format_rss_item($node->label(), $node->link, $item_text, $node->rss_elements); - } - - $channel_defaults = array( - 'version' => '2.0', - 'title' => \Drupal::config('system.site')->get('name'), - 'link' => $base_url, - 'description' => $rss_config->get('channel.description'), - 'language' => $language_content->id - ); - $channel_extras = array_diff_key($channel, $channel_defaults); - $channel = array_merge($channel_defaults, $channel); - - $output = "\n"; - $output .= "\n"; - $output .= format_rss_channel($channel['title'], $channel['link'], $channel['description'], $items, $channel['language'], $channel_extras); - $output .= "\n"; - - return new Response($output, 200, array('Content-Type' => 'application/rss+xml; charset=utf-8')); -} - -/** * Generates an array for rendering the given node. * * @param \Drupal\node\NodeInterface $node @@ -1074,7 +980,6 @@ function node_page_build(&$page) { * default setting for number of posts to show on node listing pages. * * @see node_page_default() - * @see taxonomy_term_page() * @see node_form_system_site_information_settings_form_submit() */ function node_form_system_site_information_settings_form_alter(&$form, FormStateInterface $form_state, $form_id) { diff --git a/core/modules/node/src/Plugin/views/row/Rss.php b/core/modules/node/src/Plugin/views/row/Rss.php index 820f9fb..04aff57 100644 --- a/core/modules/node/src/Plugin/views/row/Rss.php +++ b/core/modules/node/src/Plugin/views/row/Rss.php @@ -85,7 +85,6 @@ public function preRender($values) { } public function render($row) { - // For the most part, this code is taken from node_feed() in node.module global $base_url; $nid = $row->{$this->field_alias}; diff --git a/core/modules/node/src/Tests/NodeAccessBaseTableTest.php b/core/modules/node/src/Tests/NodeAccessBaseTableTest.php index 0bd1707..d5e4dd4 100644 --- a/core/modules/node/src/Tests/NodeAccessBaseTableTest.php +++ b/core/modules/node/src/Tests/NodeAccessBaseTableTest.php @@ -19,7 +19,7 @@ class NodeAccessBaseTableTest extends NodeTestBase { * * @var array */ - public static $modules = array('node_access_test'); + public static $modules = array('node_access_test', 'views'); /** * The installation profile to use with this test. diff --git a/core/modules/rdf/src/Tests/TaxonomyAttributesTest.php b/core/modules/rdf/src/Tests/TaxonomyAttributesTest.php index 76c97cd..1900485 100644 --- a/core/modules/rdf/src/Tests/TaxonomyAttributesTest.php +++ b/core/modules/rdf/src/Tests/TaxonomyAttributesTest.php @@ -21,7 +21,7 @@ class TaxonomyAttributesTest extends TaxonomyTestBase { * * @var array */ - public static $modules = array('rdf'); + public static $modules = array('rdf', 'views'); protected function setUp() { parent::setUp(); diff --git a/core/modules/taxonomy/config/install/core.entity_view_mode.taxonomy_term.full.yml b/core/modules/taxonomy/config/install/core.entity_view_mode.taxonomy_term.full.yml index 50b1854..bb8c47e 100644 --- a/core/modules/taxonomy/config/install/core.entity_view_mode.taxonomy_term.full.yml +++ b/core/modules/taxonomy/config/install/core.entity_view_mode.taxonomy_term.full.yml @@ -1,6 +1,6 @@ id: taxonomy_term.full label: 'Taxonomy term page' -status: false +status: true cache: true targetEntityType: taxonomy_term dependencies: diff --git a/core/modules/taxonomy/config/install/views.view.taxonomy_term.yml b/core/modules/taxonomy/config/install/views.view.taxonomy_term.yml index db374a8..6ff1211 100644 --- a/core/modules/taxonomy/config/install/views.view.taxonomy_term.yml +++ b/core/modules/taxonomy/config/install/views.view.taxonomy_term.yml @@ -1,8 +1,16 @@ -base_field: nid +langcode: en +status: true +dependencies: + module: + - taxonomy +id: taxonomy_term +label: 'Taxonomy term' +module: taxonomy +description: 'Content belonging to a certain taxonomy term.' +tag: default base_table: node +base_field: nid core: '8' -description: 'Content belonging to a certain taxonomy term.' -status: false display: default: id: default @@ -60,7 +68,7 @@ display: sorts: sticky: id: sticky - table: node_field_data + table: taxonomy_index field: sticky order: DESC plugin_id: standard @@ -73,7 +81,7 @@ display: provider: views created: id: created - table: node_field_data + table: taxonomy_index field: created order: DESC plugin_id: date @@ -86,35 +94,24 @@ display: granularity: second provider: views arguments: - term_node_tid_depth: - id: term_node_tid_depth - table: node - field: term_node_tid_depth - default_action: 'not found' + tid: + id: tid + table: taxonomy_index + field: tid + relationship: none + group_type: group + admin_label: '' + dependencies: + module: + - taxonomy + default_action: ignore exception: value: all - title_enable: true + title_enable: false title: All title_enable: true title: '%1' default_argument_type: fixed - summary: - format: default_summary - specify_validation: true - validate: - type: 'entity:taxonomy_term' - fail: 'not found' - validate_options: - access: true - operation: view - multiple: 1 - bundles: { } - depth: 0 - break_phrase: true - plugin_id: taxonomy_index_tid_depth - relationship: none - group_type: group - admin_label: '' default_argument_options: argument: '' default_argument_skip_url: false @@ -123,42 +120,68 @@ display: count: true items_per_page: 25 override: false - provider: taxonomy - term_node_tid_depth_modifier: - id: term_node_tid_depth_modifier - table: node - field: term_node_tid_depth_modifier - exception: - title_enable: true - default_argument_type: fixed summary: + sort_order: asc + number_of_records: 0 format: default_summary specify_validation: true - plugin_id: taxonomy_index_tid_depth_modifier - relationship: none - group_type: group - admin_label: '' - default_action: ignore - title_enable: false - title: '' - default_argument_options: { } - default_argument_skip_url: false - summary_options: { } validate: - type: none + type: 'entity:taxonomy_term' fail: 'not found' - validate_options: { } + validate_options: + access: true + operation: view + multiple: 0 + bundles: { } + break_phrase: false + add_table: false + require_value: false + reduce_duplicates: false + plugin_id: taxonomy_index_tid provider: taxonomy filters: - status_extra: - id: status_extra + langcode: + id: langcode table: node_field_data - field: status_extra - group: 0 + field: langcode + relationship: none + group_type: group + admin_label: '' + dependencies: + module: + - views + operator: in + value: + '***LANGUAGE_language_content***': '***LANGUAGE_language_content***' + group: 1 + exposed: false expose: - operator: '0' - plugin_id: node_status - provider: node + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + plugin_id: language + provider: views style: type: default options: @@ -173,11 +196,27 @@ display: view_mode: teaser comments: false provider: views - header: { } + header: + entity_taxonomy_term: + id: entity_taxonomy_term + table: views + field: entity_taxonomy_term + relationship: none + group_type: group + admin_label: '' + empty: true + tokenize: true + entity_id: '!1' + view_mode: full + bypass_access: false + plugin_id: entity + provider: views footer: { } empty: { } relationships: { } fields: { } + field_langcode: '***LANGUAGE_language_content***' + field_langcode_add_to_query: null page_1: id: page_1 display_title: Page @@ -189,6 +228,8 @@ display: options: { } provider: views path: taxonomy/term/% + field_langcode: '***LANGUAGE_language_content***' + field_langcode_add_to_query: null feed_1: id: feed_1 display_title: Feed @@ -221,7 +262,7 @@ display: last: 'last ยป' quantity: 9 provider: views - path: taxonomy/term/%/%/feed + path: taxonomy/term/%/feed displays: page: page default: '0' @@ -238,12 +279,5 @@ display: relationship: none view_mode: default provider: views -label: 'Taxonomy term' -module: taxonomy -id: taxonomy_term -tag: default -langcode: en -dependencies: - module: - - node - - taxonomy + field_langcode: '***LANGUAGE_language_content***' + field_langcode_add_to_query: null diff --git a/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml b/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml index 1de5e1e..3fce880 100644 --- a/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml +++ b/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml @@ -1,18 +1,8 @@ # Schema for the views plugins of the Taxonomy module. views.argument.taxonomy_index_tid: - type: views_argument + type: views.argument.many_to_one label: 'Taxonomy term ID' - mapping: - break_phrase: - type: boolean - label: 'Allow multiple values' - add_table: - type: boolean - label: 'Allow multiple filter values to work together' - require_value: - type: boolean - label: 'Do not display items with no value in summary' views.argument.taxonomy_index_tid_depth: type: views_argument diff --git a/core/modules/taxonomy/src/Controller/TaxonomyController.php b/core/modules/taxonomy/src/Controller/TaxonomyController.php index 0ddbce4..e24eb6f 100644 --- a/core/modules/taxonomy/src/Controller/TaxonomyController.php +++ b/core/modules/taxonomy/src/Controller/TaxonomyController.php @@ -18,6 +18,19 @@ class TaxonomyController extends ControllerBase { /** + * Title callback for term pages. + * + * @param \Drupal\taxonomy\TermInterface $term + * A taxonomy term entity. + * + * @return + * The term name to be used as the page title. + */ + public function getTitle(TermInterface $term) { + return $term->label(); + } + + /** * Returns a rendered edit form to create a new term associated to the given vocabulary. * * @param \Drupal\taxonomy\VocabularyInterface $taxonomy_vocabulary @@ -32,15 +45,6 @@ public function addForm(VocabularyInterface $taxonomy_vocabulary) { } /** - * @todo Remove taxonomy_term_page(). - */ - public function termPage(TermInterface $taxonomy_term) { - module_load_include('pages.inc', 'taxonomy'); - return taxonomy_term_page($taxonomy_term); - - } - - /** * Route title callback. * * @param \Drupal\taxonomy\VocabularyInterface $taxonomy_vocabulary @@ -66,12 +70,4 @@ public function termTitle(TermInterface $taxonomy_term) { return Xss::filter($taxonomy_term->getName()); } - /** - * @todo Remove taxonomy_term_feed(). - */ - public function termFeed(TermInterface $taxonomy_term) { - module_load_include('pages.inc', 'taxonomy'); - return taxonomy_term_feed($taxonomy_term); - } - } diff --git a/core/modules/taxonomy/src/TermViewsData.php b/core/modules/taxonomy/src/TermViewsData.php index 03eca19..8c9f37a 100644 --- a/core/modules/taxonomy/src/TermViewsData.php +++ b/core/modules/taxonomy/src/TermViewsData.php @@ -313,6 +313,32 @@ public function getViewsData() { ), ); + + $data['taxonomy_index']['sticky'] = [ + 'title' => t('Sticky status'), + 'help' => t('Whether or not the content related to a term is sticky.'), + 'filter' => [ + 'id' => 'boolean', + 'label' => t('Sticky status'), + 'type' => 'yes-no', + ], + 'sort' => [ + 'id' => 'standard', + 'help' => t('Whether or not the content related to a term is sticky. To list sticky content first, set this to descending.'), + ], + ]; + + $data['taxonomy_index']['created'] = [ + 'title' => t('Post date'), + 'help' => t('The date the content related to a term was posted.'), + 'sort' => [ + 'id' => 'date' + ], + 'filter' => [ + 'id' => 'date', + ], + ]; + $data['taxonomy_term_hierarchy']['table']['group'] = t('Taxonomy term'); $data['taxonomy_term_hierarchy']['table']['join'] = array( diff --git a/core/modules/taxonomy/src/Tests/TermIndexTest.php b/core/modules/taxonomy/src/Tests/TermIndexTest.php index d1ec1b4..877b8b7 100644 --- a/core/modules/taxonomy/src/Tests/TermIndexTest.php +++ b/core/modules/taxonomy/src/Tests/TermIndexTest.php @@ -17,6 +17,13 @@ */ class TermIndexTest extends TaxonomyTestBase { + /** + * Modules to enable. + * + * @var array + */ + public static $modules = array('views'); + protected function setUp() { parent::setUp(); diff --git a/core/modules/taxonomy/src/Tests/TermTest.php b/core/modules/taxonomy/src/Tests/TermTest.php index b2e9757..8b66001 100644 --- a/core/modules/taxonomy/src/Tests/TermTest.php +++ b/core/modules/taxonomy/src/Tests/TermTest.php @@ -347,6 +347,7 @@ function testTermAutocompletion() { * Save, edit and delete a term using the user interface. */ function testTermInterface() { + \Drupal::moduleHandler()->install(array('views')); $edit = array( 'name[0][value]' => $this->randomMachineName(12), 'description[0][value]' => $this->randomMachineName(100), diff --git a/core/modules/taxonomy/src/Tests/Views/TaxonomyTermViewTest.php b/core/modules/taxonomy/src/Tests/Views/TaxonomyTermViewTest.php new file mode 100644 index 0000000..7bba5b3 --- /dev/null +++ b/core/modules/taxonomy/src/Tests/Views/TaxonomyTermViewTest.php @@ -0,0 +1,126 @@ +admin_user = $this->drupalCreateUser(array('administer taxonomy', 'bypass node access')); + $this->drupalLogin($this->admin_user); + + // Create a vocabulary and add two term reference fields to article nodes. + + $this->field_name_1 = drupal_strtolower($this->randomMachineName()); + entity_create('field_storage_config', array( + 'name' => $this->field_name_1, + 'entity_type' => 'node', + 'type' => 'taxonomy_term_reference', + 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, + 'settings' => array( + 'allowed_values' => array( + array( + 'vocabulary' => $this->vocabulary->id(), + 'parent' => 0, + ), + ), + ), + ))->save(); + entity_create('field_instance_config', array( + 'field_name' => $this->field_name_1, + 'bundle' => 'article', + 'entity_type' => 'node', + ))->save(); + entity_get_form_display('node', 'article', 'default') + ->setComponent($this->field_name_1, array( + 'type' => 'options_select', + )) + ->save(); + entity_get_display('node', 'article', 'default') + ->setComponent($this->field_name_1, array( + 'type' => 'taxonomy_term_reference_link', + )) + ->save(); + } + + /** + * Tests that the taxonomy term view is working properly. + */ + public function testTaxonomyTermView() { + // Create terms in the vocabulary. + $term = $this->createTerm($this->vocabulary); + + // Post an article. + $edit = array(); + $edit['title[0][value]'] = $original_title = $this->randomMachineName(); + $edit['body[0][value]'] = $this->randomMachineName(); + $edit["{$this->field_name_1}[]"] = $term->id(); + $this->drupalPostForm('node/add/article', $edit, t('Save')); + $node = $this->drupalGetNodeByTitle($edit['title[0][value]']); + + $this->drupalGet('taxonomy/term/' . $term->id()); + $this->assertText($term->label()); + $this->assertText($node->label()); + + \Drupal::moduleHandler()->install(array('language', 'content_translation')); + $language = new Language(array('id' => 'ur')); + language_save($language); + // Enable translation for the article content type and ensure the change is + // picked up. + content_translation_set_config('node', 'article', 'enabled', TRUE); + $roles = $this->admin_user->getRoles(TRUE); + Role::load(reset($roles)) + ->grantPermission('create content translations') + ->grantPermission('translate any entity') + ->save(); + drupal_static_reset(); + \Drupal::entityManager()->clearCachedDefinitions(); + \Drupal::service('router.builder')->rebuild(); + + $edit['title[0][value]'] = $translated_title = $this->randomMachineName(); + + $this->drupalPostForm('node/' . $node->id() . '/translations/add/en/ur', $edit, t('Save (this translation)')); + + /** @var \Drupal\views\ViewStorageInterface $view */ + $view = View::load('taxonomy_term'); + $view->enable()->save(); + + $this->drupalGet('taxonomy/term/' . $term->id()); + $this->assertText($term->label()); + $this->assertText($original_title); + $this->assertNoText($translated_title); + + $this->drupalGet('ur/taxonomy/term/' . $term->id()); + $this->assertText($term->label()); + $this->assertNoText($original_title); + $this->assertText($translated_title); + } + +} diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index d58064d..3165d4a 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -16,6 +16,7 @@ use Drupal\node\Entity\Node; use Drupal\taxonomy\Entity\Term; use Drupal\taxonomy\Entity\Vocabulary; +use Drupal\taxonomy\TermInterface; use Drupal\taxonomy\VocabularyInterface; use Drupal\Component\Utility\String; @@ -68,8 +69,8 @@ function taxonomy_help($route_name, RouteMatchInterface $route_match) { $output .= '
' . t('Before you can use a new vocabulary to classify your content, a new Taxonomy term field must be added to a content type on its manage fields page. When adding a taxonomy field, you choose a widget to use to enter the taxonomy information on the content editing page: a select list, checkboxes, radio buttons, or an auto-complete field (to build a free-tagging vocabulary). After choosing the field type and widget, on the subsequent field settings page you can choose the desired vocabulary, whether one or multiple terms can be chosen from the vocabulary, and other settings. The same vocabulary can be added to multiple content types, by using the "Re-use existing field" section on the manage fields page.', array('@ctedit' => url('admin/structure/types'))) . '
'; $output .= '
' . t('Classifying content') . '
'; $output .= '
' . t('After the vocabulary is assigned to the content type, you can start classifying content. The field with terms will appear on the content editing screen when you edit or add new content.', array('@addnode' => url('node/add'))) . '
'; - $output .= '
' . t('Viewing listings and RSS feeds by term') . '
'; - $output .= '
' . t("Each taxonomy term automatically provides a page listing content that has its classification, and a corresponding RSS feed. For example, if the taxonomy term country rock has the ID 123 (you can see this by looking at the URL when hovering on the linked term, which you can click to navigate to the listing page), then you will find this list at the path taxonomy/term/123. The RSS feed will use the path taxonomy/term/123/feed (the RSS icon for this term's listing will automatically display in your browser's address bar when viewing the listing page).") . '
'; + $output .= '
' . t('Viewing listings') . '
'; + $output .= '
' . t("Each taxonomy term automatically provides a page listing content that has its classificatio. For example, if the taxonomy term country rock has the ID 123 (you can see this by looking at the URL when hovering on the linked term, which you can click to navigate to the listing page), then you will find this list at the path taxonomy/term/123.") . '
'; $output .= '
' . t('Extending Taxonomy module') . '
'; $output .= '
' . t('There are many contributed modules that extend the behavior of the Taxonomy module for both display and organization of terms.', array('@taxcontrib' => 'http://drupal.org/project/modules?filters=tid:71&solrsort=sis_project_release_usage%20desc')); $output .= ''; @@ -126,6 +127,37 @@ function taxonomy_term_uri($term) { } /** + * Implements hook_page_build(). + */ +function taxonomy_page_build(&$page) { + $route_match = \Drupal::routeMatch(); + if ($route_match->getRouteName() == 'entity.taxonomy_term.canonical' && ($term = $route_match->getParameter('taxonomy_term')) && $term instanceof TermInterface) { + foreach ($term->uriRelationships() as $rel) { + // Set the URI relationships, like canonical. + $page['#attached']['drupal_add_html_head_link'][] = array( + array( + 'rel' => $rel, + 'href' => $term->url($rel), + ), + TRUE, + ); + + // Set the term path as the canonical URL to prevent duplicate content. + if ($rel == 'canonical') { + // Set the non-aliased canonical path as a default shortlink. + $page['#attached']['drupal_add_html_head_link'][] = array( + array( + 'rel' => 'shortlink', + 'href' => $term->url($rel, array('alias' => TRUE)), + ), + TRUE, + ); + } + } + } +} + +/** * Return nodes attached to a term across all field instances. * * This function requires taxonomy module to be maintaining its own tables, @@ -333,8 +365,8 @@ function template_preprocess_taxonomy_term(&$variables) { * A taxonomy term entity. */ function taxonomy_term_is_page(Term $term) { - if ($page_term = \Drupal::routeMatch()->getParameter('taxonomy_term')) { - return $page_term->id() == $term->id(); + if (in_array(\Drupal::routeMatch()->getRouteName(), ['view.taxonomy_term.page_1', 'entity.taxonomy_term.canonical']) && $page_term_id = \Drupal::routeMatch()->getRawParameter('taxonomy_term')) { + return $page_term_id == $term->id(); } return FALSE; } diff --git a/core/modules/taxonomy/taxonomy.pages.inc b/core/modules/taxonomy/taxonomy.pages.inc deleted file mode 100644 index 294dabb..0000000 --- a/core/modules/taxonomy/taxonomy.pages.inc +++ /dev/null @@ -1,81 +0,0 @@ -id() . '/feed', 'RSS - ' . $term->getName()); - - foreach ($term->uriRelationships() as $rel) { - // Set the term path as the canonical URL to prevent duplicate content. - $build['#attached']['drupal_add_html_head_link'][] = array( - array( - 'rel' => $rel, - 'href' => $term->url($rel), - ), - TRUE, - ); - - if ($rel == 'canonical') { - // Set the non-aliased canonical path as a default shortlink. - $build['#attached']['drupal_add_html_head_link'][] = array( - array( - 'rel' => 'shortlink', - 'href' => $term->url($rel, array('alias' => TRUE)), - ), - TRUE, - ); - } - } - - $build['taxonomy_terms'] = taxonomy_term_view_multiple(array($term->id() => $term)); - if ($nids = taxonomy_select_nodes($term->id(), TRUE, \Drupal::config('node.settings')->get('items_per_page'))) { - $nodes = node_load_multiple($nids); - $build['nodes'] = node_view_multiple($nodes); - $build['pager'] = array( - '#theme' => 'pager', - '#weight' => 5, - ); - } - else { - $build['no_content'] = array( - '#prefix' => '

', - '#markup' => t('There is currently no content classified with this term.'), - '#suffix' => '

', - ); - } - return $build; -} - -/** - * Generate the content feed for a taxonomy term. - * - * @param \Drupal\taxonomy\Entity\Term $term - * The taxonomy term entity. - * - * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. - * Use \Drupal\taxonomy\Controller\TaxonomyController::termFeed(). - */ -function taxonomy_term_feed(Term $term) { - $channel['link'] = url('taxonomy/term/' . $term->id(), array('absolute' => TRUE)); - $channel['title'] = \Drupal::config('system.site')->get('name') . ' - ' . $term->getName(); - // Only display the description if we have a single term, to avoid clutter and confusion. - // HTML will be removed from feed description. - $channel['description'] = $term->description->processed; - $nids = taxonomy_select_nodes($term->id(), FALSE, \Drupal::config('system.rss')->get('items.limit')); - - return node_feed($nids, $channel); -} diff --git a/core/modules/taxonomy/taxonomy.routing.yml b/core/modules/taxonomy/taxonomy.routing.yml index 5b065c2..9f6e70b 100644 --- a/core/modules/taxonomy/taxonomy.routing.yml +++ b/core/modules/taxonomy/taxonomy.routing.yml @@ -91,16 +91,7 @@ entity.taxonomy_vocabulary.overview_form: entity.taxonomy_term.canonical: path: '/taxonomy/term/{taxonomy_term}' defaults: - _content: '\Drupal\taxonomy\Controller\TaxonomyController::termPage' - _title: 'Taxonomy term' - _title_callback: '\Drupal\taxonomy\Controller\TaxonomyController::termTitle' - requirements: - _entity_access: 'taxonomy_term.view' - -taxonomy.term_feed: - path: '/taxonomy/term/{taxonomy_term}/feed' - defaults: - _content: '\Drupal\taxonomy\Controller\TaxonomyController::termFeed' + _entity_view: 'taxonomy_term.full' _title: 'Taxonomy term' _title_callback: '\Drupal\taxonomy\Controller\TaxonomyController::termTitle' requirements: diff --git a/core/modules/views/config/schema/views.argument.schema.yml b/core/modules/views/config/schema/views.argument.schema.yml index 8d9a3b4..2f43282 100644 --- a/core/modules/views/config/schema/views.argument.schema.yml +++ b/core/modules/views/config/schema/views.argument.schema.yml @@ -17,6 +17,9 @@ views.argument.many_to_one: require_value: type: boolean label: 'Do not display items with no value in summary' + reduce_duplicates: + type: boolean + label: 'Reduce duplicates' views.argument.null: type: views_argument diff --git a/core/modules/views/config/schema/views.data_types.schema.yml b/core/modules/views/config/schema/views.data_types.schema.yml index 8ebcea0..3b2a7f5 100644 --- a/core/modules/views/config/schema/views.data_types.schema.yml +++ b/core/modules/views/config/schema/views.data_types.schema.yml @@ -331,15 +331,8 @@ views_handler: type: string label: 'Provider' dependencies: - type: mapping + type: config_dependencies label: 'Dependencies' - mapping: - module: - type: sequence - label: 'Modules' - sequence: - - type: string - label: 'Dependency' views_argument: type: views_handler diff --git a/core/modules/views/src/EventSubscriber/RouteSubscriber.php b/core/modules/views/src/EventSubscriber/RouteSubscriber.php index 9fec99b..4b92282 100644 --- a/core/modules/views/src/EventSubscriber/RouteSubscriber.php +++ b/core/modules/views/src/EventSubscriber/RouteSubscriber.php @@ -87,6 +87,8 @@ public static function getSubscribedEvents() { $events = parent::getSubscribedEvents(); $events[KernelEvents::VIEW][] = array('onHtmlPage', 75); $events[RoutingEvents::FINISHED] = array('routeRebuildFinished'); + $events[RoutingEvents::ALTER] = ['onAlterRoutes', -175]; + return $events; } diff --git a/core/modules/views/src/Plugin/views/display/PathPluginBase.php b/core/modules/views/src/Plugin/views/display/PathPluginBase.php index 07a2816..168338e 100644 --- a/core/modules/views/src/Plugin/views/display/PathPluginBase.php +++ b/core/modules/views/src/Plugin/views/display/PathPluginBase.php @@ -239,6 +239,7 @@ public function alterRoutes(RouteCollection $collection) { // requirements). // Replace the existing route with a new one based on views. + $original_route = $collection->get($name); $collection->remove($name); $view_id = $this->view->storage->id(); @@ -251,9 +252,13 @@ public function alterRoutes(RouteCollection $collection) { // We assume that the numeric ids of the parameters match the one from // the view argument handlers. foreach ($parameters as $position => $parameter_name) { - $path = str_replace('arg_' . $position, $parameter_name, $path); + $path = str_replace('{arg_' . $position . '}', '{' . $parameter_name . '}', $path); $argument_map['arg_' . $position] = $parameter_name; } + // Copy the original options from the route, so for example we ensure + // that parameter conversion options is carried over. + $route->setOptions($route->getOptions() + $original_route->getOptions()); + // Set the corrected path and the mapping to the route object. $route->setOption('_view_argument_map', $argument_map); $route->setPath($path); diff --git a/core/modules/views/tests/src/Unit/Plugin/display/PathPluginBaseTest.php b/core/modules/views/tests/src/Unit/Plugin/display/PathPluginBaseTest.php index a6d3fab..56ec973 100644 --- a/core/modules/views/tests/src/Unit/Plugin/display/PathPluginBaseTest.php +++ b/core/modules/views/tests/src/Unit/Plugin/display/PathPluginBaseTest.php @@ -205,7 +205,7 @@ public function testCollectRoutesWithNamedParameters() { } /** - * Tests alter routes with parameters in the overriding route. + * Tests altering routes with parameters in the overridden route. */ public function testAlterRoutesWithParameters() { $collection = new RouteCollection(); @@ -213,7 +213,7 @@ public function testAlterRoutesWithParameters() { list($view) = $this->setupViewExecutableAccessPlugin(); - // Manually setup an argument handler. + // Manually set up an argument handler. $argument = $this->getMockBuilder('Drupal\views\Plugin\views\argument\ArgumentPluginBase') ->disableOriginalConstructor() ->getMock(); @@ -241,6 +241,77 @@ public function testAlterRoutesWithParameters() { } /** + * Tests altering routes with parameters and upcasting information + */ + public function testAlterRoutesWithParametersAndUpcasting() { + $collection = new RouteCollection(); + $collection->add('test_route', new Route('test_route/{parameter}', ['_controller' => 'Drupal\Tests\Core\Controller\TestController::content'], [], ['parameters' => ['taxonomy_term' => 'entity:entity_test']])); + + list($view) = $this->setupViewExecutableAccessPlugin(); + + // Manually set up an argument handler. + $argument = $this->getMockBuilder('Drupal\views\Plugin\views\argument\ArgumentPluginBase') + ->disableOriginalConstructor() + ->getMock(); + $view->argument['test_id'] = $argument; + + $display = array(); + $display['display_plugin'] = 'page'; + $display['id'] = 'page_1'; + $display['display_options'] = [ + 'path' => 'test_route/%', + ]; + $this->pathPlugin->initDisplay($view, $display); + + $view_route_names = $this->pathPlugin->alterRoutes($collection); + $this->assertEquals(['test_id.page_1' => 'test_route'], $view_route_names); + + // Ensure that the test_route is overridden. + $route = $collection->get('test_route'); + $this->assertInstanceOf('\Symfony\Component\Routing\Route', $route); + $this->assertEquals('test_id', $route->getDefault('view_id')); + $this->assertEquals('page_1', $route->getDefault('display_id')); + $this->assertEquals(['taxonomy_term' => 'entity:entity_test'], $route->getOption('parameters')); + // Ensure that the path did not changed and placeholders are respected kk. + $this->assertEquals('/test_route/{parameter}', $route->getPath()); + $this->assertEquals(['arg_0' => 'parameter'], $route->getOption('_view_argument_map')); + } + + /** + * Tests altering routes with optional parameters in the overridden route. + */ + public function testAlterRoutesWithOptionalParameters() { + $collection = new RouteCollection(); + $collection->add('test_route', new Route('test_route/{parameter}', array('_controller' => 'Drupal\Tests\Core\Controller\TestController::content'))); + + list($view) = $this->setupViewExecutableAccessPlugin(); + + $display = array(); + $display['display_plugin'] = 'page'; + $display['id'] = 'page_1'; + $display['display_options'] = array( + 'path' => 'test_route/%', + ); + $display['display_options']['arguments'] = array( + 'test_id' => array(), + 'test_id2' => array(), + ); + $this->pathPlugin->initDisplay($view, $display); + + $view_route_names = $this->pathPlugin->alterRoutes($collection); + $this->assertEquals(array('test_id.page_1' => 'test_route'), $view_route_names); + + // Ensure that the test_route is overridden. + $route = $collection->get('test_route'); + $this->assertInstanceOf('\Symfony\Component\Routing\Route', $route); + $this->assertEquals('test_id', $route->getDefault('view_id')); + $this->assertEquals('page_1', $route->getDefault('display_id')); + // Ensure that the path did not changed and placeholders are respected. + $this->assertEquals('/test_route/{parameter}/{arg_1}', $route->getPath()); + $this->assertEquals(array('arg_0' => 'parameter'), $route->getOption('_view_argument_map')); + } + + /** * Returns some mocked view entity, view executable, and access plugin. */ protected function setupViewExecutableAccessPlugin() {