diff --git a/core/modules/node/lib/Drupal/node/Plugin/views/row/Rss.php b/core/modules/node/lib/Drupal/node/Plugin/views/row/Rss.php index 2df6281..1e2e344 100644 --- a/core/modules/node/lib/Drupal/node/Plugin/views/row/Rss.php +++ b/core/modules/node/lib/Drupal/node/Plugin/views/row/Rss.php @@ -88,7 +88,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/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php index f227792..a8e3b7a 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php @@ -17,7 +17,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/node/node.api.php b/core/modules/node/node.api.php index 2004013..8b18a57 100644 --- a/core/modules/node/node.api.php +++ b/core/modules/node/node.api.php @@ -774,11 +774,6 @@ function hook_node_submit(\Drupal\node\NodeInterface $node, $form, &$form_state) * The structure of $node->content is a renderable array as expected by * drupal_render(). * - * When $view_mode is 'rss', modules can also add extra RSS elements and - * namespaces to $node->rss_elements and $node->rss_namespaces respectively for - * the RSS item generated for this node. - * For details on how this is used, see node_feed(). - * * @param \Drupal\node\NodeInterface $node * The node that is being assembled for rendering. * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 4092e76..e38fec9 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -1046,103 +1046,6 @@ function node_block_access(Block $block, $operation, AccountInterface $account, } /** - * 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(Language::TYPE_CONTENT); - $rss_config = \Drupal::config('system.rss'); - - if ($nids === FALSE) { - $nids = db_select('node_field_data', 'n') - ->distinct() - ->fields('n', array('nid')) - ->condition('n.promote', 1) - ->condition('n.status', 1) - ->orderBy('n.created', 'DESC') - ->range(0, $rss_config->get('items.limit')) - ->addTag('node_access') - ->execute() - ->fetchCol(); - } - - $item_length = $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 ($item_length != '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 @@ -1185,7 +1088,6 @@ function node_view_multiple($nodes, $view_mode = 'teaser', $langcode = NULL) { * 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, &$form_state, $form_id) { diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyAttributesTest.php index 1172fbc..d63101b 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyAttributesTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyAttributesTest.php @@ -19,7 +19,7 @@ class TaxonomyAttributesTest extends TaxonomyTestBase { * * @var array */ - public static $modules = array('rdf'); + public static $modules = array('rdf', 'views'); public static function getInfo() { return array( diff --git a/core/modules/taxonomy/config/install/entity.view_mode.taxonomy_term.full.yml b/core/modules/taxonomy/config/install/entity.view_mode.taxonomy_term.full.yml index 50b1854..bb8c47e 100644 --- a/core/modules/taxonomy/config/install/entity.view_mode.taxonomy_term.full.yml +++ b/core/modules/taxonomy/config/install/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 6477894..4a432d0 100644 --- a/core/modules/taxonomy/config/install/views.view.taxonomy_term.yml +++ b/core/modules/taxonomy/config/install/views.view.taxonomy_term.yml @@ -2,7 +2,7 @@ base_field: nid base_table: node core: '8' description: 'Content belonging to a certain taxonomy term.' -status: false +status: true display: default: id: default @@ -175,7 +175,21 @@ display: links: true 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: { } diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php index 0312a9d..94785cb 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php @@ -19,6 +19,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 @@ -36,15 +49,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 @@ -70,12 +74,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/lib/Drupal/taxonomy/Tests/RssTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/RssTest.php index 709d0fa..aa97163 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/RssTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/RssTest.php @@ -110,7 +110,7 @@ function testTaxonomyRss() { $this->assertRaw(format_xml_elements(array($test_element)), 'Term is displayed when viewing the rss feed.'); // Test that the feed page exists for the term. - $this->drupalGet("taxonomy/term/{$term1->id()}/feed"); + $this->drupalGet("taxonomy/term/{$term1->id()}/0/feed"); $this->assertRaw('install(array('views')); // Create two taxonomy terms and set term2 as the parent of term1. $term1 = $this->createTerm($this->vocabulary); $term2 = $this->createTerm($this->vocabulary); diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php index 04bbf6c..546b312 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php @@ -302,6 +302,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->randomName(12), 'description[0][value]' => $this->randomName(100), diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index 69bb530..6d9e128 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -16,6 +16,7 @@ use Drupal\taxonomy\Entity\Vocabulary; use Drupal\taxonomy\VocabularyInterface; use Drupal\Component\Utility\String; +use Symfony\Cmf\Component\Routing\RouteObjectInterface; /** * Denotes that no term in the vocabulary has a parent. @@ -66,8 +67,8 @@ function taxonomy_help($path, $arg) { $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 .= ''; @@ -134,6 +135,37 @@ function taxonomy_term_uri($term) { } /** + * Implements hook_page_build(). + */ +function taxonomy_page_build(&$page) { + $request = \Drupal::request(); + if ($request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'taxonomy.term_page') { + $term = $request->attributes->get('taxonomy_term'); + foreach ($term->uriRelationships() as $rel) { + // Set the term path as the canonical URL to prevent duplicate content. + $page['#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. + $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, 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 1182589..3f49299 100644 --- a/core/modules/taxonomy/taxonomy.routing.yml +++ b/core/modules/taxonomy/taxonomy.routing.yml @@ -91,16 +91,7 @@ taxonomy.overview_terms: taxonomy.term_page: 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/lib/Drupal/views/Plugin/views/display/PathPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php index bf4e008..b815be2 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php @@ -249,7 +249,7 @@ 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; } // Set the corrected path and the mapping to the route object. diff --git a/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php b/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php index 95f140e..549a913 100644 --- a/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php +++ b/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php @@ -214,7 +214,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(); @@ -222,7 +222,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(); @@ -250,6 +250,40 @@ public function testAlterRoutesWithParameters() { } /** + * Tests altering routes with optional parameter 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() {