summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel Catchpole2014-09-15 09:17:06 (GMT)
committerNathaniel Catchpole2014-09-15 09:17:06 (GMT)
commit983cdbf0aea2bb9cd710bcefc73909e7dced5dd8 (patch)
treea6b8c0b58c717f1867e098febefb8b86bd07f3a6
parent5e4b9a9a148673ddc6640ff26ed86b01eb42511e (diff)
Issue #1857256 by dawehner, xjm, tim.plunkett, jibran, ParisLiakos, hussainweb, pcambra, ekes, InternetDevels, rhabbachi, rdrh555, tstoeckler, oadaeh, Gábor Hojtsy, vijaycs85: Fixed Convert the taxonomy listing and feed at /taxonomy/term/%term to Views.
-rw-r--r--core/modules/node/node.module95
-rw-r--r--core/modules/node/src/Plugin/views/row/Rss.php1
-rw-r--r--core/modules/node/src/Tests/NodeAccessBaseTableTest.php2
-rw-r--r--core/modules/rdf/src/Tests/TaxonomyAttributesTest.php2
-rw-r--r--core/modules/taxonomy/config/install/core.entity_view_mode.taxonomy_term.full.yml2
-rw-r--r--core/modules/taxonomy/config/install/views.view.taxonomy_term.yml101
-rw-r--r--core/modules/taxonomy/config/schema/taxonomy.views.schema.yml12
-rw-r--r--core/modules/taxonomy/src/Controller/TaxonomyController.php30
-rw-r--r--core/modules/taxonomy/src/TermViewsData.php26
-rw-r--r--core/modules/taxonomy/src/Tests/TermIndexTest.php7
-rw-r--r--core/modules/taxonomy/src/Tests/TermTest.php1
-rw-r--r--core/modules/taxonomy/src/Tests/Views/TaxonomyTermViewTest.php122
-rw-r--r--core/modules/taxonomy/taxonomy.module40
-rw-r--r--core/modules/taxonomy/taxonomy.pages.inc81
-rw-r--r--core/modules/taxonomy/taxonomy.routing.yml11
-rw-r--r--core/modules/views/config/schema/views.argument.schema.yml3
-rw-r--r--core/modules/views/config/schema/views.data_types.schema.yml9
-rw-r--r--core/modules/views/src/EventSubscriber/RouteSubscriber.php6
-rw-r--r--core/modules/views/src/Plugin/views/display/PathPluginBase.php7
-rw-r--r--core/modules/views/tests/src/Unit/EventSubscriber/RouteSubscriberTest.php34
-rw-r--r--core/modules/views/tests/src/Unit/Plugin/display/PathPluginBaseTest.php75
21 files changed, 366 insertions, 301 deletions
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index d7eeeed..5dc8d92 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -894,100 +894,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 = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
- $output .= "<rss version=\"" . $channel["version"] . "\" xml:base=\"" . $base_url . "\" " . new Attribute($namespaces) . ">\n";
- $output .= format_rss_channel($channel['title'], $channel['link'], $channel['description'], $items, $channel['language'], $channel_extras);
- $output .= "</rss>\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
@@ -1049,7 +955,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 @@ class Rss extends RowPluginBase {
}
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 08ce707..8b7bf6a 100644
--- a/core/modules/taxonomy/config/install/views.view.taxonomy_term.yml
+++ b/core/modules/taxonomy/config/install/views.view.taxonomy_term.yml
@@ -1,5 +1,5 @@
langcode: en
-status: false
+status: true
dependencies:
module:
- node
@@ -69,7 +69,7 @@ display:
sorts:
sticky:
id: sticky
- table: node_field_data
+ table: taxonomy_index
field: sticky
order: DESC
plugin_id: standard
@@ -82,7 +82,7 @@ display:
provider: views
created:
id: created
- table: node_field_data
+ table: taxonomy_index
field: created
order: DESC
plugin_id: date
@@ -95,35 +95,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
@@ -132,42 +121,26 @@ 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
- table: node_field_data
- field: status_extra
- group: 0
- expose:
- operator: '0'
- plugin_id: node_status
- provider: node
langcode:
id: langcode
table: node_field_data
@@ -224,7 +197,21 @@ 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: { }
@@ -276,7 +263,7 @@ display:
last: 'last »'
quantity: 9
provider: views
- path: taxonomy/term/%/%/feed
+ path: taxonomy/term/%/feed
displays:
page: page
default: '0'
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 @@ use Drupal\taxonomy\VocabularyInterface;
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 @@ class TaxonomyController extends ControllerBase {
}
/**
- * @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 @@ class TaxonomyController extends ControllerBase {
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 @@ class TermViewsData implements EntityViewsDataInterface {
),
);
+
+ $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 @@ use Drupal\Core\Field\FieldStorageDefinitionInterface;
*/
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 @@ class TermTest extends TaxonomyTestBase {
* 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..e1aa1de
--- /dev/null
+++ b/core/modules/taxonomy/src/Tests/Views/TaxonomyTermViewTest.php
@@ -0,0 +1,122 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\taxonomy\Tests\Views\TaxonomyTermViewTest.
+ */
+
+namespace Drupal\taxonomy\Tests\Views;
+
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\Language\Language;
+use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\user\Entity\Role;
+
+/**
+ * Tests the taxonomy term view page and its translation.
+ *
+ * @group taxonomy
+ */
+class TaxonomyTermViewTest extends TaxonomyTestBase {
+
+ /**
+ * Modules to enable.
+ *
+ * @var array
+ */
+ public static $modules = array('taxonomy', 'views');
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+
+ // Create an administrative user.
+ $this->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 = ConfigurableLanguage::createFromLangcode('ur');
+ $language->save();
+ // 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)'));
+
+ $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 9668dbe..7b26e7b 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -16,6 +16,7 @@ use Drupal\file\FileInterface;
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 .= '<dd>' . t('Before you can use a new vocabulary to classify your content, a new Taxonomy term field must be added to a <a href="@ctedit">content type</a> on its <em>manage fields</em> page. When adding a taxonomy field, you choose a <em>widget</em> 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 <em>field settings</em> 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'))) . '</dd>';
$output .= '<dt>' . t('Classifying content') . '</dt>';
$output .= '<dd>' . 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 <a href="@addnode">add new content</a>.', array('@addnode' => url('node/add'))) . '</dd>';
- $output .= '<dt>' . t('Viewing listings and RSS feeds by term') . '</dt>';
- $output .= '<dd>' . 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 <em>country rock</em> 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 <em>taxonomy/term/123</em>. The RSS feed will use the path <em>taxonomy/term/123/feed</em> (the RSS icon for this term's listing will automatically display in your browser's address bar when viewing the listing page).") . '</dd>';
+ $output .= '<dt>' . t('Viewing listings') . '</dt>';
+ $output .= '<dd>' . t("Each taxonomy term automatically provides a page listing content that has its classification. For example, if the taxonomy term <em>country rock</em> 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 <em>taxonomy/term/123</em>.") . '</dd>';
$output .= '<dt>' . t('Extending Taxonomy module') . '</dt>';
$output .= '<dd>' . t('There are <a href="@taxcontrib">many contributed modules</a> 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 .= '</dl>';
@@ -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,
@@ -328,8 +360,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 (\Drupal::routeMatch()->getRouteName() == '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 @@
-<?php
-
-/**
- * @file
- * Page callbacks for the taxonomy module.
- */
-
-use Drupal\taxonomy\Entity\Term;
-
-/**
- * Menu callback; displays all nodes associated with a 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::termPage().
- */
-function taxonomy_term_page(Term $term) {
- $build['#attached']['drupal_add_feed'][] = array('taxonomy/term/' . $term->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' => '<p>',
- '#markup' => t('There is currently no content classified with this term.'),
- '#suffix' => '</p>',
- );
- }
- 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 21da5dd..85ab981 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..f1f02b3 100644
--- a/core/modules/views/src/EventSubscriber/RouteSubscriber.php
+++ b/core/modules/views/src/EventSubscriber/RouteSubscriber.php
@@ -87,6 +87,10 @@ class RouteSubscriber extends RouteSubscriberBase {
$events = parent::getSubscribedEvents();
$events[KernelEvents::VIEW][] = array('onHtmlPage', 75);
$events[RoutingEvents::FINISHED] = array('routeRebuildFinished');
+ // Ensure to run after the entity resolver subscriber
+ // @see \Drupal\Core\EventSubscriber\EntityRouteAlterSubscriber
+ $events[RoutingEvents::ALTER] = ['onAlterRoutes', -175];
+
return $events;
}
@@ -168,7 +172,9 @@ class RouteSubscriber extends RouteSubscriberBase {
$view_route_names = $display->alterRoutes($collection);
$this->viewRouteNames = $view_route_names + $this->viewRouteNames;
foreach ($view_route_names as $id_display => $route_name) {
+ $view_route_name = $this->viewsDisplayPairs[$id_display];
unset($this->viewsDisplayPairs[$id_display]);
+ $collection->remove("views.$view_route_name");
}
}
}
diff --git a/core/modules/views/src/Plugin/views/display/PathPluginBase.php b/core/modules/views/src/Plugin/views/display/PathPluginBase.php
index 4f6e220..2400ac0 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 @@ abstract class PathPluginBase extends DisplayPluginBase implements DisplayRouter
// 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 @@ abstract class PathPluginBase extends DisplayPluginBase implements DisplayRouter
// 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/EventSubscriber/RouteSubscriberTest.php b/core/modules/views/tests/src/Unit/EventSubscriber/RouteSubscriberTest.php
index e48c311..16788d9 100644
--- a/core/modules/views/tests/src/Unit/EventSubscriber/RouteSubscriberTest.php
+++ b/core/modules/views/tests/src/Unit/EventSubscriber/RouteSubscriberTest.php
@@ -88,6 +88,7 @@ class RouteSubscriberTest extends UnitTestCase {
*/
public function testOnAlterRoutes() {
$collection = new RouteCollection();
+ // The first route will be overridden later.
$collection->add('test_route', new Route('test_route', array('_controller' => 'Drupal\Tests\Core\Controller\TestController')));
$route_2 = new Route('test_route/example', array('_controller' => 'Drupal\Tests\Core\Controller\TestController'));
$collection->add('test_route_2', $route_2);
@@ -99,28 +100,39 @@ class RouteSubscriberTest extends UnitTestCase {
// The page_1 display overrides an existing route, so the dynamicRoutes
// should only call the second display.
$display_1->expects($this->once())
+ ->method('collectRoutes')
+ ->willReturnCallback(function() use ($collection) {
+ $collection->add('views.test_id.page_1', new Route('test_route', ['_content' => 'Drupal\views\Routing\ViewPageController']));
+ return ['test_id.page_1' => 'views.test_id.page_1'];
+ });
+ $display_1->expects($this->once())
->method('alterRoutes')
- ->will($this->returnValue(array('test_id.page_1' => 'test_route')));
- $display_1->expects($this->never())
- ->method('collectRoutes');
+ ->willReturn(['test_id.page_1' => 'test_route']);
$display_2->expects($this->once())
- ->method('alterRoutes')
- ->will($this->returnValue(array()));
- $display_2->expects($this->once())
->method('collectRoutes')
- ->will($this->returnValue(array('test_id.page_2' => 'views.test_id.page_2')));
-
- $this->assertNull($this->routeSubscriber->onAlterRoutes($route_event));
+ ->willReturnCallback(function() use ($collection) {
+ $collection->add('views.test_id.page_2', new Route('test_route', ['_content' => 'Drupal\views\Routing\ViewPageController']));
+ return ['test_id.page_2' => 'views.test_id.page_2'];
+ });
+ $display_2->expects($this->once())
+ ->method('alterRoutes')
+ ->willReturn([]);
- // Ensure that after the alterRoutes the collectRoutes method is just called
- // once (not for page_1 anymore).
+ // Ensure that even both the collectRoutes() and alterRoutes() methods
+ // are called on the displays, we ensure that the route first defined by
+ // views is dropped.
$this->routeSubscriber->routes();
+ $this->assertNull($this->routeSubscriber->onAlterRoutes($route_event));
$this->state->expects($this->once())
->method('set')
->with('views.view_route_names', array('test_id.page_1' => 'test_route', 'test_id.page_2' => 'views.test_id.page_2'));
+
+ $collection = $route_event->getRouteCollection();
+ $this->assertEquals(['test_route', 'test_route_2', 'views.test_id.page_2'], array_keys($collection->all()));
+
$this->routeSubscriber->routeRebuildFinished();
}
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 @@ class PathPluginBaseTest extends UnitTestCase {
}
/**
- * 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 @@ class PathPluginBaseTest extends UnitTestCase {
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 @@ class PathPluginBaseTest extends UnitTestCase {
}
/**
+ * 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() {