Commit 983cdbf0 authored by catch's avatar catch

Issue #1857256 by dawehner, xjm, tim.plunkett, jibran, ParisLiakos,...

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.
parent 5e4b9a9a
......@@ -893,100 +893,6 @@ function node_get_recent($number = 10) {
return $nodes ? $nodes : array();
}
/**
* 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.
*
......@@ -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) {
......
......@@ -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};
......
......@@ -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.
......
......@@ -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();
......
id: taxonomy_term.full
label: 'Taxonomy term page'
status: false
status: true
cache: true
targetEntityType: taxonomy_term
dependencies:
......
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'
......
# 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
......
......@@ -17,6 +17,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.
*
......@@ -31,15 +44,6 @@ public function addForm(VocabularyInterface $taxonomy_vocabulary) {
return $this->entityFormBuilder()->getForm($term);
}
/**
* @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.
*
......@@ -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);
}
}
......@@ -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(
......
......@@ -17,6 +17,13 @@
*/
class TermIndexTest extends TaxonomyTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('views');
protected function setUp() {
parent::setUp();
......
......@@ -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),
......
<?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);
}
}
......@@ -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 .= '<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>';
......@@ -125,6 +126,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.
*
......@@ -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;
}
......
<?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'));