Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
|
@ -0,0 +1,8 @@
|
|||
id: taxonomy_term.full
|
||||
label: 'Taxonomy term page'
|
||||
status: true
|
||||
cache: true
|
||||
targetEntityType: taxonomy_term
|
||||
dependencies:
|
||||
module:
|
||||
- taxonomy
|
|
@ -0,0 +1,3 @@
|
|||
maintain_index_table: true
|
||||
override_selector: false
|
||||
terms_per_page_admin: 100
|
|
@ -0,0 +1,303 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- core.entity_view_mode.node.teaser
|
||||
module:
|
||||
- node
|
||||
- taxonomy
|
||||
- user
|
||||
id: taxonomy_term
|
||||
label: 'Taxonomy term'
|
||||
module: taxonomy
|
||||
description: 'Content belonging to a certain taxonomy term.'
|
||||
tag: default
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
core: '8'
|
||||
display:
|
||||
default:
|
||||
id: default
|
||||
display_title: Master
|
||||
display_plugin: default
|
||||
position: 0
|
||||
display_options:
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
query_comment: ''
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_tags: { }
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'access content'
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Apply
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: mini
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: 0
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 25, 50'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: ‹‹
|
||||
next: ››
|
||||
sorts:
|
||||
sticky:
|
||||
id: sticky
|
||||
table: taxonomy_index
|
||||
field: sticky
|
||||
order: DESC
|
||||
plugin_id: standard
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
created:
|
||||
id: created
|
||||
table: taxonomy_index
|
||||
field: created
|
||||
order: DESC
|
||||
plugin_id: date
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
granularity: second
|
||||
arguments:
|
||||
tid:
|
||||
id: tid
|
||||
table: taxonomy_index
|
||||
field: tid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
default_action: 'not found'
|
||||
exception:
|
||||
value: ''
|
||||
title_enable: false
|
||||
title: All
|
||||
title_enable: true
|
||||
title: '%1'
|
||||
default_argument_type: fixed
|
||||
default_argument_options:
|
||||
argument: ''
|
||||
default_argument_skip_url: false
|
||||
summary_options:
|
||||
base_path: ''
|
||||
count: true
|
||||
items_per_page: 25
|
||||
override: false
|
||||
summary:
|
||||
sort_order: asc
|
||||
number_of_records: 0
|
||||
format: default_summary
|
||||
specify_validation: true
|
||||
validate:
|
||||
type: 'entity:taxonomy_term'
|
||||
fail: 'not found'
|
||||
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
|
||||
filters:
|
||||
langcode:
|
||||
id: langcode
|
||||
table: node_field_data
|
||||
field: langcode
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: in
|
||||
value:
|
||||
'***LANGUAGE_language_content***': '***LANGUAGE_language_content***'
|
||||
group: 1
|
||||
exposed: false
|
||||
expose:
|
||||
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
|
||||
entity_type: node
|
||||
entity_field: langcode
|
||||
status:
|
||||
id: status
|
||||
table: taxonomy_index
|
||||
field: status
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: '='
|
||||
value: true
|
||||
group: 1
|
||||
exposed: false
|
||||
expose:
|
||||
operator_id: ''
|
||||
label: ''
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: ''
|
||||
identifier: ''
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
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: boolean
|
||||
style:
|
||||
type: default
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
uses_fields: false
|
||||
row:
|
||||
type: 'entity:node'
|
||||
options:
|
||||
view_mode: teaser
|
||||
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
|
||||
target: '!1'
|
||||
view_mode: full
|
||||
bypass_access: false
|
||||
plugin_id: entity
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships: { }
|
||||
fields: { }
|
||||
display_extenders: { }
|
||||
link_url: ''
|
||||
link_display: page_1
|
||||
cache_metadata:
|
||||
contexts:
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- 'user.node_grants:view'
|
||||
cacheable: false
|
||||
feed_1:
|
||||
id: feed_1
|
||||
display_title: Feed
|
||||
display_plugin: feed
|
||||
position: 2
|
||||
display_options:
|
||||
query:
|
||||
type: views_query
|
||||
options: { }
|
||||
pager:
|
||||
type: some
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
path: taxonomy/term/%/feed
|
||||
displays:
|
||||
page_1: page_1
|
||||
default: '0'
|
||||
style:
|
||||
type: rss
|
||||
options:
|
||||
description: ''
|
||||
grouping: { }
|
||||
uses_fields: false
|
||||
row:
|
||||
type: node_rss
|
||||
options:
|
||||
relationship: none
|
||||
view_mode: default
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
contexts:
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- 'user.node_grants:view'
|
||||
cacheable: false
|
||||
page_1:
|
||||
id: page_1
|
||||
display_title: Page
|
||||
display_plugin: page
|
||||
position: 1
|
||||
display_options:
|
||||
query:
|
||||
type: views_query
|
||||
options: { }
|
||||
path: taxonomy/term/%
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
contexts:
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- 'user.node_grants:view'
|
||||
cacheable: false
|
39
core/modules/taxonomy/config/schema/taxonomy.schema.yml
Normal file
39
core/modules/taxonomy/config/schema/taxonomy.schema.yml
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Schema for the configuration files of the Taxonomy module.
|
||||
|
||||
taxonomy.settings:
|
||||
type: config_object
|
||||
label: 'Taxonomy settings'
|
||||
mapping:
|
||||
maintain_index_table:
|
||||
type: boolean
|
||||
label: 'Maintain index table'
|
||||
override_selector:
|
||||
type: boolean
|
||||
label: 'Override selector'
|
||||
terms_per_page_admin:
|
||||
type: integer
|
||||
label: 'Number of terms per page'
|
||||
|
||||
taxonomy.vocabulary.*:
|
||||
type: config_entity
|
||||
label: 'Vocabulary'
|
||||
mapping:
|
||||
name:
|
||||
type: label
|
||||
label: 'Name'
|
||||
vid:
|
||||
type: string
|
||||
label: 'Machine name'
|
||||
description:
|
||||
type: label
|
||||
label: 'Description'
|
||||
hierarchy:
|
||||
type: integer
|
||||
label: 'Hierarchy'
|
||||
weight:
|
||||
type: integer
|
||||
label: 'Weight'
|
||||
|
||||
field.formatter.settings.entity_reference_rss_category:
|
||||
type: mapping
|
||||
label: 'Taxonomy format settings'
|
159
core/modules/taxonomy/config/schema/taxonomy.views.schema.yml
Normal file
159
core/modules/taxonomy/config/schema/taxonomy.views.schema.yml
Normal file
|
@ -0,0 +1,159 @@
|
|||
# Schema for the views plugins of the Taxonomy module.
|
||||
|
||||
views.argument.taxonomy_index_tid:
|
||||
type: views.argument.many_to_one
|
||||
label: 'Taxonomy term ID'
|
||||
|
||||
views.argument.taxonomy_index_tid_depth:
|
||||
type: views_argument
|
||||
label: 'Taxonomy term ID'
|
||||
mapping:
|
||||
depth:
|
||||
type: integer
|
||||
label: 'Depth'
|
||||
break_phrase:
|
||||
type: boolean
|
||||
label: 'Allow multiple values'
|
||||
use_taxonomy_term_path:
|
||||
type: boolean
|
||||
label: 'Use taxonomy term path'
|
||||
|
||||
views.argument.taxonomy_index_tid_depth_modifier:
|
||||
type: views_argument
|
||||
label: 'Taxonomy depth modifier'
|
||||
|
||||
views.argument.taxonomy:
|
||||
type: views_argument
|
||||
label: 'Taxonomy'
|
||||
mapping:
|
||||
break_phrase:
|
||||
type: boolean
|
||||
label: 'Allow multiple values'
|
||||
not:
|
||||
type: boolean
|
||||
label: 'Exclude'
|
||||
|
||||
views.argument.vocabulary_vid:
|
||||
type: views_argument
|
||||
label: 'Vocabulary'
|
||||
mapping:
|
||||
break_phrase:
|
||||
type: boolean
|
||||
label: 'Allow multiple values'
|
||||
not:
|
||||
type: boolean
|
||||
label: 'Exclude'
|
||||
|
||||
|
||||
views.argument_validator.entity:taxonomy_term:
|
||||
type: views.argument_validator_entity
|
||||
label: 'Taxonomy term'
|
||||
|
||||
views.argument_validator.taxonomy_term_name:
|
||||
type: views.argument_validator_entity
|
||||
label: 'Taxonomy term'
|
||||
mapping:
|
||||
vids:
|
||||
type: sequence
|
||||
label: 'Vocabularies'
|
||||
sequence:
|
||||
type: string
|
||||
label: 'Vocabulary'
|
||||
|
||||
views.argument_default.taxonomy_tid:
|
||||
type: mapping
|
||||
label: 'Taxonomy term ID from URL'
|
||||
mapping:
|
||||
term_page:
|
||||
type: string
|
||||
label: 'Load default filter from term page'
|
||||
node:
|
||||
type: boolean
|
||||
label: 'Load default filter from node page, that''s good for related taxonomy blocks'
|
||||
limit:
|
||||
type: boolean
|
||||
label: 'Limit terms by vocabulary'
|
||||
vids:
|
||||
type: sequence
|
||||
label: 'Vocabularies'
|
||||
sequence:
|
||||
type: string
|
||||
label: 'Vocabulary'
|
||||
anyall:
|
||||
type: string
|
||||
label: 'Multiple-value handling'
|
||||
|
||||
views.field.term_name:
|
||||
type: views.field.field
|
||||
mapping:
|
||||
convert_spaces:
|
||||
type: boolean
|
||||
label: 'Convert spaces in term names to hyphens'
|
||||
|
||||
views.field.taxonomy_index_tid:
|
||||
type: views_field
|
||||
label: 'Taxonomy language'
|
||||
mapping:
|
||||
type:
|
||||
type: string
|
||||
label: 'Display type'
|
||||
separator:
|
||||
type: string
|
||||
label: 'Separator'
|
||||
link_to_taxonomy:
|
||||
type: boolean
|
||||
label: 'Link this field to its term page'
|
||||
limit:
|
||||
type: boolean
|
||||
label: 'Limit terms by vocabulary'
|
||||
vids:
|
||||
type: sequence
|
||||
label: 'Vocabularies'
|
||||
sequence:
|
||||
type: string
|
||||
label: 'Vocabulary'
|
||||
|
||||
views.filter.taxonomy_index_tid:
|
||||
type: views.filter.many_to_one
|
||||
label: 'Taxonomy term ID'
|
||||
mapping:
|
||||
vid:
|
||||
type: string
|
||||
label: 'Vocabulary'
|
||||
type:
|
||||
type: string
|
||||
label: 'Selection type'
|
||||
hierarchy:
|
||||
type: boolean
|
||||
label: 'Show hierarchy in dropdown'
|
||||
limit:
|
||||
type: boolean
|
||||
label: 'Limit to vocabulary'
|
||||
error_message:
|
||||
type: boolean
|
||||
label: 'Display error message'
|
||||
value:
|
||||
type: sequence
|
||||
label: 'Values'
|
||||
sequence:
|
||||
type: integer
|
||||
label: 'Value'
|
||||
|
||||
views.filter.taxonomy_index_tid_depth:
|
||||
type: views.filter.taxonomy_index_tid
|
||||
label: 'Taxonomy term ID with depth'
|
||||
mapping:
|
||||
depth:
|
||||
type: integer
|
||||
label: 'Depth'
|
||||
|
||||
views.relationship.node_term_data:
|
||||
type: views_relationship
|
||||
label: 'Taxonomy term'
|
||||
mapping:
|
||||
vids:
|
||||
type: sequence
|
||||
label: 'Vocabularies'
|
||||
sequence:
|
||||
type: string
|
||||
label: 'Vocabulary'
|
10
core/modules/taxonomy/css/taxonomy.theme.css
Normal file
10
core/modules/taxonomy/css/taxonomy.theme.css
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
.taxonomy-term-preview {
|
||||
background-color: #eee;
|
||||
}
|
||||
.taxonomy-term-divider-top {
|
||||
border-bottom: none;
|
||||
}
|
||||
.taxonomy-term-divider-bottom {
|
||||
border-top: 1px dotted #ccc;
|
||||
}
|
73
core/modules/taxonomy/src/Controller/TaxonomyController.php
Normal file
73
core/modules/taxonomy/src/Controller/TaxonomyController.php
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Controller\TaxonomyController.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Controller;
|
||||
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\taxonomy\TermInterface;
|
||||
use Drupal\taxonomy\VocabularyInterface;
|
||||
|
||||
/**
|
||||
* Provides route responses for taxonomy.module.
|
||||
*/
|
||||
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
|
||||
* The vocabulary this term will be added to.
|
||||
*
|
||||
* @return array
|
||||
* The taxonomy term add form.
|
||||
*/
|
||||
public function addForm(VocabularyInterface $taxonomy_vocabulary) {
|
||||
$term = $this->entityManager()->getStorage('taxonomy_term')->create(array('vid' => $taxonomy_vocabulary->id()));
|
||||
return $this->entityFormBuilder()->getForm($term);
|
||||
}
|
||||
|
||||
/**
|
||||
* Route title callback.
|
||||
*
|
||||
* @param \Drupal\taxonomy\VocabularyInterface $taxonomy_vocabulary
|
||||
* The taxonomy term.
|
||||
*
|
||||
* @return string
|
||||
* The term label.
|
||||
*/
|
||||
public function vocabularyTitle(VocabularyInterface $taxonomy_vocabulary) {
|
||||
return Xss::filter($taxonomy_vocabulary->label());
|
||||
}
|
||||
|
||||
/**
|
||||
* Route title callback.
|
||||
*
|
||||
* @param \Drupal\taxonomy\TermInterface $taxonomy_term
|
||||
* The taxonomy term.
|
||||
*
|
||||
* @return string
|
||||
* The term label.
|
||||
*/
|
||||
public function termTitle(TermInterface $taxonomy_term) {
|
||||
return Xss::filter($taxonomy_term->getName());
|
||||
}
|
||||
|
||||
}
|
264
core/modules/taxonomy/src/Entity/Term.php
Normal file
264
core/modules/taxonomy/src/Entity/Term.php
Normal file
|
@ -0,0 +1,264 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Entity\Term.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Entity;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityBase;
|
||||
use Drupal\Core\Entity\EntityChangedTrait;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\taxonomy\TermInterface;
|
||||
|
||||
/**
|
||||
* Defines the taxonomy term entity.
|
||||
*
|
||||
* @ContentEntityType(
|
||||
* id = "taxonomy_term",
|
||||
* label = @Translation("Taxonomy term"),
|
||||
* bundle_label = @Translation("Vocabulary"),
|
||||
* handlers = {
|
||||
* "storage" = "Drupal\taxonomy\TermStorage",
|
||||
* "storage_schema" = "Drupal\taxonomy\TermStorageSchema",
|
||||
* "view_builder" = "Drupal\taxonomy\TermViewBuilder",
|
||||
* "access" = "Drupal\taxonomy\TermAccessControlHandler",
|
||||
* "views_data" = "Drupal\taxonomy\TermViewsData",
|
||||
* "form" = {
|
||||
* "default" = "Drupal\taxonomy\TermForm",
|
||||
* "delete" = "Drupal\taxonomy\Form\TermDeleteForm"
|
||||
* },
|
||||
* "translation" = "Drupal\taxonomy\TermTranslationHandler"
|
||||
* },
|
||||
* base_table = "taxonomy_term_data",
|
||||
* data_table = "taxonomy_term_field_data",
|
||||
* uri_callback = "taxonomy_term_uri",
|
||||
* translatable = TRUE,
|
||||
* entity_keys = {
|
||||
* "id" = "tid",
|
||||
* "bundle" = "vid",
|
||||
* "label" = "name",
|
||||
* "langcode" = "langcode",
|
||||
* "uuid" = "uuid"
|
||||
* },
|
||||
* bundle_entity_type = "taxonomy_vocabulary",
|
||||
* field_ui_base_route = "entity.taxonomy_vocabulary.overview_form",
|
||||
* common_reference_target = TRUE,
|
||||
* links = {
|
||||
* "canonical" = "/taxonomy/term/{taxonomy_term}",
|
||||
* "delete-form" = "/taxonomy/term/{taxonomy_term}/delete",
|
||||
* "edit-form" = "/taxonomy/term/{taxonomy_term}/edit",
|
||||
* },
|
||||
* permission_granularity = "bundle"
|
||||
* )
|
||||
*/
|
||||
class Term extends ContentEntityBase implements TermInterface {
|
||||
|
||||
use EntityChangedTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageInterface $storage, array $entities) {
|
||||
parent::postDelete($storage, $entities);
|
||||
|
||||
// See if any of the term's children are about to be become orphans.
|
||||
$orphans = array();
|
||||
foreach (array_keys($entities) as $tid) {
|
||||
if ($children = $storage->loadChildren($tid)) {
|
||||
foreach ($children as $child) {
|
||||
// If the term has multiple parents, we don't delete it.
|
||||
$parents = $storage->loadParents($child->id());
|
||||
if (empty($parents)) {
|
||||
$orphans[] = $child->id();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete term hierarchy information after looking up orphans but before
|
||||
// deleting them so that their children/parent information is consistent.
|
||||
$storage->deleteTermHierarchy(array_keys($entities));
|
||||
|
||||
if (!empty($orphans)) {
|
||||
entity_delete_multiple('taxonomy_term', $orphans);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
|
||||
parent::postSave($storage, $update);
|
||||
|
||||
// Only change the parents if a value is set, keep the existing values if
|
||||
// not.
|
||||
if (isset($this->parent->target_id)) {
|
||||
$storage->deleteTermHierarchy(array($this->id()));
|
||||
$storage->updateTermHierarchy($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||
$fields['tid'] = BaseFieldDefinition::create('integer')
|
||||
->setLabel(t('Term ID'))
|
||||
->setDescription(t('The term ID.'))
|
||||
->setReadOnly(TRUE)
|
||||
->setSetting('unsigned', TRUE);
|
||||
|
||||
$fields['uuid'] = BaseFieldDefinition::create('uuid')
|
||||
->setLabel(t('UUID'))
|
||||
->setDescription(t('The term UUID.'))
|
||||
->setReadOnly(TRUE);
|
||||
|
||||
$fields['vid'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('Vocabulary'))
|
||||
->setDescription(t('The vocabulary to which the term is assigned.'))
|
||||
->setSetting('target_type', 'taxonomy_vocabulary');
|
||||
|
||||
$fields['langcode'] = BaseFieldDefinition::create('language')
|
||||
->setLabel(t('Language'))
|
||||
->setDescription(t('The term language code.'))
|
||||
->setTranslatable(TRUE)
|
||||
->setDisplayOptions('view', array(
|
||||
'type' => 'hidden',
|
||||
))
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'language_select',
|
||||
'weight' => 2,
|
||||
));
|
||||
|
||||
$fields['name'] = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('Name'))
|
||||
->setDescription(t('The term name.'))
|
||||
->setTranslatable(TRUE)
|
||||
->setRequired(TRUE)
|
||||
->setSetting('max_length', 255)
|
||||
->setDisplayOptions('view', array(
|
||||
'label' => 'hidden',
|
||||
'type' => 'string',
|
||||
'weight' => -5,
|
||||
))
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'string_textfield',
|
||||
'weight' => -5,
|
||||
))
|
||||
->setDisplayConfigurable('form', TRUE);
|
||||
|
||||
$fields['description'] = BaseFieldDefinition::create('text_long')
|
||||
->setLabel(t('Description'))
|
||||
->setDescription(t('A description of the term.'))
|
||||
->setTranslatable(TRUE)
|
||||
->setDisplayOptions('view', array(
|
||||
'label' => 'hidden',
|
||||
'type' => 'text_default',
|
||||
'weight' => 0,
|
||||
))
|
||||
->setDisplayConfigurable('view', TRUE)
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'text_textfield',
|
||||
'weight' => 0,
|
||||
))
|
||||
->setDisplayConfigurable('form', TRUE);
|
||||
|
||||
$fields['weight'] = BaseFieldDefinition::create('integer')
|
||||
->setLabel(t('Weight'))
|
||||
->setDescription(t('The weight of this term in relation to other terms.'))
|
||||
->setDefaultValue(0);
|
||||
|
||||
$fields['parent'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('Term Parents'))
|
||||
->setDescription(t('The parents of this term.'))
|
||||
->setSetting('target_type', 'taxonomy_term')
|
||||
->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED)
|
||||
->setCustomStorage(TRUE);
|
||||
|
||||
$fields['changed'] = BaseFieldDefinition::create('changed')
|
||||
->setLabel(t('Changed'))
|
||||
->setDescription(t('The time that the term was last edited.'))
|
||||
->setTranslatable(TRUE);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getChangedTime() {
|
||||
return $this->get('changed')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->get('description')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDescription($description) {
|
||||
$this->set('description', $description);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormat() {
|
||||
return $this->get('description')->format;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setFormat($format) {
|
||||
$this->get('description')->format = $format;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return $this->label();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setName($name) {
|
||||
$this->set('name', $name);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWeight() {
|
||||
return $this->get('weight')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setWeight($weight) {
|
||||
$this->set('weight', $weight);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getVocabularyId() {
|
||||
return $this->get('vid')->target_id;
|
||||
}
|
||||
|
||||
}
|
222
core/modules/taxonomy/src/Entity/Vocabulary.php
Normal file
222
core/modules/taxonomy/src/Entity/Vocabulary.php
Normal file
|
@ -0,0 +1,222 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Entity\Vocabulary.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\taxonomy\VocabularyInterface;
|
||||
|
||||
/**
|
||||
* Defines the taxonomy vocabulary entity.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "taxonomy_vocabulary",
|
||||
* label = @Translation("Taxonomy vocabulary"),
|
||||
* handlers = {
|
||||
* "storage" = "Drupal\taxonomy\VocabularyStorage",
|
||||
* "list_builder" = "Drupal\taxonomy\VocabularyListBuilder",
|
||||
* "form" = {
|
||||
* "default" = "Drupal\taxonomy\VocabularyForm",
|
||||
* "reset" = "Drupal\taxonomy\Form\VocabularyResetForm",
|
||||
* "delete" = "Drupal\taxonomy\Form\VocabularyDeleteForm"
|
||||
* }
|
||||
* },
|
||||
* admin_permission = "administer taxonomy",
|
||||
* config_prefix = "vocabulary",
|
||||
* bundle_of = "taxonomy_term",
|
||||
* entity_keys = {
|
||||
* "id" = "vid",
|
||||
* "label" = "name",
|
||||
* "weight" = "weight"
|
||||
* },
|
||||
* links = {
|
||||
* "add-form" = "/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/add",
|
||||
* "delete-form" = "/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/delete",
|
||||
* "reset-form" = "/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/reset",
|
||||
* "overview-form" = "/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/overview",
|
||||
* "edit-form" = "/admin/structure/taxonomy/manage/{taxonomy_vocabulary}",
|
||||
* "collection" = "/admin/structure/taxonomy",
|
||||
* },
|
||||
* config_export = {
|
||||
* "name",
|
||||
* "vid",
|
||||
* "description",
|
||||
* "hierarchy",
|
||||
* "weight",
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class Vocabulary extends ConfigEntityBundleBase implements VocabularyInterface {
|
||||
|
||||
/**
|
||||
* The taxonomy vocabulary ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $vid;
|
||||
|
||||
/**
|
||||
* Name of the vocabulary.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* Description of the vocabulary.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
/**
|
||||
* The type of hierarchy allowed within the vocabulary.
|
||||
*
|
||||
* Possible values:
|
||||
* - TAXONOMY_HIERARCHY_DISABLED: No parents.
|
||||
* - TAXONOMY_HIERARCHY_SINGLE: Single parent.
|
||||
* - TAXONOMY_HIERARCHY_MULTIPLE: Multiple parents.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $hierarchy = TAXONOMY_HIERARCHY_DISABLED;
|
||||
|
||||
/**
|
||||
* The weight of this vocabulary in relation to other vocabularies.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $weight = 0;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHierarchy() {
|
||||
return $this->hierarchy;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setHierarchy($hierarchy) {
|
||||
$this->hierarchy = $hierarchy;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function id() {
|
||||
return $this->vid;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
|
||||
parent::postSave($storage, $update);
|
||||
|
||||
if ($update && $this->getOriginalId() != $this->id() && !$this->isSyncing()) {
|
||||
// Reflect machine name changes in the definitions of existing 'taxonomy'
|
||||
// fields.
|
||||
$field_ids = array();
|
||||
$field_map = \Drupal::entityManager()->getFieldMapByFieldType('entity_reference');
|
||||
foreach ($field_map as $entity_type => $field_storages) {
|
||||
foreach ($field_storages as $field_storage => $info) {
|
||||
$field_ids[] = $entity_type . '.' . $field_storage;
|
||||
}
|
||||
}
|
||||
|
||||
$field_storages = \Drupal::entityManager()->getStorage('field_storage_config')->loadMultiple($field_ids);
|
||||
$taxonomy_fields = array_filter($field_storages, function ($field_storage) {
|
||||
return $field_storage->getType() == 'entity_reference' && $field_storage->getSetting('target_type') == 'taxonomy_term';
|
||||
});
|
||||
|
||||
foreach ($taxonomy_fields as $field_storage) {
|
||||
$update_storage = FALSE;
|
||||
|
||||
$allowed_values = $field_storage->getSetting('allowed_values');
|
||||
foreach ($allowed_values as &$value) {
|
||||
if ($value['vocabulary'] == $this->getOriginalId()) {
|
||||
$value['vocabulary'] = $this->id();
|
||||
$update_storage = TRUE;
|
||||
}
|
||||
}
|
||||
$field_storage->setSetting('allowed_values', $allowed_values);
|
||||
|
||||
if ($update_storage) {
|
||||
$field_storage->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
$storage->resetCache($update ? array($this->getOriginalId()) : array());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preDelete(EntityStorageInterface $storage, array $entities) {
|
||||
parent::preDelete($storage, $entities);
|
||||
|
||||
// Only load terms without a parent, child terms will get deleted too.
|
||||
entity_delete_multiple('taxonomy_term', $storage->getToplevelTids(array_keys($entities)));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageInterface $storage, array $entities) {
|
||||
parent::postDelete($storage, $entities);
|
||||
|
||||
// Reset caches.
|
||||
$storage->resetCache(array_keys($entities));
|
||||
|
||||
if (reset($entities)->isSyncing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$vocabularies = array();
|
||||
foreach ($entities as $vocabulary) {
|
||||
$vocabularies[$vocabulary->id()] = $vocabulary->id();
|
||||
}
|
||||
// Load all Taxonomy module fields and delete those which use only this
|
||||
// vocabulary.
|
||||
$field_storages = entity_load_multiple_by_properties('field_storage_config', array('module' => 'taxonomy'));
|
||||
foreach ($field_storages as $field_storage) {
|
||||
$modified_storage = FALSE;
|
||||
// Term reference fields may reference terms from more than one
|
||||
// vocabulary.
|
||||
foreach ($field_storage->getSetting('allowed_values') as $key => $allowed_value) {
|
||||
if (isset($vocabularies[$allowed_value['vocabulary']])) {
|
||||
$allowed_values = $field_storage->getSetting('allowed_values');
|
||||
unset($allowed_values[$key]);
|
||||
$field_storage->setSetting('allowed_values', $allowed_values);
|
||||
$modified_storage = TRUE;
|
||||
}
|
||||
}
|
||||
if ($modified_storage) {
|
||||
$allowed_values = $field_storage->getSetting('allowed_values');
|
||||
if (empty($allowed_values)) {
|
||||
$field_storage->delete();
|
||||
}
|
||||
else {
|
||||
// Update the field definition with the new allowed values.
|
||||
$field_storage->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
473
core/modules/taxonomy/src/Form/OverviewTerms.php
Normal file
473
core/modules/taxonomy/src/Form/OverviewTerms.php
Normal file
|
@ -0,0 +1,473 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Form\OverviewTerms.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\taxonomy\VocabularyInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/*
|
||||
* Provides terms overview form for a taxonomy vocabulary.
|
||||
*/
|
||||
class OverviewTerms extends FormBase {
|
||||
|
||||
/**
|
||||
* The module handler service.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The term storage controller.
|
||||
*
|
||||
* @var \Drupal\taxonomy\TermStorageInterface
|
||||
*/
|
||||
protected $storageController;
|
||||
|
||||
/**
|
||||
* Constructs an OverviewTerms object.
|
||||
*
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler service.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager service.
|
||||
*/
|
||||
public function __construct(ModuleHandlerInterface $module_handler, EntityManagerInterface $entity_manager) {
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->storageController = $entity_manager->getStorage('taxonomy_term');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('module_handler'),
|
||||
$container->get('entity.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'taxonomy_overview_terms';
|
||||
}
|
||||
|
||||
/**
|
||||
* Form constructor.
|
||||
*
|
||||
* Display a tree of all the terms in a vocabulary, with options to edit
|
||||
* each one. The form is made drag and drop by the theme function.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
* @param \Drupal\taxonomy\VocabularyInterface $taxonomy_vocabulary
|
||||
* The vocabulary to display the overview form for.
|
||||
*
|
||||
* @return array
|
||||
* The form structure.
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, VocabularyInterface $taxonomy_vocabulary = NULL) {
|
||||
// @todo Remove global variables when https://www.drupal.org/node/2044435 is
|
||||
// in.
|
||||
global $pager_page_array, $pager_total, $pager_total_items;
|
||||
|
||||
$form_state->set(['taxonomy', 'vocabulary'], $taxonomy_vocabulary);
|
||||
$parent_fields = FALSE;
|
||||
|
||||
$page = $this->getRequest()->query->get('page') ?: 0;
|
||||
// Number of terms per page.
|
||||
$page_increment = $this->config('taxonomy.settings')->get('terms_per_page_admin');
|
||||
// Elements shown on this page.
|
||||
$page_entries = 0;
|
||||
// Elements at the root level before this page.
|
||||
$before_entries = 0;
|
||||
// Elements at the root level after this page.
|
||||
$after_entries = 0;
|
||||
// Elements at the root level on this page.
|
||||
$root_entries = 0;
|
||||
|
||||
// Terms from previous and next pages are shown if the term tree would have
|
||||
// been cut in the middle. Keep track of how many extra terms we show on
|
||||
// each page of terms.
|
||||
$back_step = NULL;
|
||||
$forward_step = 0;
|
||||
|
||||
// An array of the terms to be displayed on this page.
|
||||
$current_page = array();
|
||||
|
||||
$delta = 0;
|
||||
$term_deltas = array();
|
||||
$tree = $this->storageController->loadTree($taxonomy_vocabulary->id(), 0, NULL, TRUE);
|
||||
$tree_index = 0;
|
||||
do {
|
||||
// In case this tree is completely empty.
|
||||
if (empty($tree[$tree_index])) {
|
||||
break;
|
||||
}
|
||||
$delta++;
|
||||
// Count entries before the current page.
|
||||
if ($page && ($page * $page_increment) > $before_entries && !isset($back_step)) {
|
||||
$before_entries++;
|
||||
continue;
|
||||
}
|
||||
// Count entries after the current page.
|
||||
elseif ($page_entries > $page_increment && isset($complete_tree)) {
|
||||
$after_entries++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do not let a term start the page that is not at the root.
|
||||
$term = $tree[$tree_index];
|
||||
if (isset($term->depth) && ($term->depth > 0) && !isset($back_step)) {
|
||||
$back_step = 0;
|
||||
while ($pterm = $tree[--$tree_index]) {
|
||||
$before_entries--;
|
||||
$back_step++;
|
||||
if ($pterm->depth == 0) {
|
||||
$tree_index--;
|
||||
// Jump back to the start of the root level parent.
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
$back_step = isset($back_step) ? $back_step : 0;
|
||||
|
||||
// Continue rendering the tree until we reach the a new root item.
|
||||
if ($page_entries >= $page_increment + $back_step + 1 && $term->depth == 0 && $root_entries > 1) {
|
||||
$complete_tree = TRUE;
|
||||
// This new item at the root level is the first item on the next page.
|
||||
$after_entries++;
|
||||
continue;
|
||||
}
|
||||
if ($page_entries >= $page_increment + $back_step) {
|
||||
$forward_step++;
|
||||
}
|
||||
|
||||
// Finally, if we've gotten down this far, we're rendering a term on this
|
||||
// page.
|
||||
$page_entries++;
|
||||
$term_deltas[$term->id()] = isset($term_deltas[$term->id()]) ? $term_deltas[$term->id()] + 1 : 0;
|
||||
$key = 'tid:' . $term->id() . ':' . $term_deltas[$term->id()];
|
||||
|
||||
// Keep track of the first term displayed on this page.
|
||||
if ($page_entries == 1) {
|
||||
$form['#first_tid'] = $term->id();
|
||||
}
|
||||
// Keep a variable to make sure at least 2 root elements are displayed.
|
||||
if ($term->parents[0] == 0) {
|
||||
$root_entries++;
|
||||
}
|
||||
$current_page[$key] = $term;
|
||||
} while (isset($tree[++$tree_index]));
|
||||
|
||||
// Because we didn't use a pager query, set the necessary pager variables.
|
||||
$total_entries = $before_entries + $page_entries + $after_entries;
|
||||
$pager_total_items[0] = $total_entries;
|
||||
$pager_page_array[0] = $page;
|
||||
$pager_total[0] = ceil($total_entries / $page_increment);
|
||||
|
||||
// If this form was already submitted once, it's probably hit a validation
|
||||
// error. Ensure the form is rebuilt in the same order as the user
|
||||
// submitted.
|
||||
$user_input = $form_state->getUserInput();
|
||||
if (!empty($user_input)) {
|
||||
// Get the POST order.
|
||||
$order = array_flip(array_keys($user_input['terms']));
|
||||
// Update our form with the new order.
|
||||
$current_page = array_merge($order, $current_page);
|
||||
foreach ($current_page as $key => $term) {
|
||||
// Verify this is a term for the current page and set at the current
|
||||
// depth.
|
||||
if (is_array($user_input['terms'][$key]) && is_numeric($user_input['terms'][$key]['term']['tid'])) {
|
||||
$current_page[$key]->depth = $user_input['terms'][$key]['term']['depth'];
|
||||
}
|
||||
else {
|
||||
unset($current_page[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$errors = $form_state->getErrors();
|
||||
$destination = $this->getDestinationArray();
|
||||
$row_position = 0;
|
||||
// Build the actual form.
|
||||
$form['terms'] = array(
|
||||
'#type' => 'table',
|
||||
'#header' => array($this->t('Name'), $this->t('Weight'), $this->t('Operations')),
|
||||
'#empty' => $this->t('No terms available. <a href="@link">Add term</a>.', array('@link' => $this->url('entity.taxonomy_term.add_form', array('taxonomy_vocabulary' => $taxonomy_vocabulary->id())))),
|
||||
'#attributes' => array(
|
||||
'id' => 'taxonomy',
|
||||
),
|
||||
);
|
||||
foreach ($current_page as $key => $term) {
|
||||
/** @var $term \Drupal\Core\Entity\EntityInterface */
|
||||
$form['terms'][$key]['#term'] = $term;
|
||||
$indentation = array();
|
||||
if (isset($term->depth) && $term->depth > 0) {
|
||||
$indentation = array(
|
||||
'#theme' => 'indentation',
|
||||
'#size' => $term->depth,
|
||||
);
|
||||
}
|
||||
$form['terms'][$key]['term'] = array(
|
||||
'#prefix' => !empty($indentation) ? drupal_render($indentation) : '',
|
||||
'#type' => 'link',
|
||||
'#title' => $term->getName(),
|
||||
'#url' => $term->urlInfo(),
|
||||
);
|
||||
if ($taxonomy_vocabulary->getHierarchy() != TAXONOMY_HIERARCHY_MULTIPLE && count($tree) > 1) {
|
||||
$parent_fields = TRUE;
|
||||
$form['terms'][$key]['term']['tid'] = array(
|
||||
'#type' => 'hidden',
|
||||
'#value' => $term->id(),
|
||||
'#attributes' => array(
|
||||
'class' => array('term-id'),
|
||||
),
|
||||
);
|
||||
$form['terms'][$key]['term']['parent'] = array(
|
||||
'#type' => 'hidden',
|
||||
// Yes, default_value on a hidden. It needs to be changeable by the
|
||||
// javascript.
|
||||
'#default_value' => $term->parents[0],
|
||||
'#attributes' => array(
|
||||
'class' => array('term-parent'),
|
||||
),
|
||||
);
|
||||
$form['terms'][$key]['term']['depth'] = array(
|
||||
'#type' => 'hidden',
|
||||
// Same as above, the depth is modified by javascript, so it's a
|
||||
// default_value.
|
||||
'#default_value' => $term->depth,
|
||||
'#attributes' => array(
|
||||
'class' => array('term-depth'),
|
||||
),
|
||||
);
|
||||
}
|
||||
$form['terms'][$key]['weight'] = array(
|
||||
'#type' => 'weight',
|
||||
'#delta' => $delta,
|
||||
'#title' => $this->t('Weight for added term'),
|
||||
'#title_display' => 'invisible',
|
||||
'#default_value' => $term->getWeight(),
|
||||
'#attributes' => array(
|
||||
'class' => array('term-weight'),
|
||||
),
|
||||
);
|
||||
$operations = array(
|
||||
'edit' => array(
|
||||
'title' => $this->t('Edit'),
|
||||
'query' => $destination,
|
||||
'url' => $term->urlInfo('edit-form'),
|
||||
),
|
||||
'delete' => array(
|
||||
'title' => $this->t('Delete'),
|
||||
'query' => $destination,
|
||||
'url' => $term->urlInfo('delete-form'),
|
||||
),
|
||||
);
|
||||
if ($this->moduleHandler->moduleExists('content_translation') && content_translation_translate_access($term)->isAllowed()) {
|
||||
$operations['translate'] = array(
|
||||
'title' => $this->t('Translate'),
|
||||
'query' => $destination,
|
||||
'url' => $term->urlInfo('drupal:content-translation-overview'),
|
||||
);
|
||||
}
|
||||
$form['terms'][$key]['operations'] = array(
|
||||
'#type' => 'operations',
|
||||
'#links' => $operations,
|
||||
);
|
||||
|
||||
$form['terms'][$key]['#attributes']['class'] = array();
|
||||
if ($parent_fields) {
|
||||
$form['terms'][$key]['#attributes']['class'][] = 'draggable';
|
||||
}
|
||||
|
||||
// Add classes that mark which terms belong to previous and next pages.
|
||||
if ($row_position < $back_step || $row_position >= $page_entries - $forward_step) {
|
||||
$form['terms'][$key]['#attributes']['class'][] = 'taxonomy-term-preview';
|
||||
}
|
||||
|
||||
if ($row_position !== 0 && $row_position !== count($tree) - 1) {
|
||||
if ($row_position == $back_step - 1 || $row_position == $page_entries - $forward_step - 1) {
|
||||
$form['terms'][$key]['#attributes']['class'][] = 'taxonomy-term-divider-top';
|
||||
}
|
||||
elseif ($row_position == $back_step || $row_position == $page_entries - $forward_step) {
|
||||
$form['terms'][$key]['#attributes']['class'][] = 'taxonomy-term-divider-bottom';
|
||||
}
|
||||
}
|
||||
|
||||
// Add an error class if this row contains a form error.
|
||||
foreach ($errors as $error_key => $error) {
|
||||
if (strpos($error_key, $key) === 0) {
|
||||
$form['terms'][$key]['#attributes']['class'][] = 'error';
|
||||
}
|
||||
}
|
||||
$row_position++;
|
||||
}
|
||||
|
||||
if ($parent_fields) {
|
||||
$form['terms']['#tabledrag'][] = array(
|
||||
'action' => 'match',
|
||||
'relationship' => 'parent',
|
||||
'group' => 'term-parent',
|
||||
'subgroup' => 'term-parent',
|
||||
'source' => 'term-id',
|
||||
'hidden' => FALSE,
|
||||
);
|
||||
$form['terms']['#tabledrag'][] = array(
|
||||
'action' => 'depth',
|
||||
'relationship' => 'group',
|
||||
'group' => 'term-depth',
|
||||
'hidden' => FALSE,
|
||||
);
|
||||
$form['terms']['#attached']['library'][] = 'taxonomy/drupal.taxonomy';
|
||||
$form['terms']['#attached']['drupalSettings']['taxonomy'] = [
|
||||
'backStep' => $back_step,
|
||||
'forwardStep' => $forward_step,
|
||||
];
|
||||
}
|
||||
$form['terms']['#tabledrag'][] = array(
|
||||
'action' => 'order',
|
||||
'relationship' => 'sibling',
|
||||
'group' => 'term-weight',
|
||||
);
|
||||
|
||||
if ($taxonomy_vocabulary->getHierarchy() != TAXONOMY_HIERARCHY_MULTIPLE && count($tree) > 1) {
|
||||
$form['actions'] = array('#type' => 'actions', '#tree' => FALSE);
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save'),
|
||||
'#button_type' => 'primary',
|
||||
);
|
||||
$form['actions']['reset_alphabetical'] = array(
|
||||
'#type' => 'submit',
|
||||
'#submit' => array('::submitReset'),
|
||||
'#value' => $this->t('Reset to alphabetical'),
|
||||
);
|
||||
}
|
||||
|
||||
$form['pager_pager'] = ['#type' => 'pager'];
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler.
|
||||
*
|
||||
* Rather than using a textfield or weight field, this form depends entirely
|
||||
* upon the order of form elements on the page to determine new weights.
|
||||
*
|
||||
* Because there might be hundreds or thousands of taxonomy terms that need to
|
||||
* be ordered, terms are weighted from 0 to the number of terms in the
|
||||
* vocabulary, rather than the standard -10 to 10 scale. Numbers are sorted
|
||||
* lowest to highest, but are not necessarily sequential. Numbers may be
|
||||
* skipped when a term has children so that reordering is minimal when a child
|
||||
* is added or removed from a term.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
// Sort term order based on weight.
|
||||
uasort($form_state->getValue('terms'), array('Drupal\Component\Utility\SortArray', 'sortByWeightElement'));
|
||||
|
||||
$vocabulary = $form_state->get(['taxonomy', 'vocabulary']);
|
||||
// Update the current hierarchy type as we go.
|
||||
$hierarchy = TAXONOMY_HIERARCHY_DISABLED;
|
||||
|
||||
$changed_terms = array();
|
||||
$tree = $this->storageController->loadTree($vocabulary->id(), 0, NULL, TRUE);
|
||||
|
||||
if (empty($tree)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build a list of all terms that need to be updated on previous pages.
|
||||
$weight = 0;
|
||||
$term = $tree[0];
|
||||
while ($term->id() != $form['#first_tid']) {
|
||||
if ($term->parents[0] == 0 && $term->getWeight() != $weight) {
|
||||
$term->setWeight($weight);
|
||||
$changed_terms[$term->id()] = $term;
|
||||
}
|
||||
$weight++;
|
||||
$hierarchy = $term->parents[0] != 0 ? TAXONOMY_HIERARCHY_SINGLE : $hierarchy;
|
||||
$term = $tree[$weight];
|
||||
}
|
||||
|
||||
// Renumber the current page weights and assign any new parents.
|
||||
$level_weights = array();
|
||||
foreach ($form_state->getValue('terms') as $tid => $values) {
|
||||
if (isset($form['terms'][$tid]['#term'])) {
|
||||
$term = $form['terms'][$tid]['#term'];
|
||||
// Give terms at the root level a weight in sequence with terms on previous pages.
|
||||
if ($values['term']['parent'] == 0 && $term->getWeight() != $weight) {
|
||||
$term->setWeight($weight);
|
||||
$changed_terms[$term->id()] = $term;
|
||||
}
|
||||
// Terms not at the root level can safely start from 0 because they're all on this page.
|
||||
elseif ($values['term']['parent'] > 0) {
|
||||
$level_weights[$values['term']['parent']] = isset($level_weights[$values['term']['parent']]) ? $level_weights[$values['term']['parent']] + 1 : 0;
|
||||
if ($level_weights[$values['term']['parent']] != $term->getWeight()) {
|
||||
$term->setWeight($level_weights[$values['term']['parent']]);
|
||||
$changed_terms[$term->id()] = $term;
|
||||
}
|
||||
}
|
||||
// Update any changed parents.
|
||||
if ($values['term']['parent'] != $term->parents[0]) {
|
||||
$term->parent->target_id = $values['term']['parent'];
|
||||
$changed_terms[$term->id()] = $term;
|
||||
}
|
||||
$hierarchy = $term->parents[0] != 0 ? TAXONOMY_HIERARCHY_SINGLE : $hierarchy;
|
||||
$weight++;
|
||||
}
|
||||
}
|
||||
|
||||
// Build a list of all terms that need to be updated on following pages.
|
||||
for ($weight; $weight < count($tree); $weight++) {
|
||||
$term = $tree[$weight];
|
||||
if ($term->parents[0] == 0 && $term->getWeight() != $weight) {
|
||||
$term->parent->target_id = $term->parents[0];
|
||||
$term->setWeight($weight);
|
||||
$changed_terms[$term->id()] = $term;
|
||||
}
|
||||
$hierarchy = $term->parents[0] != 0 ? TAXONOMY_HIERARCHY_SINGLE : $hierarchy;
|
||||
}
|
||||
|
||||
// Save all updated terms.
|
||||
foreach ($changed_terms as $term) {
|
||||
$term->save();
|
||||
}
|
||||
|
||||
// Update the vocabulary hierarchy to flat or single hierarchy.
|
||||
if ($vocabulary->getHierarchy() != $hierarchy) {
|
||||
$vocabulary->setHierarchy($hierarchy);
|
||||
$vocabulary->save();
|
||||
}
|
||||
drupal_set_message($this->t('The configuration options have been saved.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects to confirmation form for the reset action.
|
||||
*/
|
||||
public function submitReset(array &$form, FormStateInterface $form_state) {
|
||||
/** @var $vocabulary \Drupal\taxonomy\VocabularyInterface */
|
||||
$vocabulary = $form_state->get(['taxonomy', 'vocabulary']);
|
||||
$form_state->setRedirectUrl($vocabulary->urlInfo('reset-form'));
|
||||
}
|
||||
|
||||
}
|
66
core/modules/taxonomy/src/Form/TermDeleteForm.php
Normal file
66
core/modules/taxonomy/src/Form/TermDeleteForm.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Form\TermDeleteForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Form;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Entity\ContentEntityDeleteForm;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Provides a deletion confirmation form for taxonomy term.
|
||||
*/
|
||||
class TermDeleteForm extends ContentEntityDeleteForm {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
// The cancel URL is the vocabulary collection, terms have no global
|
||||
// list page.
|
||||
return new Url('entity.taxonomy_vocabulary.collection');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getRedirectUrl() {
|
||||
return $this->getCancelUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->t('Deleting a term will delete all its children if there are any. This action cannot be undone.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDeletionMessage() {
|
||||
return $this->t('Deleted term %name.', array('%name' => $this->entity->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitForm($form, $form_state);
|
||||
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $term */
|
||||
$term = $this->getEntity();
|
||||
if ($term->isDefaultTranslation()) {
|
||||
$storage = $this->entityManager->getStorage('taxonomy_vocabulary');
|
||||
$vocabulary = $storage->load($this->entity->bundle());
|
||||
|
||||
// @todo Move to storage http://drupal.org/node/1988712
|
||||
taxonomy_check_vocabulary_hierarchy($vocabulary, array('tid' => $term->id()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
45
core/modules/taxonomy/src/Form/VocabularyDeleteForm.php
Normal file
45
core/modules/taxonomy/src/Form/VocabularyDeleteForm.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Form\VocabularyDeleteForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityDeleteForm;
|
||||
|
||||
/**
|
||||
* Provides a deletion confirmation form for taxonomy vocabulary.
|
||||
*/
|
||||
class VocabularyDeleteForm extends EntityDeleteForm {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'taxonomy_vocabulary_confirm_delete';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you want to delete the vocabulary %title?', array('%title' => $this->entity->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->t('Deleting a vocabulary will delete all the terms in it. This action cannot be undone.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDeletionMessage() {
|
||||
return $this->t('Deleted vocabulary %name.', array('%name' => $this->entity->label()));
|
||||
}
|
||||
|
||||
}
|
93
core/modules/taxonomy/src/Form/VocabularyResetForm.php
Normal file
93
core/modules/taxonomy/src/Form/VocabularyResetForm.php
Normal file
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Form\VocabularyResetForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\taxonomy\TermStorageInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides confirmation form for resetting a vocabulary to alphabetical order.
|
||||
*/
|
||||
class VocabularyResetForm extends EntityConfirmFormBase {
|
||||
|
||||
/**
|
||||
* The term storage.
|
||||
*
|
||||
* @var \Drupal\taxonomy\TermStorageInterface
|
||||
*/
|
||||
protected $termStorage;
|
||||
|
||||
/**
|
||||
* Constructs a new VocabularyResetForm object.
|
||||
*
|
||||
* @param \Drupal\taxonomy\TermStorageInterface $term_storage
|
||||
* The term storage.
|
||||
*/
|
||||
public function __construct(TermStorageInterface $term_storage) {
|
||||
$this->termStorage = $term_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager')->getStorage('taxonomy_term')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'taxonomy_vocabulary_confirm_reset_alphabetical';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you want to reset the vocabulary %title to alphabetical order?', array('%title' => $this->entity->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return $this->entity->urlInfo('overview-form');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->t('Resetting a vocabulary will discard all custom ordering and sort items alphabetically.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return $this->t('Reset to alphabetical');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitForm($form, $form_state);
|
||||
$this->termStorage->resetWeights($this->entity->id());
|
||||
|
||||
drupal_set_message($this->t('Reset vocabulary %name to alphabetical order.', array('%name' => $this->entity->label())));
|
||||
$this->logger('taxonomy')->notice('Reset vocabulary %name to alphabetical order.', array('%name' => $this->entity->label()));
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Plugin\EntityReferenceSelection\TermSelection.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Plugin\EntityReferenceSelection;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
use Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
|
||||
/**
|
||||
* Provides specific access control for the taxonomy_term entity type.
|
||||
*
|
||||
* @EntityReferenceSelection(
|
||||
* id = "default:taxonomy_term",
|
||||
* label = @Translation("Taxonomy Term selection"),
|
||||
* entity_types = {"taxonomy_term"},
|
||||
* group = "default",
|
||||
* weight = 1
|
||||
* )
|
||||
*/
|
||||
class TermSelection extends SelectionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function entityQueryAlter(SelectInterface $query) {
|
||||
// @todo: How to set access, as vocabulary is now config?
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::buildConfigurationForm($form, $form_state);
|
||||
|
||||
$form['target_bundles']['#title'] = $this->t('Vocabularies');
|
||||
// @todo: Currently allow auto-create only on taxonomy terms.
|
||||
$form['auto_create'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t("Create referenced entities if they don't already exist"),
|
||||
'#default_value' => isset($this->configuration['handler_settings']['auto_create']) ? $this->configuration['handler_settings']['auto_create'] : FALSE,
|
||||
);
|
||||
|
||||
// Sorting is not possible for taxonomy terms because we use
|
||||
// \Drupal\taxonomy\TermStorageInterface::loadTree() to retrieve matches.
|
||||
$form['sort']['#access'] = FALSE;
|
||||
|
||||
return $form;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
|
||||
if ($match || $limit) {
|
||||
return parent::getReferenceableEntities($match , $match_operator, $limit);
|
||||
}
|
||||
|
||||
$options = array();
|
||||
|
||||
$bundles = $this->entityManager->getBundleInfo('taxonomy_term');
|
||||
$handler_settings = $this->configuration['handler_settings'];
|
||||
$bundle_names = !empty($handler_settings['target_bundles']) ? $handler_settings['target_bundles'] : array_keys($bundles);
|
||||
|
||||
foreach ($bundle_names as $bundle) {
|
||||
if ($vocabulary = Vocabulary::load($bundle)) {
|
||||
if ($terms = $this->entityManager->getStorage('taxonomy_term')->loadTree($vocabulary->id(), 0, NULL, TRUE)) {
|
||||
foreach ($terms as $term) {
|
||||
$options[$vocabulary->id()][$term->id()] = str_repeat('-', $term->depth) . SafeMarkup::checkPlain($term->getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Plugin\Field\FieldFormatter\EntityReferenceTaxonomyTermRssFormatter.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Plugin\Field\FieldFormatter;
|
||||
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceFormatterBase;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'entity reference taxonomy term RSS' formatter.
|
||||
*
|
||||
* @FieldFormatter(
|
||||
* id = "entity_reference_rss_category",
|
||||
* label = @Translation("RSS category"),
|
||||
* description = @Translation("Display reference to taxonomy term in RSS."),
|
||||
* field_types = {
|
||||
* "entity_reference"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class EntityReferenceTaxonomyTermRssFormatter extends EntityReferenceFormatterBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function viewElements(FieldItemListInterface $items) {
|
||||
$parent_entity = $items->getEntity();
|
||||
$elements = array();
|
||||
|
||||
foreach ($this->getEntitiesToView($items) as $delta => $entity) {
|
||||
$parent_entity->rss_elements[] = array(
|
||||
'key' => 'category',
|
||||
'value' => $entity->label(),
|
||||
'attributes' => array(
|
||||
'domain' => $entity->id() ? \Drupal::url('entity.taxonomy_term.canonical', ['taxonomy_term' => $entity->id()], array('absolute' => TRUE)) : '',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function isApplicable(FieldDefinitionInterface $field_definition) {
|
||||
// This formatter is only available for taxonomy terms.
|
||||
return $field_definition->getFieldStorageDefinition()->getSetting('target_type') == 'taxonomy_term';
|
||||
}
|
||||
|
||||
}
|
32
core/modules/taxonomy/src/Plugin/views/argument/IndexTid.php
Normal file
32
core/modules/taxonomy/src/Plugin/views/argument/IndexTid.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Plugin\views\argument\IndexTid.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Plugin\views\argument;
|
||||
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\views\Plugin\views\argument\ManyToOne;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
|
||||
/**
|
||||
* Allow taxonomy term ID(s) as argument.
|
||||
*
|
||||
* @ingroup views_argument_handlers
|
||||
*
|
||||
* @ViewsArgument("taxonomy_index_tid")
|
||||
*/
|
||||
class IndexTid extends ManyToOne {
|
||||
|
||||
public function titleQuery() {
|
||||
$titles = array();
|
||||
$terms = Term::loadMultiple($this->value);
|
||||
foreach ($terms as $term) {
|
||||
$titles[] = SafeMarkup::checkPlain(\Drupal::entityManager()->getTranslationFromContext($term)->label());
|
||||
}
|
||||
return $titles;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Plugin\views\argument\IndexTidDepth.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Plugin\views\argument;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\views\Plugin\views\argument\ArgumentPluginBase;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Argument handler for taxonomy terms with depth.
|
||||
*
|
||||
* This handler is actually part of the node table and has some restrictions,
|
||||
* because it uses a subquery to find nodes with.
|
||||
*
|
||||
* @ingroup views_argument_handlers
|
||||
*
|
||||
* @ViewsArgument("taxonomy_index_tid_depth")
|
||||
*/
|
||||
class IndexTidDepth extends ArgumentPluginBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* @var EntityStorageInterface
|
||||
*/
|
||||
protected $termStorage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityStorageInterface $termStorage) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->termStorage = $termStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static($configuration, $plugin_id, $plugin_definition, $container->get('entity.manager')->getStorage('taxonomy_term'));
|
||||
}
|
||||
|
||||
protected function defineOptions() {
|
||||
$options = parent::defineOptions();
|
||||
|
||||
$options['depth'] = array('default' => 0);
|
||||
$options['break_phrase'] = array('default' => FALSE);
|
||||
$options['use_taxonomy_term_path'] = array('default' => FALSE);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
$form['depth'] = array(
|
||||
'#type' => 'weight',
|
||||
'#title' => $this->t('Depth'),
|
||||
'#default_value' => $this->options['depth'],
|
||||
'#description' => $this->t('The depth will match nodes tagged with terms in the hierarchy. For example, if you have the term "fruit" and a child term "apple", with a depth of 1 (or higher) then filtering for the term "fruit" will get nodes that are tagged with "apple" as well as "fruit". If negative, the reverse is true; searching for "apple" will also pick up nodes tagged with "fruit" if depth is -1 (or lower).'),
|
||||
);
|
||||
|
||||
$form['break_phrase'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Allow multiple values'),
|
||||
'#description' => $this->t('If selected, users can enter multiple values in the form of 1+2+3. Due to the number of JOINs it would require, AND will be treated as OR with this filter.'),
|
||||
'#default_value' => !empty($this->options['break_phrase']),
|
||||
);
|
||||
|
||||
parent::buildOptionsForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override defaultActions() to remove summary actions.
|
||||
*/
|
||||
protected function defaultActions($which = NULL) {
|
||||
if ($which) {
|
||||
if (in_array($which, array('ignore', 'not found', 'empty', 'default'))) {
|
||||
return parent::defaultActions($which);
|
||||
}
|
||||
return;
|
||||
}
|
||||
$actions = parent::defaultActions();
|
||||
unset($actions['summary asc']);
|
||||
unset($actions['summary desc']);
|
||||
unset($actions['summary asc by count']);
|
||||
unset($actions['summary desc by count']);
|
||||
return $actions;
|
||||
}
|
||||
|
||||
public function query($group_by = FALSE) {
|
||||
$this->ensureMyTable();
|
||||
|
||||
if (!empty($this->options['break_phrase'])) {
|
||||
$break = static::breakString($this->argument);
|
||||
if ($break->value === array(-1)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$operator = (count($break->value) > 1) ? 'IN' : '=';
|
||||
$tids = $break->value;
|
||||
}
|
||||
else {
|
||||
$operator = "=";
|
||||
$tids = $this->argument;
|
||||
}
|
||||
// Now build the subqueries.
|
||||
$subquery = db_select('taxonomy_index', 'tn');
|
||||
$subquery->addField('tn', 'nid');
|
||||
$where = db_or()->condition('tn.tid', $tids, $operator);
|
||||
$last = "tn";
|
||||
|
||||
if ($this->options['depth'] > 0) {
|
||||
$subquery->leftJoin('taxonomy_term_hierarchy', 'th', "th.tid = tn.tid");
|
||||
$last = "th";
|
||||
foreach (range(1, abs($this->options['depth'])) as $count) {
|
||||
$subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.parent = th$count.tid");
|
||||
$where->condition("th$count.tid", $tids, $operator);
|
||||
$last = "th$count";
|
||||
}
|
||||
}
|
||||
elseif ($this->options['depth'] < 0) {
|
||||
foreach (range(1, abs($this->options['depth'])) as $count) {
|
||||
$subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.tid = th$count.parent");
|
||||
$where->condition("th$count.tid", $tids, $operator);
|
||||
$last = "th$count";
|
||||
}
|
||||
}
|
||||
|
||||
$subquery->condition($where);
|
||||
$this->query->addWhere(0, "$this->tableAlias.$this->realField", $subquery, 'IN');
|
||||
}
|
||||
|
||||
function title() {
|
||||
$term = $this->termStorage->load($this->argument);
|
||||
if (!empty($term)) {
|
||||
return SafeMarkup::checkPlain($term->getName());
|
||||
}
|
||||
// TODO review text
|
||||
return $this->t('No name');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Plugin\views\argument\IndexTidDepthModifier.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Plugin\views\argument;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\Plugin\views\argument\ArgumentPluginBase;
|
||||
|
||||
/**
|
||||
* Argument handler for to modify depth for a previous term.
|
||||
*
|
||||
* This handler is actually part of the node table and has some restrictions,
|
||||
* because it uses a subquery to find nodes with.
|
||||
*
|
||||
* @ingroup views_argument_handlers
|
||||
*
|
||||
* @ViewsArgument("taxonomy_index_tid_depth_modifier")
|
||||
*/
|
||||
class IndexTidDepthModifier extends ArgumentPluginBase {
|
||||
|
||||
public function buildOptionsForm(&$form, FormStateInterface $form_state) { }
|
||||
|
||||
public function query($group_by = FALSE) { }
|
||||
|
||||
public function preQuery() {
|
||||
// We don't know our argument yet, but it's based upon our position:
|
||||
$argument = isset($this->view->args[$this->position]) ? $this->view->args[$this->position] : NULL;
|
||||
if (!is_numeric($argument)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($argument > 10) {
|
||||
$argument = 10;
|
||||
}
|
||||
|
||||
if ($argument < -10) {
|
||||
$argument = -10;
|
||||
}
|
||||
|
||||
// figure out which argument preceded us.
|
||||
$keys = array_reverse(array_keys($this->view->argument));
|
||||
$skip = TRUE;
|
||||
foreach ($keys as $key) {
|
||||
if ($key == $this->options['id']) {
|
||||
$skip = FALSE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($skip) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($this->view->argument[$key])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($handler)) {
|
||||
unset($handler);
|
||||
}
|
||||
|
||||
$handler = &$this->view->argument[$key];
|
||||
if (empty($handler->definition['accept depth modifier'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Finally!
|
||||
$handler->options['depth'] = $argument;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
66
core/modules/taxonomy/src/Plugin/views/argument/Taxonomy.php
Normal file
66
core/modules/taxonomy/src/Plugin/views/argument/Taxonomy.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Plugin\views\argument\Taxonomy.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Plugin\views\argument;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\views\Plugin\views\argument\NumericArgument;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Argument handler for basic taxonomy tid.
|
||||
*
|
||||
* @ingroup views_argument_handlers
|
||||
*
|
||||
* @ViewsArgument("taxonomy")
|
||||
*/
|
||||
class Taxonomy extends NumericArgument implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* @var EntityStorageInterface
|
||||
*/
|
||||
protected $termStorage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityStorageInterface $term_storage) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->termStorage = $term_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity.manager')->getStorage('taxonomy_term')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the behavior of title(). Get the title of the node.
|
||||
*/
|
||||
function title() {
|
||||
// There might be no valid argument.
|
||||
if ($this->argument) {
|
||||
$term = $this->termStorage->load($this->argument);
|
||||
if (!empty($term)) {
|
||||
return SafeMarkup::checkPlain($term->getName());
|
||||
}
|
||||
}
|
||||
// TODO review text
|
||||
return $this->t('No name');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Plugin\views\argument\VocabularyVid.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Plugin\views\argument;
|
||||
|
||||
use Drupal\views\Plugin\views\argument\NumericArgument;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\taxonomy\VocabularyStorageInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Argument handler to accept a vocabulary id.
|
||||
*
|
||||
* @ingroup views_argument_handlers
|
||||
*
|
||||
* @ViewsArgument("vocabulary_vid")
|
||||
*/
|
||||
class VocabularyVid extends NumericArgument {
|
||||
|
||||
/**
|
||||
* The vocabulary storage.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyStorageInterface
|
||||
*/
|
||||
protected $vocabularyStorage;
|
||||
|
||||
/**
|
||||
* Constructs the VocabularyVid object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param VocabularyStorageInterface $vocabulary_storage
|
||||
* The vocabulary storage.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, VocabularyStorageInterface $vocabulary_storage) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->vocabularyStorage = $vocabulary_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity.manager')->getStorage('taxonomy_vocabulary')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the behavior of title(). Get the name of the vocabulary.
|
||||
*/
|
||||
function title() {
|
||||
$vocabulary = $this->vocabularyStorage->load($this->argument);
|
||||
if ($vocabulary) {
|
||||
return SafeMarkup::checkPlain($vocabulary->label());
|
||||
}
|
||||
|
||||
return $this->t('No vocabulary');
|
||||
}
|
||||
|
||||
}
|
242
core/modules/taxonomy/src/Plugin/views/argument_default/Tid.php
Normal file
242
core/modules/taxonomy/src/Plugin/views/argument_default/Tid.php
Normal file
|
@ -0,0 +1,242 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Plugin\views\argument_default\Tid.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Plugin\views\argument_default;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\taxonomy\TermInterface;
|
||||
use Drupal\views\Plugin\CacheablePluginInterface;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
use Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Drupal\taxonomy\VocabularyStorageInterface;
|
||||
|
||||
/**
|
||||
* Taxonomy tid default argument.
|
||||
*
|
||||
* @ViewsArgumentDefault(
|
||||
* id = "taxonomy_tid",
|
||||
* title = @Translation("Taxonomy term ID from URL")
|
||||
* )
|
||||
*/
|
||||
class Tid extends ArgumentDefaultPluginBase implements CacheablePluginInterface {
|
||||
|
||||
/**
|
||||
* The route match.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RouteMatchInterface
|
||||
*/
|
||||
protected $routeMatch;
|
||||
|
||||
/**
|
||||
* The vocabulary storage.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyStorageInterface.
|
||||
*/
|
||||
protected $vocabularyStorage;
|
||||
|
||||
/**
|
||||
* Constructs a new Tid instance.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition. *
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match.
|
||||
* @param \Drupal\taxonomy\VocabularyStorageInterface $vocabulary_storage
|
||||
* The vocabulary storage.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteMatchInterface $route_match, VocabularyStorageInterface $vocabulary_storage) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->routeMatch = $route_match;
|
||||
$this->vocabularyStorage = $vocabulary_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('current_route_match'),
|
||||
$container->get('entity.manager')->getStorage('taxonomy_vocabulary')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\views\Plugin\views\Plugin\views\PluginBase::init().
|
||||
*/
|
||||
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
|
||||
parent::init($view, $display, $options);
|
||||
|
||||
// @todo Remove the legacy code.
|
||||
// Convert legacy vids option to machine name vocabularies.
|
||||
if (!empty($this->options['vids'])) {
|
||||
$vocabularies = taxonomy_vocabulary_get_names();
|
||||
foreach ($this->options['vids'] as $vid) {
|
||||
if (isset($vocabularies[$vid], $vocabularies[$vid]->machine_name)) {
|
||||
$this->options['vocabularies'][$vocabularies[$vid]->machine_name] = $vocabularies[$vid]->machine_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function defineOptions() {
|
||||
$options = parent::defineOptions();
|
||||
|
||||
$options['term_page'] = array('default' => TRUE);
|
||||
$options['node'] = array('default' => FALSE);
|
||||
$options['anyall'] = array('default' => ',');
|
||||
$options['limit'] = array('default' => FALSE);
|
||||
$options['vids'] = array('default' => array());
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
$form['term_page'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Load default filter from term page'),
|
||||
'#default_value' => $this->options['term_page'],
|
||||
);
|
||||
$form['node'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Load default filter from node page, that\'s good for related taxonomy blocks'),
|
||||
'#default_value' => $this->options['node'],
|
||||
);
|
||||
|
||||
$form['limit'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Limit terms by vocabulary'),
|
||||
'#default_value' => $this->options['limit'],
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
':input[name="options[argument_default][taxonomy_tid][node]"]' => array('checked' => TRUE),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$options = array();
|
||||
$vocabularies = $this->vocabularyStorage->loadMultiple();
|
||||
foreach ($vocabularies as $voc) {
|
||||
$options[$voc->id()] = $voc->label();
|
||||
}
|
||||
|
||||
$form['vids'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => $this->t('Vocabularies'),
|
||||
'#options' => $options,
|
||||
'#default_value' => $this->options['vids'],
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
':input[name="options[argument_default][taxonomy_tid][limit]"]' => array('checked' => TRUE),
|
||||
':input[name="options[argument_default][taxonomy_tid][node]"]' => array('checked' => TRUE),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$form['anyall'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => $this->t('Multiple-value handling'),
|
||||
'#default_value' => $this->options['anyall'],
|
||||
'#options' => array(
|
||||
',' => $this->t('Filter to items that share all terms'),
|
||||
'+' => $this->t('Filter to items that share any term'),
|
||||
),
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
':input[name="options[argument_default][taxonomy_tid][node]"]' => array('checked' => TRUE),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function submitOptionsForm(&$form, FormStateInterface $form_state, &$options = array()) {
|
||||
// Filter unselected items so we don't unnecessarily store giant arrays.
|
||||
$options['vids'] = array_filter($options['vids']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getArgument() {
|
||||
// Load default argument from taxonomy page.
|
||||
if (!empty($this->options['term_page'])) {
|
||||
if (($taxonomy_term = $this->routeMatch->getParameter('taxonomy_term')) && $taxonomy_term instanceof TermInterface) {
|
||||
return $taxonomy_term->id();
|
||||
}
|
||||
}
|
||||
// Load default argument from node.
|
||||
if (!empty($this->options['node'])) {
|
||||
// Just check, if a node could be detected.
|
||||
if (($node = $this->routeMatch->getParameter('node')) && $node instanceof NodeInterface) {
|
||||
$taxonomy = array();
|
||||
foreach ($node->getFieldDefinitions() as $field) {
|
||||
if ($field->getType() == 'entity_reference' && $field->getSetting('target_type') == 'taxonomy_term') {
|
||||
foreach ($node->get($field->getName()) as $item) {
|
||||
if (($handler_settings = $field->getSetting('handler_settings')) && isset($handler_settings['target_bundles'])) {
|
||||
$taxonomy[$item->target_id] = reset($handler_settings['target_bundles']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($this->options['limit'])) {
|
||||
$tids = array();
|
||||
// filter by vocabulary
|
||||
foreach ($taxonomy as $tid => $vocab) {
|
||||
if (!empty($this->options['vids'][$vocab])) {
|
||||
$tids[] = $tid;
|
||||
}
|
||||
}
|
||||
return implode($this->options['anyall'], $tids);
|
||||
}
|
||||
// Return all tids.
|
||||
else {
|
||||
return implode($this->options['anyall'], array_keys($taxonomy));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCacheable() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return ['url'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
$dependencies = parent::calculateDependencies();
|
||||
|
||||
foreach ($this->vocabularyStorage->loadMultiple(array_keys($this->options['vids'])) as $vocabulary) {
|
||||
$dependencies[$vocabulary->getConfigDependencyKey()][] = $vocabulary->getConfigDependencyName();
|
||||
}
|
||||
return $dependencies;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Plugin\views\argument_validator\Term.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Plugin\views\argument_validator;
|
||||
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
use Drupal\views\Plugin\views\argument_validator\Entity;
|
||||
|
||||
/**
|
||||
* Adds legacy vocabulary handling to standard Entity Argument validation..
|
||||
*/
|
||||
class Term extends Entity {
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\views\Plugin\views\Plugin\views\PluginBase::init().
|
||||
*/
|
||||
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
|
||||
parent::init($view, $display, $options);
|
||||
|
||||
// @todo Remove the legacy code.
|
||||
// Convert legacy vids option to machine name vocabularies.
|
||||
if (!empty($this->options['vids'])) {
|
||||
$vocabularies = taxonomy_vocabulary_get_names();
|
||||
foreach ($this->options['vids'] as $vid) {
|
||||
if (isset($vocabularies[$vid], $vocabularies[$vid]->machine_name)) {
|
||||
$this->options['vocabularies'][$vocabularies[$vid]->machine_name] = $vocabularies[$vid]->machine_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Plugin\views\argument_validator\TermName.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Plugin\views\argument_validator;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\views\Plugin\views\argument_validator\Entity;
|
||||
|
||||
/**
|
||||
* Validates whether a term name is a valid term argument.
|
||||
*
|
||||
* @ViewsArgumentValidator(
|
||||
* id = "taxonomy_term_name",
|
||||
* title = @Translation("Taxonomy term name"),
|
||||
* entity_type = "taxonomy_term"
|
||||
* )
|
||||
*/
|
||||
class TermName extends Entity {
|
||||
|
||||
/**
|
||||
* The taxonomy term storage.
|
||||
*
|
||||
* @var \Drupal\taxonomy\TermStorageInterface
|
||||
*/
|
||||
protected $termStorage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_manager);
|
||||
// Not handling exploding term names.
|
||||
$this->multipleCapable = FALSE;
|
||||
$this->termStorage = $entity_manager->getStorage('taxonomy_term');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function defineOptions() {
|
||||
$options = parent::defineOptions();
|
||||
$options['transform'] = array('default' => FALSE);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
parent::buildOptionsForm($form, $form_state);
|
||||
|
||||
$form['transform'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Transform dashes in URL to spaces in term name filter values'),
|
||||
'#default_value' => $this->options['transform'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateArgument($argument) {
|
||||
if ($this->options['transform']) {
|
||||
$argument = str_replace('-', ' ', $argument);
|
||||
}
|
||||
$terms = $this->termStorage->loadByProperties(array('name' => $argument));
|
||||
|
||||
if (!$terms) {
|
||||
// Returned empty array no terms with the name.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Not knowing which term will be used if more than one is returned check
|
||||
// each one.
|
||||
foreach ($terms as $term) {
|
||||
if (!$this->validateEntity($term)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Plugin\views\field\TaxonomyIndexTid.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Plugin\views\field;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
use Drupal\views\Plugin\views\field\PrerenderList;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\taxonomy\VocabularyStorageInterface;
|
||||
|
||||
/**
|
||||
* Field handler to display all taxonomy terms of a node.
|
||||
*
|
||||
* @ingroup views_field_handlers
|
||||
*
|
||||
* @ViewsField("taxonomy_index_tid")
|
||||
*/
|
||||
class TaxonomyIndexTid extends PrerenderList {
|
||||
|
||||
/**
|
||||
* The vocabulary storage.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyStorageInterface.
|
||||
*/
|
||||
protected $vocabularyStorage;
|
||||
|
||||
/**
|
||||
* Constructs a TaxonomyIndexTid object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\taxonomy\VocabularyStorageInterface $vocabulary_storage
|
||||
* The vocabulary storage.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, VocabularyStorageInterface $vocabulary_storage) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->vocabularyStorage = $vocabulary_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity.manager')->getStorage('taxonomy_vocabulary')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\views\Plugin\views\field\PrerenderList::init().
|
||||
*/
|
||||
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
|
||||
parent::init($view, $display, $options);
|
||||
|
||||
// @todo: Wouldn't it be possible to use $this->base_table and no if here?
|
||||
if ($view->storage->get('base_table') == 'node_field_revision') {
|
||||
$this->additional_fields['nid'] = array('table' => 'node_field_revision', 'field' => 'nid');
|
||||
}
|
||||
else {
|
||||
$this->additional_fields['nid'] = array('table' => 'node_field_data', 'field' => 'nid');
|
||||
}
|
||||
}
|
||||
|
||||
protected function defineOptions() {
|
||||
$options = parent::defineOptions();
|
||||
|
||||
$options['link_to_taxonomy'] = array('default' => TRUE);
|
||||
$options['limit'] = array('default' => FALSE);
|
||||
$options['vids'] = array('default' => array());
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide "link to term" option.
|
||||
*/
|
||||
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
$form['link_to_taxonomy'] = array(
|
||||
'#title' => $this->t('Link this field to its term page'),
|
||||
'#type' => 'checkbox',
|
||||
'#default_value' => !empty($this->options['link_to_taxonomy']),
|
||||
);
|
||||
|
||||
$form['limit'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Limit terms by vocabulary'),
|
||||
'#default_value' => $this->options['limit'],
|
||||
);
|
||||
|
||||
$options = array();
|
||||
$vocabularies = $this->vocabularyStorage->loadMultiple();
|
||||
foreach ($vocabularies as $voc) {
|
||||
$options[$voc->id()] = $voc->label();
|
||||
}
|
||||
|
||||
$form['vids'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => $this->t('Vocabularies'),
|
||||
'#options' => $options,
|
||||
'#default_value' => $this->options['vids'],
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
':input[name="options[limit]"]' => array('checked' => TRUE),
|
||||
),
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
parent::buildOptionsForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this term to the query
|
||||
*/
|
||||
public function query() {
|
||||
$this->addAdditionalFields();
|
||||
}
|
||||
|
||||
public function preRender(&$values) {
|
||||
$vocabularies = $this->vocabularyStorage->loadMultiple();
|
||||
$this->field_alias = $this->aliases['nid'];
|
||||
$nids = array();
|
||||
foreach ($values as $result) {
|
||||
if (!empty($result->{$this->aliases['nid']})) {
|
||||
$nids[] = $result->{$this->aliases['nid']};
|
||||
}
|
||||
}
|
||||
|
||||
if ($nids) {
|
||||
$vocabs = array_filter($this->options['vids']);
|
||||
if (empty($this->options['limit'])) {
|
||||
$vocabs = array();
|
||||
}
|
||||
$result = \Drupal::entityManager()->getStorage('taxonomy_term')->getNodeTerms($nids, $vocabs);
|
||||
|
||||
foreach ($result as $node_nid => $data) {
|
||||
foreach ($data as $tid => $term) {
|
||||
$this->items[$node_nid][$tid]['name'] = \Drupal::entityManager()->getTranslationFromContext($term)->label();
|
||||
$this->items[$node_nid][$tid]['tid'] = $tid;
|
||||
$this->items[$node_nid][$tid]['vocabulary_vid'] = $term->getVocabularyId();
|
||||
$this->items[$node_nid][$tid]['vocabulary'] = SafeMarkup::checkPlain($vocabularies[$term->getVocabularyId()]->label());
|
||||
|
||||
if (!empty($this->options['link_to_taxonomy'])) {
|
||||
$this->items[$node_nid][$tid]['make_link'] = TRUE;
|
||||
$this->items[$node_nid][$tid]['path'] = 'taxonomy/term/' . $tid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function render_item($count, $item) {
|
||||
return $item['name'];
|
||||
}
|
||||
|
||||
protected function documentSelfTokens(&$tokens) {
|
||||
$tokens['[' . $this->options['id'] . '-tid' . ']'] = $this->t('The taxonomy term ID for the term.');
|
||||
$tokens['[' . $this->options['id'] . '-name' . ']'] = $this->t('The taxonomy term name for the term.');
|
||||
$tokens['[' . $this->options['id'] . '-vocabulary-vid' . ']'] = $this->t('The machine name for the vocabulary the term belongs to.');
|
||||
$tokens['[' . $this->options['id'] . '-vocabulary' . ']'] = $this->t('The name for the vocabulary the term belongs to.');
|
||||
}
|
||||
|
||||
protected function addSelfTokens(&$tokens, $item) {
|
||||
foreach (array('tid', 'name', 'vocabulary_vid', 'vocabulary') as $token) {
|
||||
// Replace _ with - for the vocabulary vid.
|
||||
$tokens['[' . $this->options['id'] . '-' . str_replace('_', '-', $token) . ']'] = isset($item[$token]) ? $item[$token] : '';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
60
core/modules/taxonomy/src/Plugin/views/field/TermName.php
Normal file
60
core/modules/taxonomy/src/Plugin/views/field/TermName.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Plugin\views\field\TermName.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Plugin\views\field;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\Plugin\views\field\Field;
|
||||
use Drupal\views\ResultRow;
|
||||
|
||||
/**
|
||||
* Displays taxonomy term names and allows converting spaces to hyphens.
|
||||
*
|
||||
* @ingroup views_field_handlers
|
||||
*
|
||||
* @ViewsField("term_name")
|
||||
*/
|
||||
class TermName extends Field {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItems(ResultRow $values) {
|
||||
$items = parent::getItems($values);
|
||||
if ($this->options['convert_spaces']) {
|
||||
foreach ($items as &$item) {
|
||||
// Replace spaces with hyphens.
|
||||
$name = $item['raw']->get('value')->getValue();
|
||||
$item['rendered']['#markup'] = str_replace(' ', '-', $name);
|
||||
}
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function defineOptions() {
|
||||
$options = parent::defineOptions();
|
||||
$options['convert_spaces'] = array('default' => FALSE);
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
$form['convert_spaces'] = array(
|
||||
'#title' => $this->t('Convert spaces in term names to hyphens'),
|
||||
'#type' => 'checkbox',
|
||||
'#default_value' => !empty($this->options['convert_spaces']),
|
||||
);
|
||||
|
||||
parent::buildOptionsForm($form, $form_state);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,386 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Plugin\views\filter\TaxonomyIndexTid.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Plugin\views\filter;
|
||||
|
||||
use Drupal\Core\Entity\Element\EntityAutocomplete;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\taxonomy\TermStorageInterface;
|
||||
use Drupal\taxonomy\VocabularyStorageInterface;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
use Drupal\views\Plugin\views\filter\ManyToOne;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Tags;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Filter by term id.
|
||||
*
|
||||
* @ingroup views_filter_handlers
|
||||
*
|
||||
* @ViewsFilter("taxonomy_index_tid")
|
||||
*/
|
||||
class TaxonomyIndexTid extends ManyToOne {
|
||||
|
||||
// Stores the exposed input for this filter.
|
||||
var $validated_exposed_input = NULL;
|
||||
|
||||
/**
|
||||
* The vocabulary storage.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyStorageInterface
|
||||
*/
|
||||
protected $vocabularyStorage;
|
||||
|
||||
/**
|
||||
* The term storage.
|
||||
*
|
||||
* @var \Drupal\taxonomy\TermStorageInterface
|
||||
*/
|
||||
protected $termStorage;
|
||||
|
||||
/**
|
||||
* Constructs a TaxonomyIndexTid object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\taxonomy\VocabularyStorageInterface $vocabulary_storage
|
||||
* The vocabulary storage.
|
||||
* @param \Drupal\taxonomy\TermStorageInterface $term_storage
|
||||
* The term storage.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, VocabularyStorageInterface $vocabulary_storage, TermStorageInterface $term_storage) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->vocabularyStorage = $vocabulary_storage;
|
||||
$this->termStorage = $term_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity.manager')->getStorage('taxonomy_vocabulary'),
|
||||
$container->get('entity.manager')->getStorage('taxonomy_term')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\views\Plugin\views\filter\ManyToOne::init().
|
||||
*/
|
||||
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
|
||||
parent::init($view, $display, $options);
|
||||
|
||||
if (!empty($this->definition['vocabulary'])) {
|
||||
$this->options['vid'] = $this->definition['vocabulary'];
|
||||
}
|
||||
}
|
||||
|
||||
public function hasExtraOptions() { return TRUE; }
|
||||
|
||||
public function getValueOptions() { /* don't overwrite the value options */ }
|
||||
|
||||
protected function defineOptions() {
|
||||
$options = parent::defineOptions();
|
||||
|
||||
$options['type'] = array('default' => 'textfield');
|
||||
$options['limit'] = array('default' => TRUE);
|
||||
$options['vid'] = array('default' => '');
|
||||
$options['hierarchy'] = array('default' => FALSE);
|
||||
$options['error_message'] = array('default' => TRUE);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
$vocabularies = $this->vocabularyStorage->loadMultiple();
|
||||
$options = array();
|
||||
foreach ($vocabularies as $voc) {
|
||||
$options[$voc->id()] = $voc->label();
|
||||
}
|
||||
|
||||
if ($this->options['limit']) {
|
||||
// We only do this when the form is displayed.
|
||||
if (empty($this->options['vid'])) {
|
||||
$first_vocabulary = reset($vocabularies);
|
||||
$this->options['vid'] = $first_vocabulary->id();
|
||||
}
|
||||
|
||||
if (empty($this->definition['vocabulary'])) {
|
||||
$form['vid'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => $this->t('Vocabulary'),
|
||||
'#options' => $options,
|
||||
'#description' => $this->t('Select which vocabulary to show terms for in the regular options.'),
|
||||
'#default_value' => $this->options['vid'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$form['type'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => $this->t('Selection type'),
|
||||
'#options' => array('select' => $this->t('Dropdown'), 'textfield' => $this->t('Autocomplete')),
|
||||
'#default_value' => $this->options['type'],
|
||||
);
|
||||
|
||||
$form['hierarchy'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Show hierarchy in dropdown'),
|
||||
'#default_value' => !empty($this->options['hierarchy']),
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
':input[name="options[type]"]' => array('value' => 'select'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
protected function valueForm(&$form, FormStateInterface $form_state) {
|
||||
$vocabulary = $this->vocabularyStorage->load($this->options['vid']);
|
||||
if (empty($vocabulary) && $this->options['limit']) {
|
||||
$form['markup'] = array(
|
||||
'#markup' => '<div class="form-item">' . $this->t('An invalid vocabulary is selected. Please change it in the options.') . '</div>',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->options['type'] == 'textfield') {
|
||||
$terms = $this->value ? Term::loadMultiple(($this->value)) : array();
|
||||
$form['value'] = array(
|
||||
'#title' => $this->options['limit'] ? $this->t('Select terms from vocabulary @voc', array('@voc' => $vocabulary->label())) : $this->t('Select terms'),
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => EntityAutocomplete::getEntityLabels($terms),
|
||||
);
|
||||
|
||||
if ($this->options['limit']) {
|
||||
$form['value']['#type'] = 'entity_autocomplete';
|
||||
$form['value']['#target_type'] = 'taxonomy_term';
|
||||
$form['value']['#selection_settings']['target_bundles'] = array($vocabulary->id());
|
||||
$form['value']['#tags'] = TRUE;
|
||||
$form['value']['#process_default_value'] = FALSE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!empty($this->options['hierarchy']) && $this->options['limit']) {
|
||||
$tree = $this->termStorage->loadTree($vocabulary->id(), 0, NULL, TRUE);
|
||||
$options = array();
|
||||
|
||||
if ($tree) {
|
||||
foreach ($tree as $term) {
|
||||
$choice = new \stdClass();
|
||||
$choice->option = array($term->id() => str_repeat('-', $term->depth) . SafeMarkup::checkPlain(\Drupal::entityManager()->getTranslationFromContext($term)->label()));
|
||||
$options[] = $choice;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$options = array();
|
||||
$query = \Drupal::entityQuery('taxonomy_term')
|
||||
// @todo Sorting on vocabulary properties -
|
||||
// https://www.drupal.org/node/1821274.
|
||||
->sort('weight')
|
||||
->sort('name')
|
||||
->addTag('term_access');
|
||||
if ($this->options['limit']) {
|
||||
$query->condition('vid', $vocabulary->id());
|
||||
}
|
||||
$terms = Term::loadMultiple($query->execute());
|
||||
foreach ($terms as $term) {
|
||||
$options[$term->id()] = SafeMarkup::checkPlain(\Drupal::entityManager()->getTranslationFromContext($term)->label());
|
||||
}
|
||||
}
|
||||
|
||||
$default_value = (array) $this->value;
|
||||
|
||||
if ($exposed = $form_state->get('exposed')) {
|
||||
$identifier = $this->options['expose']['identifier'];
|
||||
|
||||
if (!empty($this->options['expose']['reduce'])) {
|
||||
$options = $this->reduceValueOptions($options);
|
||||
|
||||
if (!empty($this->options['expose']['multiple']) && empty($this->options['expose']['required'])) {
|
||||
$default_value = array();
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($this->options['expose']['multiple'])) {
|
||||
if (empty($this->options['expose']['required']) && (empty($default_value) || !empty($this->options['expose']['reduce']))) {
|
||||
$default_value = 'All';
|
||||
}
|
||||
elseif (empty($default_value)) {
|
||||
$keys = array_keys($options);
|
||||
$default_value = array_shift($keys);
|
||||
}
|
||||
// Due to #1464174 there is a chance that array('') was saved in the admin ui.
|
||||
// Let's choose a safe default value.
|
||||
elseif ($default_value == array('')) {
|
||||
$default_value = 'All';
|
||||
}
|
||||
else {
|
||||
$copy = $default_value;
|
||||
$default_value = array_shift($copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
$form['value'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->options['limit'] ? $this->t('Select terms from vocabulary @voc', array('@voc' => $vocabulary->label())) : $this->t('Select terms'),
|
||||
'#multiple' => TRUE,
|
||||
'#options' => $options,
|
||||
'#size' => min(9, count($options)),
|
||||
'#default_value' => $default_value,
|
||||
);
|
||||
|
||||
$user_input = $form_state->getUserInput();
|
||||
if ($exposed && isset($identifier) && !isset($user_input[$identifier])) {
|
||||
$user_input[$identifier] = $default_value;
|
||||
$form_state->setUserInput($user_input);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$form_state->get('exposed')) {
|
||||
// Retain the helper option
|
||||
$this->helper->buildOptionsForm($form, $form_state);
|
||||
}
|
||||
}
|
||||
|
||||
protected function valueValidate($form, FormStateInterface $form_state) {
|
||||
// We only validate if they've chosen the text field style.
|
||||
if ($this->options['type'] != 'textfield') {
|
||||
return;
|
||||
}
|
||||
|
||||
$tids = array();
|
||||
foreach ($form_state->getValue(array('options', 'value')) as $value) {
|
||||
$tids[] = $value['target_id'];
|
||||
}
|
||||
$form_state->setValue(array('options', 'value'), $tids);
|
||||
}
|
||||
|
||||
public function acceptExposedInput($input) {
|
||||
if (empty($this->options['exposed'])) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// If view is an attachment and is inheriting exposed filters, then assume
|
||||
// exposed input has already been validated
|
||||
if (!empty($this->view->is_attachment) && $this->view->display_handler->usesExposed()) {
|
||||
$this->validated_exposed_input = (array) $this->view->exposed_raw_input[$this->options['expose']['identifier']];
|
||||
}
|
||||
|
||||
// If it's non-required and there's no value don't bother filtering.
|
||||
if (!$this->options['expose']['required'] && empty($this->validated_exposed_input)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$rc = parent::acceptExposedInput($input);
|
||||
if ($rc) {
|
||||
// If we have previously validated input, override.
|
||||
if (isset($this->validated_exposed_input)) {
|
||||
$this->value = $this->validated_exposed_input;
|
||||
}
|
||||
}
|
||||
|
||||
return $rc;
|
||||
}
|
||||
|
||||
public function validateExposed(&$form, FormStateInterface $form_state) {
|
||||
if (empty($this->options['exposed'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$identifier = $this->options['expose']['identifier'];
|
||||
|
||||
// We only validate if they've chosen the text field style.
|
||||
if ($this->options['type'] != 'textfield') {
|
||||
if ($form_state->getValue($identifier) != 'All') {
|
||||
$this->validated_exposed_input = (array) $form_state->getValue($identifier);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($this->options['expose']['identifier'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($form_state->getValue($identifier) as $value) {
|
||||
$this->validated_exposed_input[] = $value['target_id'];
|
||||
}
|
||||
}
|
||||
|
||||
protected function valueSubmit($form, FormStateInterface $form_state) {
|
||||
// prevent array_filter from messing up our arrays in parent submit.
|
||||
}
|
||||
|
||||
public function buildExposeForm(&$form, FormStateInterface $form_state) {
|
||||
parent::buildExposeForm($form, $form_state);
|
||||
if ($this->options['type'] != 'select') {
|
||||
unset($form['expose']['reduce']);
|
||||
}
|
||||
$form['error_message'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Display error message'),
|
||||
'#default_value' => !empty($this->options['error_message']),
|
||||
);
|
||||
}
|
||||
|
||||
public function adminSummary() {
|
||||
// set up $this->valueOptions for the parent summary
|
||||
$this->valueOptions = array();
|
||||
|
||||
if ($this->value) {
|
||||
$this->value = array_filter($this->value);
|
||||
$terms = Term::loadMultiple($this->value);
|
||||
foreach ($terms as $term) {
|
||||
$this->valueOptions[$term->id()] = SafeMarkup::checkPlain(\Drupal::entityManager()->getTranslationFromContext($term)->label());
|
||||
}
|
||||
}
|
||||
return parent::adminSummary();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
$contexts = parent::getCacheContexts();
|
||||
// The result potentially depends on term access and so is just cacheable
|
||||
// per user.
|
||||
// @todo See https://www.drupal.org/node/2352175.
|
||||
$contexts[] = 'user';
|
||||
|
||||
return $contexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
$dependencies = parent::calculateDependencies();
|
||||
|
||||
$vocabulary = $this->vocabularyStorage->load($this->options['vid']);
|
||||
$dependencies[$vocabulary->getConfigDependencyKey()][] = $vocabulary->getConfigDependencyName();
|
||||
|
||||
foreach ($this->options['value'] as $tid) {
|
||||
$term = $this->termStorage->load($tid);
|
||||
$dependencies[$term->getConfigDependencyKey()][] = $term->getConfigDependencyName();
|
||||
}
|
||||
|
||||
return $dependencies;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Plugin\views\filter\TaxonomyIndexTidDepth.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Plugin\views\filter;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Filter handler for taxonomy terms with depth.
|
||||
*
|
||||
* This handler is actually part of the node table and has some restrictions,
|
||||
* because it uses a subquery to find nodes with.
|
||||
*
|
||||
* @ingroup views_filter_handlers
|
||||
*
|
||||
* @ViewsFilter("taxonomy_index_tid_depth")
|
||||
*/
|
||||
class TaxonomyIndexTidDepth extends TaxonomyIndexTid {
|
||||
|
||||
public function operatorOptions($which = 'title') {
|
||||
return array(
|
||||
'or' => $this->t('Is one of'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function defineOptions() {
|
||||
$options = parent::defineOptions();
|
||||
|
||||
$options['depth'] = array('default' => 0);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
parent::buildExtraOptionsForm($form, $form_state);
|
||||
|
||||
$form['depth'] = array(
|
||||
'#type' => 'weight',
|
||||
'#title' => $this->t('Depth'),
|
||||
'#default_value' => $this->options['depth'],
|
||||
'#description' => $this->t('The depth will match nodes tagged with terms in the hierarchy. For example, if you have the term "fruit" and a child term "apple", with a depth of 1 (or higher) then filtering for the term "fruit" will get nodes that are tagged with "apple" as well as "fruit". If negative, the reverse is true; searching for "apple" will also pick up nodes tagged with "fruit" if depth is -1 (or lower).'),
|
||||
);
|
||||
}
|
||||
|
||||
public function query() {
|
||||
// If no filter values are present, then do nothing.
|
||||
if (count($this->value) == 0) {
|
||||
return;
|
||||
}
|
||||
elseif (count($this->value) == 1) {
|
||||
// Sometimes $this->value is an array with a single element so convert it.
|
||||
if (is_array($this->value)) {
|
||||
$this->value = current($this->value);
|
||||
}
|
||||
$operator = '=';
|
||||
}
|
||||
else {
|
||||
$operator = 'IN';# " IN (" . implode(', ', array_fill(0, sizeof($this->value), '%d')) . ")";
|
||||
}
|
||||
|
||||
// The normal use of ensureMyTable() here breaks Views.
|
||||
// So instead we trick the filter into using the alias of the base table.
|
||||
// See https://www.drupal.org/node/271833.
|
||||
// If a relationship is set, we must use the alias it provides.
|
||||
if (!empty($this->relationship)) {
|
||||
$this->tableAlias = $this->relationship;
|
||||
}
|
||||
// If no relationship, then use the alias of the base table.
|
||||
else {
|
||||
$this->tableAlias = $this->query->ensureTable($this->view->storage->get('base_table'));
|
||||
}
|
||||
|
||||
// Now build the subqueries.
|
||||
$subquery = db_select('taxonomy_index', 'tn');
|
||||
$subquery->addField('tn', 'nid');
|
||||
$where = db_or()->condition('tn.tid', $this->value, $operator);
|
||||
$last = "tn";
|
||||
|
||||
if ($this->options['depth'] > 0) {
|
||||
$subquery->leftJoin('taxonomy_term_hierarchy', 'th', "th.tid = tn.tid");
|
||||
$last = "th";
|
||||
foreach (range(1, abs($this->options['depth'])) as $count) {
|
||||
$subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.parent = th$count.tid");
|
||||
$where->condition("th$count.tid", $this->value, $operator);
|
||||
$last = "th$count";
|
||||
}
|
||||
}
|
||||
elseif ($this->options['depth'] < 0) {
|
||||
foreach (range(1, abs($this->options['depth'])) as $count) {
|
||||
$subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.tid = th$count.parent");
|
||||
$where->condition("th$count.tid", $this->value, $operator);
|
||||
$last = "th$count";
|
||||
}
|
||||
}
|
||||
|
||||
$subquery->condition($where);
|
||||
$this->query->addWhere($this->options['group'], "$this->tableAlias.$this->realField", $subquery, 'IN');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Plugin\views\relationship\NodeTermData.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Plugin\views\relationship;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\taxonomy\VocabularyStorageInterface;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
use Drupal\views\Plugin\views\relationship\RelationshipPluginBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Relationship handler to return the taxonomy terms of nodes.
|
||||
*
|
||||
* @ingroup views_relationship_handlers
|
||||
*
|
||||
* @ViewsRelationship("node_term_data")
|
||||
*/
|
||||
class NodeTermData extends RelationshipPluginBase {
|
||||
|
||||
/**
|
||||
* The vocabulary storage.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyStorageInterface
|
||||
*/
|
||||
protected $vocabularyStorage;
|
||||
|
||||
/**
|
||||
* Constructs a NodeTermData object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\taxonomy\VocabularyStorageInterface $vocabulary_storage
|
||||
* The vocabulary storage.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, VocabularyStorageInterface $vocabulary_storage) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->vocabularyStorage = $vocabulary_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity.manager')->getStorage('taxonomy_vocabulary')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\views\Plugin\views\relationship\RelationshipPluginBase::init().
|
||||
*/
|
||||
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
|
||||
parent::init($view, $display, $options);
|
||||
|
||||
// @todo Remove the legacy code.
|
||||
// Convert legacy vids option to machine name vocabularies.
|
||||
if (!empty($this->options['vids'])) {
|
||||
$vocabularies = taxonomy_vocabulary_get_names();
|
||||
foreach ($this->options['vids'] as $vid) {
|
||||
if (isset($vocabularies[$vid], $vocabularies[$vid]->machine_name)) {
|
||||
$this->options['vocabularies'][$vocabularies[$vid]->machine_name] = $vocabularies[$vid]->machine_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function defineOptions() {
|
||||
$options = parent::defineOptions();
|
||||
$options['vids'] = array('default' => array());
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
$vocabularies = $this->vocabularyStorage->loadMultiple();
|
||||
$options = array();
|
||||
foreach ($vocabularies as $voc) {
|
||||
$options[$voc->id()] = $voc->label();
|
||||
}
|
||||
|
||||
$form['vids'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => $this->t('Vocabularies'),
|
||||
'#options' => $options,
|
||||
'#default_value' => $this->options['vids'],
|
||||
'#description' => $this->t('Choose which vocabularies you wish to relate. Remember that every term found will create a new record, so this relationship is best used on just one vocabulary that has only one term per node.'),
|
||||
);
|
||||
parent::buildOptionsForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
// Transform the #type = checkboxes value to a numerically indexed array,
|
||||
// because the config schema expects a sequence, not a mapping.
|
||||
$vids = $form_state->getValue(['options', 'vids']);
|
||||
$form_state->setValue(['options', 'vids'], array_values(array_filter($vids)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to implement a relationship in a query.
|
||||
*/
|
||||
public function query() {
|
||||
$this->ensureMyTable();
|
||||
|
||||
$def = $this->definition;
|
||||
$def['table'] = 'taxonomy_term_field_data';
|
||||
|
||||
if (!array_filter($this->options['vids'])) {
|
||||
$taxonomy_index = $this->query->addTable('taxonomy_index', $this->relationship);
|
||||
$def['left_table'] = $taxonomy_index;
|
||||
$def['left_field'] = 'tid';
|
||||
$def['field'] = 'tid';
|
||||
$def['type'] = empty($this->options['required']) ? 'LEFT' : 'INNER';
|
||||
}
|
||||
else {
|
||||
// If vocabularies are supplied join a subselect instead
|
||||
$def['left_table'] = $this->tableAlias;
|
||||
$def['left_field'] = 'nid';
|
||||
$def['field'] = 'nid';
|
||||
$def['type'] = empty($this->options['required']) ? 'LEFT' : 'INNER';
|
||||
$def['adjusted'] = TRUE;
|
||||
|
||||
$query = db_select('taxonomy_term_field_data', 'td');
|
||||
$query->addJoin($def['type'], 'taxonomy_index', 'tn', 'tn.tid = td.tid');
|
||||
$query->condition('td.vid', array_filter($this->options['vids']), 'IN');
|
||||
$query->addTag('term_access');
|
||||
$query->fields('td');
|
||||
$query->fields('tn', array('nid'));
|
||||
$def['table formula'] = $query;
|
||||
}
|
||||
|
||||
$join = \Drupal::service('plugin.manager.views.join')->createInstance('standard', $def);
|
||||
|
||||
// use a short alias for this:
|
||||
$alias = $def['table'] . '_' . $this->table;
|
||||
|
||||
$this->alias = $this->query->addRelationship($alias, $join, 'taxonomy_term_field_data', $this->relationship);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
$dependencies = parent::calculateDependencies();
|
||||
|
||||
foreach ($this->options['vids'] as $vocabulary_id) {
|
||||
if ($vocabulary = $this->vocabularyStorage->load($vocabulary_id)) {
|
||||
$dependencies[$vocabulary->getConfigDependencyKey()][] = $vocabulary->getConfigDependencyName();
|
||||
}
|
||||
}
|
||||
|
||||
return $dependencies;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Plugin\views\wizard\TaxonomyTerm.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Plugin\views\wizard;
|
||||
|
||||
use Drupal\views\Plugin\views\wizard\WizardPluginBase;
|
||||
|
||||
/**
|
||||
* Tests creating taxonomy views with the wizard.
|
||||
*
|
||||
* @ViewsWizard(
|
||||
* id = "taxonomy_term",
|
||||
* base_table = "taxonomy_term_field_data",
|
||||
* title = @Translation("Taxonomy terms")
|
||||
* )
|
||||
*/
|
||||
class TaxonomyTerm extends WizardPluginBase {
|
||||
|
||||
/**
|
||||
* Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::defaultDisplayOptions().
|
||||
*/
|
||||
protected function defaultDisplayOptions() {
|
||||
$display_options = parent::defaultDisplayOptions();
|
||||
|
||||
// Add permission-based access control.
|
||||
$display_options['access']['type'] = 'perm';
|
||||
$display_options['access']['options']['perm'] = 'access content';
|
||||
|
||||
// Remove the default fields, since we are customizing them here.
|
||||
unset($display_options['fields']);
|
||||
|
||||
/* Field: Taxonomy: Term */
|
||||
$display_options['fields']['name']['id'] = 'name';
|
||||
$display_options['fields']['name']['table'] = 'taxonomy_term_field_data';
|
||||
$display_options['fields']['name']['field'] = 'name';
|
||||
$display_options['fields']['name']['entity_type'] = 'taxonomy_term';
|
||||
$display_options['fields']['name']['entity_field'] = 'name';
|
||||
$display_options['fields']['name']['label'] = '';
|
||||
$display_options['fields']['name']['alter']['alter_text'] = 0;
|
||||
$display_options['fields']['name']['alter']['make_link'] = 0;
|
||||
$display_options['fields']['name']['alter']['absolute'] = 0;
|
||||
$display_options['fields']['name']['alter']['trim'] = 0;
|
||||
$display_options['fields']['name']['alter']['word_boundary'] = 0;
|
||||
$display_options['fields']['name']['alter']['ellipsis'] = 0;
|
||||
$display_options['fields']['name']['alter']['strip_tags'] = 0;
|
||||
$display_options['fields']['name']['alter']['html'] = 0;
|
||||
$display_options['fields']['name']['hide_empty'] = 0;
|
||||
$display_options['fields']['name']['empty_zero'] = 0;
|
||||
$display_options['fields']['name']['type'] = 'string';
|
||||
$display_options['fields']['name']['settings']['link_to_entity'] = 1;
|
||||
$display_options['fields']['name']['plugin_id'] = 'term_name';
|
||||
|
||||
return $display_options;
|
||||
}
|
||||
|
||||
}
|
71
core/modules/taxonomy/src/TaxonomyPermissions.php
Normal file
71
core/modules/taxonomy/src/TaxonomyPermissions.php
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\TaxonomyPermissions.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides dynamic permissions of the taxonomy module.
|
||||
*
|
||||
* @see taxonomy.permissions.yml
|
||||
*/
|
||||
class TaxonomyPermissions implements ContainerInjectionInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Constructs a TaxonomyPermissions instance.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager) {
|
||||
$this->entityManager = $entity_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static($container->get('entity.manager'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get taxonomy permissions.
|
||||
*
|
||||
* @return array
|
||||
* Permissions array.
|
||||
*/
|
||||
public function permissions() {
|
||||
$permissions = [];
|
||||
foreach ($this->entityManager->getStorage('taxonomy_vocabulary')->loadMultiple() as $vocabulary) {
|
||||
$permissions += [
|
||||
'edit terms in ' . $vocabulary->id() => [
|
||||
'title' => $this->t('Edit terms in %vocabulary', ['%vocabulary' => $vocabulary->label()]),
|
||||
],
|
||||
];
|
||||
$permissions += [
|
||||
'delete terms in ' . $vocabulary->id() => [
|
||||
'title' => $this->t('Delete terms from %vocabulary', ['%vocabulary' => $vocabulary->label()]),
|
||||
],
|
||||
];
|
||||
}
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
}
|
52
core/modules/taxonomy/src/TermAccessControlHandler.php
Normal file
52
core/modules/taxonomy/src/TermAccessControlHandler.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\TermAccessControlHandler.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityAccessControlHandler;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Defines the access control handler for the taxonomy term entity type.
|
||||
*
|
||||
* @see \Drupal\taxonomy\Entity\Term
|
||||
*/
|
||||
class TermAccessControlHandler extends EntityAccessControlHandler {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
|
||||
switch ($operation) {
|
||||
case 'view':
|
||||
return AccessResult::allowedIfHasPermission($account, 'access content');
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
return AccessResult::allowedIfHasPermissions($account, ["edit terms in {$entity->bundle()}", 'administer taxonomy'], 'OR');
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
return AccessResult::allowedIfHasPermissions($account, ["delete terms in {$entity->bundle()}", 'administer taxonomy'], 'OR');
|
||||
break;
|
||||
|
||||
default:
|
||||
// No opinion.
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
|
||||
return AccessResult::allowedIfHasPermission($account, 'administer taxonomy');
|
||||
}
|
||||
|
||||
}
|
75
core/modules/taxonomy/src/TermBreadcrumbBuilder.php
Normal file
75
core/modules/taxonomy/src/TermBreadcrumbBuilder.php
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\TermBreadcrumbBuilder.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Link;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Provides a custom taxonomy breadcrumb builder that uses the term hierarchy.
|
||||
*/
|
||||
class TermBreadcrumbBuilder implements BreadcrumbBuilderInterface {
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The taxonomy storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $termStorage;
|
||||
|
||||
/**
|
||||
* Constructs the TermBreadcrumbBuilder.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entityManager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entityManager) {
|
||||
$this->entityManager = $entityManager;
|
||||
$this->termStorage = $entityManager->getStorage('taxonomy_term');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies(RouteMatchInterface $route_match) {
|
||||
return $route_match->getRouteName() == 'entity.taxonomy_term.canonical'
|
||||
&& $route_match->getParameter('taxonomy_term') instanceof TermInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(RouteMatchInterface $route_match) {
|
||||
$term = $route_match->getParameter('taxonomy_term');
|
||||
// @todo This overrides any other possible breadcrumb and is a pure
|
||||
// hard-coded presumption. Make this behavior configurable per
|
||||
// vocabulary or term.
|
||||
$breadcrumb = array();
|
||||
while ($parents = $this->termStorage->loadParents($term->id())) {
|
||||
$term = array_shift($parents);
|
||||
$term = $this->entityManager->getTranslationFromContext($term);
|
||||
$breadcrumb[] = Link::createFromRoute($term->getName(), 'entity.taxonomy_term.canonical', array('taxonomy_term' => $term->id()));
|
||||
}
|
||||
$breadcrumb[] = Link::createFromRoute($this->t('Home'), '<front>');
|
||||
$breadcrumb = array_reverse($breadcrumb);
|
||||
|
||||
return $breadcrumb;
|
||||
}
|
||||
|
||||
}
|
168
core/modules/taxonomy/src/TermForm.php
Normal file
168
core/modules/taxonomy/src/TermForm.php
Normal file
|
@ -0,0 +1,168 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\TermForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityForm;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Base for controller for taxonomy term edit forms.
|
||||
*/
|
||||
class TermForm extends ContentEntityForm {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$term = $this->entity;
|
||||
$vocab_storage = $this->entityManager->getStorage('taxonomy_vocabulary');
|
||||
$taxonomy_storage = $this->entityManager->getStorage('taxonomy_term');
|
||||
$vocabulary = $vocab_storage->load($term->bundle());
|
||||
|
||||
$parent = array_keys($taxonomy_storage->loadParents($term->id()));
|
||||
$form_state->set(['taxonomy', 'parent'], $parent);
|
||||
$form_state->set(['taxonomy', 'vocabulary'], $vocabulary);
|
||||
|
||||
$form['relations'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('Relations'),
|
||||
'#open' => $vocabulary->getHierarchy() == TAXONOMY_HIERARCHY_MULTIPLE,
|
||||
'#weight' => 10,
|
||||
);
|
||||
|
||||
// \Drupal\taxonomy\TermStorageInterface::loadTree() and
|
||||
// \Drupal\taxonomy\TermStorageInterface::loadParents() may contain large
|
||||
// numbers of items so we check for taxonomy.settings:override_selector
|
||||
// before loading the full vocabulary. Contrib modules can then intercept
|
||||
// before hook_form_alter to provide scalable alternatives.
|
||||
if (!$this->config('taxonomy.settings')->get('override_selector')) {
|
||||
$parent = array_keys($taxonomy_storage->loadParents($term->id()));
|
||||
$children = $taxonomy_storage->loadTree($vocabulary->id(), $term->id());
|
||||
|
||||
// A term can't be the child of itself, nor of its children.
|
||||
foreach ($children as $child) {
|
||||
$exclude[] = $child->tid;
|
||||
}
|
||||
$exclude[] = $term->id();
|
||||
|
||||
$tree = $taxonomy_storage->loadTree($vocabulary->id());
|
||||
$options = array('<' . $this->t('root') . '>');
|
||||
if (empty($parent)) {
|
||||
$parent = array(0);
|
||||
}
|
||||
|
||||
foreach ($tree as $item) {
|
||||
if (!in_array($item->tid, $exclude)) {
|
||||
$options[$item->tid] = str_repeat('-', $item->depth) . $item->name;
|
||||
}
|
||||
}
|
||||
|
||||
$form['relations']['parent'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Parent terms'),
|
||||
'#options' => $options,
|
||||
'#default_value' => $parent,
|
||||
'#multiple' => TRUE,
|
||||
);
|
||||
}
|
||||
|
||||
$form['relations']['weight'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Weight'),
|
||||
'#size' => 6,
|
||||
'#default_value' => $term->getWeight(),
|
||||
'#description' => $this->t('Terms are displayed in ascending order by weight.'),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
|
||||
$form['vid'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $vocabulary->id(),
|
||||
);
|
||||
|
||||
$form['tid'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $term->id(),
|
||||
);
|
||||
|
||||
return parent::form($form, $form_state, $term);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate(array $form, FormStateInterface $form_state) {
|
||||
parent::validate($form, $form_state);
|
||||
|
||||
// Ensure numeric values.
|
||||
if ($form_state->hasValue('weight') && !is_numeric($form_state->getValue('weight'))) {
|
||||
$form_state->setErrorByName('weight', $this->t('Weight value must be numeric.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildEntity(array $form, FormStateInterface $form_state) {
|
||||
$term = parent::buildEntity($form, $form_state);
|
||||
|
||||
// Prevent leading and trailing spaces in term names.
|
||||
$term->setName(trim($term->getName()));
|
||||
|
||||
// Assign parents with proper delta values starting from 0.
|
||||
$term->parent = array_keys($form_state->getValue('parent'));
|
||||
|
||||
return $term;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$term = $this->entity;
|
||||
|
||||
$result = $term->save();
|
||||
|
||||
$link = $term->link($this->t('Edit'), 'edit-form');
|
||||
switch ($result) {
|
||||
case SAVED_NEW:
|
||||
drupal_set_message($this->t('Created new term %term.', array('%term' => $term->getName())));
|
||||
$this->logger('taxonomy')->notice('Created new term %term.', array('%term' => $term->getName(), 'link' => $link));
|
||||
break;
|
||||
case SAVED_UPDATED:
|
||||
drupal_set_message($this->t('Updated term %term.', array('%term' => $term->getName())));
|
||||
$this->logger('taxonomy')->notice('Updated term %term.', array('%term' => $term->getName(), 'link' => $link));
|
||||
break;
|
||||
}
|
||||
|
||||
$current_parent_count = count($form_state->getValue('parent'));
|
||||
$previous_parent_count = count($form_state->get(['taxonomy', 'parent']));
|
||||
// Root doesn't count if it's the only parent.
|
||||
if ($current_parent_count == 1 && $form_state->hasValue(array('parent', 0))) {
|
||||
$current_parent_count = 0;
|
||||
$form_state->setValue('parent', array());
|
||||
}
|
||||
|
||||
// If the number of parents has been reduced to one or none, do a check on the
|
||||
// parents of every term in the vocabulary value.
|
||||
$vocabulary = $form_state->get(['taxonomy', 'vocabulary']);
|
||||
if ($current_parent_count < $previous_parent_count && $current_parent_count < 2) {
|
||||
taxonomy_check_vocabulary_hierarchy($vocabulary, $form_state->getValues());
|
||||
}
|
||||
// If we've increased the number of parents and this is a single or flat
|
||||
// hierarchy, update the vocabulary immediately.
|
||||
elseif ($current_parent_count > $previous_parent_count && $vocabulary->getHierarchy() != TAXONOMY_HIERARCHY_MULTIPLE) {
|
||||
$vocabulary->setHierarchy($current_parent_count == 1 ? TAXONOMY_HIERARCHY_SINGLE : TAXONOMY_HIERARCHY_MULTIPLE);
|
||||
$vocabulary->save();
|
||||
}
|
||||
|
||||
$form_state->setValue('tid', $term->id());
|
||||
$form_state->set('tid', $term->id());
|
||||
}
|
||||
|
||||
}
|
98
core/modules/taxonomy/src/TermInterface.php
Normal file
98
core/modules/taxonomy/src/TermInterface.php
Normal file
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\TermInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityChangedInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a taxonomy term entity.
|
||||
*/
|
||||
interface TermInterface extends ContentEntityInterface, EntityChangedInterface {
|
||||
|
||||
/**
|
||||
* Gets the term's description.
|
||||
*
|
||||
* @return string
|
||||
* The term description.
|
||||
*/
|
||||
public function getDescription();
|
||||
|
||||
/**
|
||||
* Sets the term's description.
|
||||
*
|
||||
* @param string $description
|
||||
* The term's description.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDescription($description);
|
||||
|
||||
/**
|
||||
* Gets the text format name for the term's description.
|
||||
*
|
||||
* @return string
|
||||
* The text format name.
|
||||
*/
|
||||
public function getFormat();
|
||||
|
||||
/**
|
||||
* Sets the text format name for the term's description.
|
||||
*
|
||||
* @param string $format
|
||||
* The term's description text format.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setFormat($format);
|
||||
|
||||
/**
|
||||
* Gets the name of the term.
|
||||
*
|
||||
* @return string
|
||||
* The name of the term.
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Sets the name of the term.
|
||||
*
|
||||
* @param int $name
|
||||
* The term's name.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setName($name);
|
||||
|
||||
/**
|
||||
* Gets the weight of this term.
|
||||
*
|
||||
* @return int
|
||||
* The weight of the term.
|
||||
*/
|
||||
public function getWeight();
|
||||
|
||||
/**
|
||||
* Gets the weight of this term.
|
||||
*
|
||||
* @param int $weight
|
||||
* The term's weight.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setWeight($weight);
|
||||
|
||||
/**
|
||||
* Get the taxonomy vocabulary id this term belongs to.
|
||||
*
|
||||
* @return int
|
||||
* The id of the vocabulary.
|
||||
*/
|
||||
public function getVocabularyId();
|
||||
|
||||
}
|
378
core/modules/taxonomy/src/TermStorage.php
Normal file
378
core/modules/taxonomy/src/TermStorage.php
Normal file
|
@ -0,0 +1,378 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\TermStorage.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\Query\QueryInterface;
|
||||
|
||||
/**
|
||||
* Defines a Controller class for taxonomy terms.
|
||||
*/
|
||||
class TermStorage extends SqlContentEntityStorage implements TermStorageInterface {
|
||||
|
||||
/**
|
||||
* Array of loaded parents keyed by child term ID.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $parents = array();
|
||||
|
||||
/**
|
||||
* Array of all loaded term ancestry keyed by ancestor term ID.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $parentsAll = array();
|
||||
|
||||
/**
|
||||
* Array of child terms keyed by parent term ID.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $children = array();
|
||||
|
||||
/**
|
||||
* Array of term parents keyed by vocabulary ID and child term ID.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $treeParents = array();
|
||||
|
||||
/**
|
||||
* Array of term ancestors keyed by vocabulary ID and parent term ID.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $treeChildren = array();
|
||||
|
||||
/**
|
||||
* Array of terms in a tree keyed by vocabulary ID and term ID.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $treeTerms = array();
|
||||
|
||||
/**
|
||||
* Array of loaded trees keyed by a cache id matching tree arguments.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $trees = array();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param array $values
|
||||
* An array of values to set, keyed by property name. A value for the
|
||||
* vocabulary ID ('vid') is required.
|
||||
*/
|
||||
public function create(array $values = array()) {
|
||||
// Save new terms with no parents by default.
|
||||
if (empty($values['parent'])) {
|
||||
$values['parent'] = array(0);
|
||||
}
|
||||
$entity = parent::create($values);
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resetCache(array $ids = NULL) {
|
||||
drupal_static_reset('taxonomy_term_count_nodes');
|
||||
$this->parents = array();
|
||||
$this->parentsAll = array();
|
||||
$this->children = array();
|
||||
$this->treeChildren = array();
|
||||
$this->treeParents = array();
|
||||
$this->treeTerms = array();
|
||||
$this->trees = array();
|
||||
parent::resetCache($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteTermHierarchy($tids) {
|
||||
$this->database->delete('taxonomy_term_hierarchy')
|
||||
->condition('tid', $tids, 'IN')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateTermHierarchy(EntityInterface $term) {
|
||||
$query = $this->database->insert('taxonomy_term_hierarchy')
|
||||
->fields(array('tid', 'parent'));
|
||||
|
||||
foreach ($term->parent as $parent) {
|
||||
$query->values(array(
|
||||
'tid' => $term->id(),
|
||||
'parent' => (int) $parent->target_id,
|
||||
));
|
||||
}
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadParents($tid) {
|
||||
if (!isset($this->parents[$tid])) {
|
||||
$parents = array();
|
||||
$query = $this->database->select('taxonomy_term_field_data', 't');
|
||||
$query->join('taxonomy_term_hierarchy', 'h', 'h.parent = t.tid');
|
||||
$query->addField('t', 'tid');
|
||||
$query->condition('h.tid', $tid);
|
||||
$query->condition('t.default_langcode', 1);
|
||||
$query->addTag('term_access');
|
||||
$query->orderBy('t.weight');
|
||||
$query->orderBy('t.name');
|
||||
if ($ids = $query->execute()->fetchCol()) {
|
||||
$parents = $this->loadMultiple($ids);
|
||||
}
|
||||
$this->parents[$tid] = $parents;
|
||||
}
|
||||
return $this->parents[$tid];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadAllParents($tid) {
|
||||
if (!isset($this->parentsAll[$tid])) {
|
||||
$parents = array();
|
||||
if ($term = $this->load($tid)) {
|
||||
$parents[$term->id()] = $term;
|
||||
$terms_to_search[] = $term->id();
|
||||
|
||||
while ($tid = array_shift($terms_to_search)) {
|
||||
if ($new_parents = $this->loadParents($tid)) {
|
||||
foreach ($new_parents as $new_parent) {
|
||||
if (!isset($parents[$new_parent->id()])) {
|
||||
$parents[$new_parent->id()] = $new_parent;
|
||||
$terms_to_search[] = $new_parent->id();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->parentsAll[$tid] = $parents;
|
||||
}
|
||||
return $this->parentsAll[$tid];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadChildren($tid, $vid = NULL) {
|
||||
if (!isset($this->children[$tid])) {
|
||||
$children = array();
|
||||
$query = $this->database->select('taxonomy_term_field_data', 't');
|
||||
$query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
|
||||
$query->addField('t', 'tid');
|
||||
$query->condition('h.parent', $tid);
|
||||
if ($vid) {
|
||||
$query->condition('t.vid', $vid);
|
||||
}
|
||||
$query->condition('t.default_langcode', 1);
|
||||
$query->addTag('term_access');
|
||||
$query->orderBy('t.weight');
|
||||
$query->orderBy('t.name');
|
||||
if ($ids = $query->execute()->fetchCol()) {
|
||||
$children = $this->loadMultiple($ids);
|
||||
}
|
||||
$this->children[$tid] = $children;
|
||||
}
|
||||
return $this->children[$tid];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadTree($vid, $parent = 0, $max_depth = NULL, $load_entities = FALSE) {
|
||||
$cache_key = implode(':', func_get_args());
|
||||
if (!isset($this->trees[$cache_key])) {
|
||||
// We cache trees, so it's not CPU-intensive to call on a term and its
|
||||
// children, too.
|
||||
if (!isset($this->treeChildren[$vid])) {
|
||||
$this->treeChildren[$vid] = array();
|
||||
$this->treeParents[$vid] = array();
|
||||
$this->treeTerms[$vid] = array();
|
||||
$query = $this->database->select('taxonomy_term_field_data', 't');
|
||||
$query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
|
||||
$result = $query
|
||||
->addTag('term_access')
|
||||
->fields('t')
|
||||
->fields('h', array('parent'))
|
||||
->condition('t.vid', $vid)
|
||||
->condition('t.default_langcode', 1)
|
||||
->orderBy('t.weight')
|
||||
->orderBy('t.name')
|
||||
->execute();
|
||||
foreach ($result as $term) {
|
||||
$this->treeChildren[$vid][$term->parent][] = $term->tid;
|
||||
$this->treeParents[$vid][$term->tid][] = $term->parent;
|
||||
$this->treeTerms[$vid][$term->tid] = $term;
|
||||
}
|
||||
}
|
||||
|
||||
// Load full entities, if necessary. The entity controller statically
|
||||
// caches the results.
|
||||
$term_entities = array();
|
||||
if ($load_entities) {
|
||||
$term_entities = $this->loadMultiple(array_keys($this->treeTerms[$vid]));
|
||||
}
|
||||
|
||||
$max_depth = (!isset($max_depth)) ? count($this->treeChildren[$vid]) : $max_depth;
|
||||
$tree = array();
|
||||
|
||||
// Keeps track of the parents we have to process, the last entry is used
|
||||
// for the next processing step.
|
||||
$process_parents = array();
|
||||
$process_parents[] = $parent;
|
||||
|
||||
// Loops over the parent terms and adds its children to the tree array.
|
||||
// Uses a loop instead of a recursion, because it's more efficient.
|
||||
while (count($process_parents)) {
|
||||
$parent = array_pop($process_parents);
|
||||
// The number of parents determines the current depth.
|
||||
$depth = count($process_parents);
|
||||
if ($max_depth > $depth && !empty($this->treeChildren[$vid][$parent])) {
|
||||
$has_children = FALSE;
|
||||
$child = current($this->treeChildren[$vid][$parent]);
|
||||
do {
|
||||
if (empty($child)) {
|
||||
break;
|
||||
}
|
||||
$term = $load_entities ? $term_entities[$child] : $this->treeTerms[$vid][$child];
|
||||
if (isset($this->treeParents[$vid][$load_entities ? $term->id() : $term->tid])) {
|
||||
// Clone the term so that the depth attribute remains correct
|
||||
// in the event of multiple parents.
|
||||
$term = clone $term;
|
||||
}
|
||||
$term->depth = $depth;
|
||||
unset($term->parent);
|
||||
$tid = $load_entities ? $term->id() : $term->tid;
|
||||
$term->parents = $this->treeParents[$vid][$tid];
|
||||
$tree[] = $term;
|
||||
if (!empty($this->treeChildren[$vid][$tid])) {
|
||||
$has_children = TRUE;
|
||||
|
||||
// We have to continue with this parent later.
|
||||
$process_parents[] = $parent;
|
||||
// Use the current term as parent for the next iteration.
|
||||
$process_parents[] = $tid;
|
||||
|
||||
// Reset pointers for child lists because we step in there more
|
||||
// often with multi parents.
|
||||
reset($this->treeChildren[$vid][$tid]);
|
||||
// Move pointer so that we get the correct term the next time.
|
||||
next($this->treeChildren[$vid][$parent]);
|
||||
break;
|
||||
}
|
||||
} while ($child = next($this->treeChildren[$vid][$parent]));
|
||||
|
||||
if (!$has_children) {
|
||||
// We processed all terms in this hierarchy-level, reset pointer
|
||||
// so that this function works the next time it gets called.
|
||||
reset($this->treeChildren[$vid][$parent]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->trees[$cache_key] = $tree;
|
||||
}
|
||||
return $this->trees[$cache_key];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function nodeCount($vid) {
|
||||
$query = $this->database->select('taxonomy_index', 'ti');
|
||||
$query->addExpression('COUNT(DISTINCT ti.nid)');
|
||||
$query->leftJoin('taxonomy_term_data', 'td', 'ti.tid = td.tid');
|
||||
$query->condition('td.vid', $vid);
|
||||
$query->addTag('vocabulary_node_count');
|
||||
return $query->execute()->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resetWeights($vid) {
|
||||
$this->database->update('taxonomy_term_field_data')
|
||||
->fields(array('weight' => 0))
|
||||
->condition('vid', $vid)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNodeTerms($nids, $vocabs = array(), $langcode = NULL) {
|
||||
$query = db_select('taxonomy_term_field_data', 'td');
|
||||
$query->innerJoin('taxonomy_index', 'tn', 'td.tid = tn.tid');
|
||||
$query->fields('td', array('tid'));
|
||||
$query->addField('tn', 'nid', 'node_nid');
|
||||
$query->orderby('td.weight');
|
||||
$query->orderby('td.name');
|
||||
$query->condition('tn.nid', $nids);
|
||||
$query->addTag('term_access');
|
||||
if (!empty($vocabs)) {
|
||||
$query->condition('td.vid', $vocabs);
|
||||
}
|
||||
if (!empty($langcode)) {
|
||||
$query->condition('td.langcode', $langcode);
|
||||
}
|
||||
|
||||
$results = array();
|
||||
foreach ($query->execute() as $term_record) {
|
||||
$results[$term_record->node_nid][] = $term_record->tid;
|
||||
$all_tids[] = $term_record->tid;
|
||||
}
|
||||
|
||||
$all_terms = $this->loadMultiple($all_tids);
|
||||
$terms = array();
|
||||
foreach ($results as $nid => $tids) {
|
||||
foreach ($tids as $tid) {
|
||||
$terms[$nid][$tid] = $all_terms[$term_record->tid];
|
||||
}
|
||||
}
|
||||
return $terms;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __sleep() {
|
||||
$vars = parent::__sleep();
|
||||
// Do not serialize static cache.
|
||||
unset($vars['parents'], $vars['parentsAll'], $vars['children'], $vars['treeChildren'], $vars['treeParents'], $vars['treeTerms'], $vars['trees']);
|
||||
return $vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __wakeup() {
|
||||
parent::__wakeup();
|
||||
// Initialize static caches.
|
||||
$this->parents = array();
|
||||
$this->parentsAll = array();
|
||||
$this->children = array();
|
||||
$this->treeChildren = array();
|
||||
$this->treeParents = array();
|
||||
$this->treeTerms = array();
|
||||
$this->trees = array();
|
||||
}
|
||||
|
||||
}
|
126
core/modules/taxonomy/src/TermStorageInterface.php
Normal file
126
core/modules/taxonomy/src/TermStorageInterface.php
Normal file
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\TermStorageInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
|
||||
/**
|
||||
* Defines an interface for taxonomy_term entity storage classes.
|
||||
*/
|
||||
interface TermStorageInterface extends EntityStorageInterface {
|
||||
|
||||
/**
|
||||
* Removed reference to terms from term_hierarchy.
|
||||
*
|
||||
* @param array $tids
|
||||
* Array of terms that need to be removed from hierarchy.
|
||||
*/
|
||||
public function deleteTermHierarchy($tids);
|
||||
|
||||
/**
|
||||
* Updates terms hierarchy information with the hierarchy trail of it.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $term
|
||||
* Term entity that needs to be added to term hierarchy information.
|
||||
*/
|
||||
public function updateTermHierarchy(EntityInterface $term);
|
||||
|
||||
/**
|
||||
* Finds all parents of a given term ID.
|
||||
*
|
||||
* @param int $tid
|
||||
* Term ID to retrieve parents for.
|
||||
*
|
||||
* @return \Drupal\taxonomy\TermInterface[]
|
||||
* An array of term objects which are the parents of the term $tid.
|
||||
*/
|
||||
public function loadParents($tid);
|
||||
|
||||
/**
|
||||
* Finds all ancestors of a given term ID.
|
||||
*
|
||||
* @param int $tid
|
||||
* Term ID to retrieve ancestors for.
|
||||
*
|
||||
* @return \Drupal\taxonomy\TermInterface[]
|
||||
* An array of term objects which are the ancestors of the term $tid.
|
||||
*/
|
||||
public function loadAllParents($tid);
|
||||
|
||||
/**
|
||||
* Finds all children of a term ID.
|
||||
*
|
||||
* @param int $tid
|
||||
* Term ID to retrieve parents for.
|
||||
* @param string $vid
|
||||
* An optional vocabulary ID to restrict the child search.
|
||||
*
|
||||
* @return \Drupal\taxonomy\TermInterface[]
|
||||
* An array of term objects that are the children of the term $tid.
|
||||
*/
|
||||
public function loadChildren($tid, $vid = NULL);
|
||||
|
||||
/**
|
||||
* Finds all terms in a given vocabulary ID.
|
||||
*
|
||||
* @param string $vid
|
||||
* Vocabulary ID to retrieve terms for.
|
||||
* @param int $parent
|
||||
* The term ID under which to generate the tree. If 0, generate the tree
|
||||
* for the entire vocabulary.
|
||||
* @param int $max_depth
|
||||
* The number of levels of the tree to return. Leave NULL to return all
|
||||
* levels.
|
||||
* @param bool $load_entities
|
||||
* If TRUE, a full entity load will occur on the term objects. Otherwise
|
||||
* they are partial objects queried directly from the {taxonomy_term_data}
|
||||
* table to save execution time and memory consumption when listing large
|
||||
* numbers of terms. Defaults to FALSE.
|
||||
*
|
||||
* @return \Drupal\taxonomy\TermInterface[]
|
||||
* An array of term objects that are the children of the vocabulary $vid.
|
||||
*/
|
||||
public function loadTree($vid, $parent = 0, $max_depth = NULL, $load_entities = FALSE);
|
||||
|
||||
/**
|
||||
* Count the number of nodes in a given vocabulary ID.
|
||||
*
|
||||
* @param string $vid
|
||||
* Vocabulary ID to retrieve terms for.
|
||||
*
|
||||
* @return int
|
||||
* A count of the nodes in a given vocabulary ID.
|
||||
*/
|
||||
public function nodeCount($vid);
|
||||
|
||||
/**
|
||||
* Reset the weights for a given vocabulary ID.
|
||||
*
|
||||
* @param string $vid
|
||||
* Vocabulary ID to retrieve terms for.
|
||||
*/
|
||||
public function resetWeights($vid);
|
||||
|
||||
/**
|
||||
* Returns all terms used to tag some given nodes.
|
||||
*
|
||||
* @param array $nids
|
||||
* Node IDs to retrieve terms for.
|
||||
* @param array $vocabs
|
||||
* (optional) A vocabularies array to restrict the term search. Defaults to
|
||||
* empty array.
|
||||
* @param string $langcode
|
||||
* (optional) A language code to restrict the term search. Defaults to NULL.
|
||||
*
|
||||
* @return array
|
||||
* An array of nids and the term entities they were tagged with.
|
||||
*/
|
||||
public function getNodeTerms($nids, $vocabs = array(), $langcode = NULL);
|
||||
|
||||
}
|
144
core/modules/taxonomy/src/TermStorageSchema.php
Normal file
144
core/modules/taxonomy/src/TermStorageSchema.php
Normal file
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\TermStorageSchema.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Defines the term schema handler.
|
||||
*/
|
||||
class TermStorageSchema extends SqlContentEntityStorageSchema {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $reset = FALSE) {
|
||||
$schema = parent::getEntitySchema($entity_type, $reset = FALSE);
|
||||
|
||||
$schema['taxonomy_term_field_data']['indexes'] += array(
|
||||
'taxonomy_term__tree' => array('vid', 'weight', 'name'),
|
||||
'taxonomy_term__vid_name' => array('vid', 'name'),
|
||||
);
|
||||
|
||||
$schema['taxonomy_term_hierarchy'] = array(
|
||||
'description' => 'Stores the hierarchical relationship between terms.',
|
||||
'fields' => array(
|
||||
'tid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'Primary Key: The {taxonomy_term_data}.tid of the term.',
|
||||
),
|
||||
'parent' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => "Primary Key: The {taxonomy_term_data}.tid of the term's parent. 0 indicates no parent.",
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'parent' => array('parent'),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'taxonomy_term_data' => array(
|
||||
'table' => 'taxonomy_term_data',
|
||||
'columns' => array('tid' => 'tid'),
|
||||
),
|
||||
),
|
||||
'primary key' => array('tid', 'parent'),
|
||||
);
|
||||
|
||||
$schema['taxonomy_index'] = array(
|
||||
'description' => 'Maintains denormalized information about node/term relationships.',
|
||||
'fields' => array(
|
||||
'nid' => array(
|
||||
'description' => 'The {node}.nid this record tracks.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'tid' => array(
|
||||
'description' => 'The term ID.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'status' => array(
|
||||
'description' => 'Boolean indicating whether the node is published (visible to non-administrators).',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 1,
|
||||
),
|
||||
'sticky' => array(
|
||||
'description' => 'Boolean indicating whether the node is sticky.',
|
||||
'type' => 'int',
|
||||
'not null' => FALSE,
|
||||
'default' => 0,
|
||||
'size' => 'tiny',
|
||||
),
|
||||
'created' => array(
|
||||
'description' => 'The Unix timestamp when the node was created.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default'=> 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('nid', 'tid'),
|
||||
'indexes' => array(
|
||||
'term_node' => array('tid', 'status', 'sticky', 'created'),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'tracked_node' => array(
|
||||
'table' => 'node',
|
||||
'columns' => array('nid' => 'nid'),
|
||||
),
|
||||
'term' => array(
|
||||
'table' => 'taxonomy_term_data',
|
||||
'columns' => array('tid' => 'tid'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSharedTableFieldSchema(FieldStorageDefinitionInterface $storage_definition, $table_name, array $column_mapping) {
|
||||
$schema = parent::getSharedTableFieldSchema($storage_definition, $table_name, $column_mapping);
|
||||
$field_name = $storage_definition->getName();
|
||||
|
||||
if ($table_name == 'taxonomy_term_field_data') {
|
||||
// Remove unneeded indexes.
|
||||
unset($schema['indexes']['taxonomy_term_field__vid__target_id']);
|
||||
unset($schema['indexes']['taxonomy_term_field__description__format']);
|
||||
|
||||
switch ($field_name) {
|
||||
case 'weight':
|
||||
// Improves the performance of the taxonomy_term__tree index defined
|
||||
// in getEntitySchema().
|
||||
$schema['fields'][$field_name]['not null'] = TRUE;
|
||||
break;
|
||||
|
||||
case 'name':
|
||||
$this->addSharedTableFieldIndex($storage_definition, $schema, TRUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
}
|
44
core/modules/taxonomy/src/TermTranslationHandler.php
Normal file
44
core/modules/taxonomy/src/TermTranslationHandler.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\TermTranslationHandler.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\content_translation\ContentTranslationHandler;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Defines the translation handler for terms.
|
||||
*/
|
||||
class TermTranslationHandler extends ContentTranslationHandler {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function entityFormAlter(array &$form, FormStateInterface $form_state, EntityInterface $entity) {
|
||||
parent::entityFormAlter($form, $form_state, $entity);
|
||||
$form['actions']['submit']['#submit'][] = array($this, 'entityFormSave');
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for TermTranslationHandler::entityFormAlter().
|
||||
*
|
||||
* This handles the save action.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityForm::build().
|
||||
*/
|
||||
function entityFormSave(array $form, FormStateInterface $form_state) {
|
||||
if ($this->getSourceLangcode($form_state)) {
|
||||
$entity = $form_state->getFormObject()->getEntity();
|
||||
// We need a redirect here, otherwise we would get an access denied page,
|
||||
// since the current URL would be preserved and we would try to add a
|
||||
// translation for a language that already has a translation.
|
||||
$form_state->setRedirectUrl($entity->urlInfo('edit-form'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
30
core/modules/taxonomy/src/TermViewBuilder.php
Normal file
30
core/modules/taxonomy/src/TermViewBuilder.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\TermViewBuilder.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityViewBuilder;
|
||||
|
||||
/**
|
||||
* Render controller for taxonomy terms.
|
||||
*/
|
||||
class TermViewBuilder extends EntityViewBuilder {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function alterBuild(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode, $langcode = NULL) {
|
||||
parent::alterBuild($build, $entity, $display, $view_mode, $langcode);
|
||||
$build['#contextual_links']['taxonomy_term'] = array(
|
||||
'route_parameters' => array('taxonomy_term' => $entity->id()),
|
||||
'metadata' => array('changed' => $entity->getChangedTime()),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
269
core/modules/taxonomy/src/TermViewsData.php
Normal file
269
core/modules/taxonomy/src/TermViewsData.php
Normal file
|
@ -0,0 +1,269 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\TermViewsData.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\views\EntityViewsData;
|
||||
|
||||
/**
|
||||
* Provides the views data for the taxonomy entity type.
|
||||
*/
|
||||
class TermViewsData extends EntityViewsData {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getViewsData() {
|
||||
$data = parent::getViewsData();
|
||||
|
||||
$data['taxonomy_term_field_data']['table']['base']['help'] = t('Taxonomy terms are attached to nodes.');
|
||||
$data['taxonomy_term_field_data']['table']['base']['access query tag'] = 'term_access';
|
||||
$data['taxonomy_term_field_data']['table']['wizard_id'] = 'taxonomy_term';
|
||||
|
||||
$data['taxonomy_term_field_data']['table']['join'] = array(
|
||||
// This is provided for the many_to_one argument.
|
||||
'taxonomy_index' => array(
|
||||
'field' => 'tid',
|
||||
'left_field' => 'tid',
|
||||
),
|
||||
);
|
||||
|
||||
$data['taxonomy_term_field_data']['tid']['help'] = t('The tid of a taxonomy term.');
|
||||
|
||||
$data['taxonomy_term_field_data']['tid']['argument']['id'] = 'taxonomy';
|
||||
$data['taxonomy_term_field_data']['tid']['argument']['name field'] = 'name';
|
||||
$data['taxonomy_term_field_data']['tid']['argument']['zero is null'] = TRUE;
|
||||
|
||||
$data['taxonomy_term_field_data']['tid']['filter']['id'] = 'taxonomy_index_tid';
|
||||
$data['taxonomy_term_field_data']['tid']['filter']['title'] = t('Term');
|
||||
$data['taxonomy_term_field_data']['tid']['filter']['help'] = t('Taxonomy term chosen from autocomplete or select widget.');
|
||||
$data['taxonomy_term_field_data']['tid']['filter']['hierarchy table'] = 'taxonomy_term_hierarchy';
|
||||
$data['taxonomy_term_field_data']['tid']['filter']['numeric'] = TRUE;
|
||||
|
||||
$data['taxonomy_term_field_data']['tid_raw'] = array(
|
||||
'title' => t('Term ID'),
|
||||
'help' => t('The tid of a taxonomy term.'),
|
||||
'real field' => 'tid',
|
||||
'filter' => array(
|
||||
'id' => 'numeric',
|
||||
'allow empty' => TRUE,
|
||||
),
|
||||
);
|
||||
|
||||
$data['taxonomy_term_field_data']['tid_representative'] = array(
|
||||
'relationship' => array(
|
||||
'title' => t('Representative node'),
|
||||
'label' => t('Representative node'),
|
||||
'help' => t('Obtains a single representative node for each term, according to a chosen sort criterion.'),
|
||||
'id' => 'groupwise_max',
|
||||
'relationship field' => 'tid',
|
||||
'outer field' => 'taxonomy_term_field_data.tid',
|
||||
'argument table' => 'taxonomy_term_field_data',
|
||||
'argument field' => 'tid',
|
||||
'base' => 'node_field_data',
|
||||
'field' => 'nid',
|
||||
'relationship' => 'node_field_data:term_node_tid'
|
||||
),
|
||||
);
|
||||
|
||||
$data['taxonomy_term_field_data']['vid']['help'] = t('Filter the results of "Taxonomy: Term" to a particular vocabulary.');
|
||||
unset($data['taxonomy_term_field_data']['vid']['field']);
|
||||
unset($data['taxonomy_term_field_data']['vid']['argument']);
|
||||
unset($data['taxonomy_term_field_data']['vid']['sort']);
|
||||
|
||||
$data['taxonomy_term_field_data']['name']['field']['id'] = 'term_name';
|
||||
$data['taxonomy_term_field_data']['name']['argument']['many to one'] = TRUE;
|
||||
$data['taxonomy_term_field_data']['name']['argument']['empty field name'] = t('Uncategorized');
|
||||
|
||||
$data['taxonomy_term_field_data']['description__value']['field']['click sortable'] = FALSE;
|
||||
|
||||
$data['taxonomy_term_field_data']['changed']['title'] = t('Updated date');
|
||||
$data['taxonomy_term_field_data']['changed']['help'] = t('The date the term was last updated.');
|
||||
|
||||
$data['taxonomy_term_field_data']['changed_fulldate'] = array(
|
||||
'title' => t('Updated date'),
|
||||
'help' => t('Date in the form of CCYYMMDD.'),
|
||||
'argument' => array(
|
||||
'field' => 'changed',
|
||||
'id' => 'date_fulldate',
|
||||
),
|
||||
);
|
||||
|
||||
$data['taxonomy_term_field_data']['changed_year_month'] = array(
|
||||
'title' => t('Updated year + month'),
|
||||
'help' => t('Date in the form of YYYYMM.'),
|
||||
'argument' => array(
|
||||
'field' => 'changed',
|
||||
'id' => 'date_year_month',
|
||||
),
|
||||
);
|
||||
|
||||
$data['taxonomy_term_field_data']['changed_year'] = array(
|
||||
'title' => t('Updated year'),
|
||||
'help' => t('Date in the form of YYYY.'),
|
||||
'argument' => array(
|
||||
'field' => 'changed',
|
||||
'id' => 'date_year',
|
||||
),
|
||||
);
|
||||
|
||||
$data['taxonomy_term_field_data']['changed_month'] = array(
|
||||
'title' => t('Updated month'),
|
||||
'help' => t('Date in the form of MM (01 - 12).'),
|
||||
'argument' => array(
|
||||
'field' => 'changed',
|
||||
'id' => 'date_month',
|
||||
),
|
||||
);
|
||||
|
||||
$data['taxonomy_term_field_data']['changed_day'] = array(
|
||||
'title' => t('Updated day'),
|
||||
'help' => t('Date in the form of DD (01 - 31).'),
|
||||
'argument' => array(
|
||||
'field' => 'changed',
|
||||
'id' => 'date_day',
|
||||
),
|
||||
);
|
||||
|
||||
$data['taxonomy_term_field_data']['changed_week'] = array(
|
||||
'title' => t('Updated week'),
|
||||
'help' => t('Date in the form of WW (01 - 53).'),
|
||||
'argument' => array(
|
||||
'field' => 'changed',
|
||||
'id' => 'date_week',
|
||||
),
|
||||
);
|
||||
|
||||
$data['taxonomy_index']['table']['group'] = t('Taxonomy term');
|
||||
|
||||
$data['taxonomy_index']['table']['join'] = array(
|
||||
'taxonomy_term_field_data' => array(
|
||||
// links directly to taxonomy_term_field_data via tid
|
||||
'left_field' => 'tid',
|
||||
'field' => 'tid',
|
||||
),
|
||||
'node_field_data' => array(
|
||||
// links directly to node via nid
|
||||
'left_field' => 'nid',
|
||||
'field' => 'nid',
|
||||
),
|
||||
'taxonomy_term_hierarchy' => array(
|
||||
'left_field' => 'tid',
|
||||
'field' => 'tid',
|
||||
),
|
||||
);
|
||||
|
||||
$data['taxonomy_index']['nid'] = array(
|
||||
'title' => t('Content with term'),
|
||||
'help' => t('Relate all content tagged with a term.'),
|
||||
'relationship' => array(
|
||||
'id' => 'standard',
|
||||
'base' => 'node',
|
||||
'base field' => 'nid',
|
||||
'label' => t('node'),
|
||||
'skip base' => 'node',
|
||||
),
|
||||
);
|
||||
|
||||
// @todo This stuff needs to move to a node field since really it's all
|
||||
// about nodes.
|
||||
$data['taxonomy_index']['tid'] = array(
|
||||
'group' => t('Content'),
|
||||
'title' => t('Has taxonomy term ID'),
|
||||
'help' => t('Display content if it has the selected taxonomy terms.'),
|
||||
'argument' => array(
|
||||
'id' => 'taxonomy_index_tid',
|
||||
'name table' => 'taxonomy_term_field_data',
|
||||
'name field' => 'name',
|
||||
'empty field name' => t('Uncategorized'),
|
||||
'numeric' => TRUE,
|
||||
'skip base' => 'taxonomy_term_field_data',
|
||||
),
|
||||
'filter' => array(
|
||||
'title' => t('Has taxonomy term'),
|
||||
'id' => 'taxonomy_index_tid',
|
||||
'hierarchy table' => 'taxonomy_term_hierarchy',
|
||||
'numeric' => TRUE,
|
||||
'skip base' => 'taxonomy_term_field_data',
|
||||
'allow empty' => TRUE,
|
||||
),
|
||||
);
|
||||
|
||||
$data['taxonomy_index']['status'] = [
|
||||
'title' => t('Publish status'),
|
||||
'help' => t('Whether or not the content related to a term is published.'),
|
||||
'filter' => [
|
||||
'id' => 'boolean',
|
||||
'label' => t('Published status'),
|
||||
'type' => 'yes-no',
|
||||
],
|
||||
];
|
||||
|
||||
$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']['provider'] = 'taxonomy';
|
||||
|
||||
$data['taxonomy_term_hierarchy']['table']['join'] = array(
|
||||
'taxonomy_term_hierarchy' => array(
|
||||
// Link to self through left.parent = right.tid (going down in depth).
|
||||
'left_field' => 'tid',
|
||||
'field' => 'parent',
|
||||
),
|
||||
'taxonomy_term_field_data' => array(
|
||||
// Link directly to taxonomy_term_field_data via tid.
|
||||
'left_field' => 'tid',
|
||||
'field' => 'tid',
|
||||
),
|
||||
);
|
||||
|
||||
$data['taxonomy_term_hierarchy']['parent'] = array(
|
||||
'title' => t('Parent term'),
|
||||
'help' => t('The parent term of the term. This can produce duplicate entries if you are using a vocabulary that allows multiple parents.'),
|
||||
'relationship' => array(
|
||||
'base' => 'taxonomy_term_field_data',
|
||||
'field' => 'parent',
|
||||
'label' => t('Parent'),
|
||||
'id' => 'standard',
|
||||
),
|
||||
'filter' => array(
|
||||
'help' => t('Filter the results of "Taxonomy: Term" by the parent pid.'),
|
||||
'id' => 'numeric',
|
||||
),
|
||||
'argument' => array(
|
||||
'help' => t('The parent term of the term.'),
|
||||
'id' => 'taxonomy',
|
||||
),
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
75
core/modules/taxonomy/src/Tests/EfqTest.php
Normal file
75
core/modules/taxonomy/src/Tests/EfqTest.php
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\EfqTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
|
||||
/**
|
||||
* Verifies operation of a taxonomy-based Entity Query.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class EfqTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* Vocabulary for testing.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyInterface
|
||||
*/
|
||||
protected $vocabulary;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer taxonomy']));
|
||||
$this->vocabulary = $this->createVocabulary();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a basic taxonomy entity query works.
|
||||
*/
|
||||
function testTaxonomyEfq() {
|
||||
$terms = array();
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$term = $this->createTerm($this->vocabulary);
|
||||
$terms[$term->id()] = $term;
|
||||
}
|
||||
$result = \Drupal::entityQuery('taxonomy_term')->execute();
|
||||
sort($result);
|
||||
$this->assertEqual(array_keys($terms), $result, 'Taxonomy terms were retrieved by entity query.');
|
||||
$tid = reset($result);
|
||||
$ids = (object) array(
|
||||
'entity_type' => 'taxonomy_term',
|
||||
'entity_id' => $tid,
|
||||
'bundle' => $this->vocabulary->id(),
|
||||
);
|
||||
$term = _field_create_entity_from_ids($ids);
|
||||
$this->assertEqual($term->id(), $tid, 'Taxonomy term can be created based on the IDs.');
|
||||
|
||||
// Create a second vocabulary and five more terms.
|
||||
$vocabulary2 = $this->createVocabulary();
|
||||
$terms2 = array();
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$term = $this->createTerm($vocabulary2);
|
||||
$terms2[$term->id()] = $term;
|
||||
}
|
||||
|
||||
$result = \Drupal::entityQuery('taxonomy_term')
|
||||
->condition('vid', $vocabulary2->id())
|
||||
->execute();
|
||||
sort($result);
|
||||
$this->assertEqual(array_keys($terms2), $result, format_string('Taxonomy terms from the %name vocabulary were retrieved by entity query.', array('%name' => $vocabulary2->label())));
|
||||
$tid = reset($result);
|
||||
$ids = (object) array(
|
||||
'entity_type' => 'taxonomy_term',
|
||||
'entity_id' => $tid,
|
||||
'bundle' => $vocabulary2->id(),
|
||||
);
|
||||
$term = _field_create_entity_from_ids($ids);
|
||||
$this->assertEqual($term->id(), $tid, 'Taxonomy term can be created based on the IDs.');
|
||||
}
|
||||
}
|
72
core/modules/taxonomy/src/Tests/LegacyTest.php
Normal file
72
core/modules/taxonomy/src/Tests/LegacyTest.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\LegacyTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Posts an article with a taxonomy term and a date prior to 1970.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class LegacyTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'datetime');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a tags vocabulary for the 'article' content type.
|
||||
$vocabulary = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => 'Tags',
|
||||
'vid' => 'tags',
|
||||
));
|
||||
$vocabulary->save();
|
||||
$field_name = 'field_' . $vocabulary->id();
|
||||
|
||||
$handler_settings = array(
|
||||
'target_bundles' => array(
|
||||
$vocabulary->id() => $vocabulary->id(),
|
||||
),
|
||||
'auto_create' => TRUE,
|
||||
);
|
||||
$this->createEntityReferenceField('node', 'article', $field_name, 'Tags', 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
|
||||
entity_get_form_display('node', 'article', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'entity_reference_autocomplete_tags',
|
||||
))
|
||||
->save();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer taxonomy', 'administer nodes', 'bypass node access']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test taxonomy functionality with nodes prior to 1970.
|
||||
*/
|
||||
function testTaxonomyLegacyNode() {
|
||||
// Posts an article with a taxonomy term and a date prior to 1970.
|
||||
$date = new DrupalDateTime('1969-01-01 00:00:00');
|
||||
$edit = array();
|
||||
$edit['title[0][value]'] = $this->randomMachineName();
|
||||
$edit['created[0][value][date]'] = $date->format('Y-m-d');
|
||||
$edit['created[0][value][time]'] = $date->format('H:i:s');
|
||||
$edit['body[0][value]'] = $this->randomMachineName();
|
||||
$edit['field_tags[target_id]'] = $this->randomMachineName();
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save and publish'));
|
||||
// Checks that the node has been saved.
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
$this->assertEqual($node->getCreatedTime(), $date->getTimestamp(), 'Legacy node was saved with the right date.');
|
||||
}
|
||||
}
|
66
core/modules/taxonomy/src/Tests/LoadMultipleTest.php
Normal file
66
core/modules/taxonomy/src/Tests/LoadMultipleTest.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\LoadMultipleTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
|
||||
/**
|
||||
* Tests the loading of multiple taxonomy terms at once.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class LoadMultipleTest extends TaxonomyTestBase {
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer taxonomy']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a vocabulary and some taxonomy terms, ensuring they're loaded
|
||||
* correctly using entity_load_multiple().
|
||||
*/
|
||||
function testTaxonomyTermMultipleLoad() {
|
||||
// Create a vocabulary.
|
||||
$vocabulary = $this->createVocabulary();
|
||||
|
||||
// Create five terms in the vocabulary.
|
||||
$i = 0;
|
||||
while ($i < 5) {
|
||||
$i++;
|
||||
$this->createTerm($vocabulary);
|
||||
}
|
||||
// Load the terms from the vocabulary.
|
||||
$terms = entity_load_multiple_by_properties('taxonomy_term', array('vid' => $vocabulary->id()));
|
||||
$count = count($terms);
|
||||
$this->assertEqual($count, 5, format_string('Correct number of terms were loaded. !count terms.', array('!count' => $count)));
|
||||
|
||||
// Load the same terms again by tid.
|
||||
$terms2 = Term::loadMultiple(array_keys($terms));
|
||||
$this->assertEqual($count, count($terms2), 'Five terms were loaded by tid.');
|
||||
$this->assertEqual($terms, $terms2, 'Both arrays contain the same terms.');
|
||||
|
||||
// Remove one term from the array, then delete it.
|
||||
$deleted = array_shift($terms2);
|
||||
$deleted->delete();
|
||||
$deleted_term = Term::load($deleted->id());
|
||||
$this->assertFalse($deleted_term);
|
||||
|
||||
// Load terms from the vocabulary by vid.
|
||||
$terms3 = entity_load_multiple_by_properties('taxonomy_term', array('vid' => $vocabulary->id()));
|
||||
$this->assertEqual(count($terms3), 4, 'Correct number of terms were loaded.');
|
||||
$this->assertFalse(isset($terms3[$deleted->id()]));
|
||||
|
||||
// Create a single term and load it by name.
|
||||
$term = $this->createTerm($vocabulary);
|
||||
$loaded_terms = entity_load_multiple_by_properties('taxonomy_term', array('name' => $term->getName()));
|
||||
$this->assertEqual(count($loaded_terms), 1, 'One term was loaded.');
|
||||
$loaded_term = reset($loaded_terms);
|
||||
$this->assertEqual($term->id(), $loaded_term->id(), 'Term loaded by name successfully.');
|
||||
}
|
||||
}
|
134
core/modules/taxonomy/src/Tests/RssTest.php
Normal file
134
core/modules/taxonomy/src/Tests/RssTest.php
Normal file
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\RssTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Ensure that data added as terms appears in RSS feeds if "RSS Category" format
|
||||
* is selected.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class RssTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'field_ui', 'views');
|
||||
|
||||
/**
|
||||
* Vocabulary for testing.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyInterface
|
||||
*/
|
||||
protected $vocabulary;
|
||||
|
||||
/**
|
||||
* Name of the taxonomy term reference field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer taxonomy', 'bypass node access', 'administer content types', 'administer node display']));
|
||||
$this->vocabulary = $this->createVocabulary();
|
||||
$this->fieldName = 'taxonomy_' . $this->vocabulary->id();
|
||||
|
||||
$handler_settings = array(
|
||||
'target_bundles' => array(
|
||||
$this->vocabulary->id() => $this->vocabulary->id(),
|
||||
),
|
||||
'auto_create' => TRUE,
|
||||
);
|
||||
$this->createEntityReferenceField('node', 'article', $this->fieldName, NULL, 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
|
||||
entity_get_form_display('node', 'article', 'default')
|
||||
->setComponent($this->fieldName, array(
|
||||
'type' => 'options_select',
|
||||
))
|
||||
->save();
|
||||
entity_get_display('node', 'article', 'default')
|
||||
->setComponent($this->fieldName, array(
|
||||
'type' => 'entity_reference_label',
|
||||
))
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that terms added to nodes are displayed in core RSS feed.
|
||||
*
|
||||
* Create a node and assert that taxonomy terms appear in rss.xml.
|
||||
*/
|
||||
function testTaxonomyRss() {
|
||||
// Create two taxonomy terms.
|
||||
$term1 = $this->createTerm($this->vocabulary);
|
||||
|
||||
// RSS display must be added manually.
|
||||
$this->drupalGet("admin/structure/types/manage/article/display");
|
||||
$edit = array(
|
||||
"display_modes_custom[rss]" => '1',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
// Change the format to 'RSS category'.
|
||||
$this->drupalGet("admin/structure/types/manage/article/display/rss");
|
||||
$edit = array(
|
||||
"fields[taxonomy_" . $this->vocabulary->id() . "][type]" => 'entity_reference_rss_category',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
// Post an article.
|
||||
$edit = array();
|
||||
$edit['title[0][value]'] = $this->randomMachineName();
|
||||
$edit[$this->fieldName . '[]'] = $term1->id();
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save'));
|
||||
|
||||
// Check that the term is displayed when the RSS feed is viewed.
|
||||
$this->drupalGet('rss.xml');
|
||||
$test_element = sprintf(
|
||||
'<category %s>%s</category>',
|
||||
'domain="' . $term1->url('canonical', array('absolute' => TRUE)) . '"',
|
||||
$term1->getName()
|
||||
);
|
||||
$this->assertRaw($test_element, 'Term is displayed when viewing the rss feed.');
|
||||
|
||||
// Test that the feed icon exists for the term.
|
||||
$this->drupalGet("taxonomy/term/{$term1->id()}");
|
||||
$this->assertLinkByHref("taxonomy/term/{$term1->id()}/feed");
|
||||
|
||||
// Test that the feed page exists for the term.
|
||||
$this->drupalGet("taxonomy/term/{$term1->id()}/feed");
|
||||
$this->assertRaw('<rss version="2.0"', "Feed page is RSS.");
|
||||
|
||||
// Check that the "Exception value" is disabled by default.
|
||||
$this->drupalGet('taxonomy/term/all/feed');
|
||||
$this->assertResponse(404);
|
||||
// Set the exception value to 'all'.
|
||||
$view = Views::getView('taxonomy_term');
|
||||
$arguments = $view->getDisplay()->getOption('arguments');
|
||||
$arguments['tid']['exception']['value'] = 'all';
|
||||
$view->getDisplay()->overrideOption('arguments', $arguments);
|
||||
$view->storage->save();
|
||||
// Check the article is shown in the feed.
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
$raw_xml = '<title>' . $node->label() . '</title>';
|
||||
$this->drupalGet('taxonomy/term/all/feed');
|
||||
$this->assertRaw($raw_xml, "Raw text '$raw_xml' is found.");
|
||||
// Unpublish the article and check that it is not shown in the feed.
|
||||
$node->setPublished(FALSE)->save();
|
||||
$this->drupalGet('taxonomy/term/all/feed');
|
||||
$this->assertNoRaw($raw_xml);
|
||||
}
|
||||
}
|
99
core/modules/taxonomy/src/Tests/TaxonomyImageTest.php
Normal file
99
core/modules/taxonomy/src/Tests/TaxonomyImageTest.php
Normal file
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\TaxonomyImageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests access checks of private image fields.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TaxonomyImageTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* Used taxonomy vocabulary.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyInterface
|
||||
*/
|
||||
protected $vocabulary;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('image');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Remove access content permission from registered users.
|
||||
user_role_revoke_permissions(RoleInterface::AUTHENTICATED_ID, array('access content'));
|
||||
|
||||
$this->vocabulary = $this->createVocabulary();
|
||||
// Add a field to the vocabulary.
|
||||
$entity_type = 'taxonomy_term';
|
||||
$name = 'field_test';
|
||||
entity_create('field_storage_config', array(
|
||||
'field_name' => $name,
|
||||
'entity_type' => $entity_type,
|
||||
'type' => 'image',
|
||||
'settings' => array(
|
||||
'uri_scheme' => 'private',
|
||||
),
|
||||
))->save();
|
||||
entity_create('field_config', array(
|
||||
'field_name' => $name,
|
||||
'entity_type' => $entity_type,
|
||||
'bundle' => $this->vocabulary->id(),
|
||||
'settings' => array(),
|
||||
))->save();
|
||||
entity_get_display($entity_type, $this->vocabulary->id(), 'default')
|
||||
->setComponent($name, array(
|
||||
'type' => 'image',
|
||||
'settings' => array(),
|
||||
))
|
||||
->save();
|
||||
entity_get_form_display($entity_type, $this->vocabulary->id(), 'default')
|
||||
->setComponent($name, array(
|
||||
'type' => 'image_image',
|
||||
'settings' => array(),
|
||||
))
|
||||
->save();
|
||||
}
|
||||
|
||||
public function testTaxonomyImageAccess() {
|
||||
$user = $this->drupalCreateUser(array('administer site configuration', 'administer taxonomy', 'access user profiles'));
|
||||
$this->drupalLogin($user);
|
||||
|
||||
// Create a term and upload the image.
|
||||
$files = $this->drupalGetTestFiles('image');
|
||||
$image = array_pop($files);
|
||||
$edit['name[0][value]'] = $this->randomMachineName();
|
||||
$edit['files[field_test_0]'] = drupal_realpath($image->uri);
|
||||
$this->drupalPostForm('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add', $edit, t('Save'));
|
||||
$this->drupalPostForm(NULL, ['field_test[0][alt]' => $this->randomMachineName()], t('Save'));
|
||||
$terms = entity_load_multiple_by_properties('taxonomy_term', array('name' => $edit['name[0][value]']));
|
||||
$term = reset($terms);
|
||||
$this->assertText(t('Created new term @name.', array('@name' => $term->getName())));
|
||||
|
||||
// Create a user that should have access to the file and one that doesn't.
|
||||
$access_user = $this->drupalCreateUser(array('access content'));
|
||||
$no_access_user = $this->drupalCreateUser();
|
||||
$image = file_load($term->field_test->target_id);
|
||||
$this->drupalLogin($access_user);
|
||||
$this->drupalGet(file_create_url($image->getFileUri()));
|
||||
$this->assertResponse(200, 'Private image on term is accessible with right permission');
|
||||
|
||||
$this->drupalLogin($no_access_user);
|
||||
$this->drupalGet(file_create_url($image->getFileUri()));
|
||||
$this->assertResponse(403, 'Private image on term not accessible without right permission');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\TaxonomyTermIndentationTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
/**
|
||||
* Ensure that the term indentation works properly.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TaxonomyTermIndentationTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('taxonomy');
|
||||
|
||||
/**
|
||||
* Vocabulary for testing.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyInterface
|
||||
*/
|
||||
protected $vocabulary;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer taxonomy', 'bypass node access']));
|
||||
$this->vocabulary = $this->createVocabulary();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests term indentation.
|
||||
*/
|
||||
function testTermIndentation() {
|
||||
// Create three taxonomy terms.
|
||||
$term1 = $this->createTerm($this->vocabulary);
|
||||
$term2 = $this->createTerm($this->vocabulary);
|
||||
$term3 = $this->createTerm($this->vocabulary);
|
||||
|
||||
// Get the taxonomy storage.
|
||||
$taxonomy_storage = $this->container->get('entity.manager')->getStorage('taxonomy_term');
|
||||
|
||||
// Indent the second term under the first one.
|
||||
$edit = array(
|
||||
'terms[tid:' . $term2->id() . ':0][term][tid]' => 2,
|
||||
'terms[tid:' . $term2->id() . ':0][term][parent]' => 1,
|
||||
'terms[tid:' . $term2->id() . ':0][term][depth]' => 1,
|
||||
'terms[tid:' . $term2->id() . ':0][weight]' => 1,
|
||||
);
|
||||
|
||||
// Submit the edited form and check for HTML indentation element presence.
|
||||
$this->drupalPostForm('admin/structure/taxonomy/manage/' . $this->vocabulary->get('vid') . '/overview', $edit, t('Save'));
|
||||
$this->assertPattern('|<div class="js-indentation indentation"> </div>|');
|
||||
|
||||
// Check explicitly that term 2's parent is term 1.
|
||||
$parents = $taxonomy_storage->loadParents($term2->id());
|
||||
$this->assertEqual(key($parents), 1, 'Term 1 is the term 2\'s parent');
|
||||
|
||||
// Move the second term back out to the root level.
|
||||
$edit = array(
|
||||
'terms[tid:' . $term2->id() . ':0][term][tid]' => 2,
|
||||
'terms[tid:' . $term2->id() . ':0][term][parent]' => 0,
|
||||
'terms[tid:' . $term2->id() . ':0][term][depth]' => 0,
|
||||
'terms[tid:' . $term2->id() . ':0][weight]' => 1,
|
||||
);
|
||||
|
||||
$this->drupalPostForm('admin/structure/taxonomy/manage/' . $this->vocabulary->get('vid' ) . '/overview', $edit, t('Save'));
|
||||
// All terms back at the root level, no indentation should be present.
|
||||
$this->assertNoPattern('|<div class="js-indentation indentation"> </div>|');
|
||||
|
||||
// Check explicitly that term 2 has no parents.
|
||||
\Drupal::entityManager()->getStorage('taxonomy_term')->resetCache();
|
||||
$parents = $taxonomy_storage->loadParents($term2->id());
|
||||
$this->assertTrue(empty($parents), 'Term 2 has no parents now');
|
||||
}
|
||||
|
||||
}
|
72
core/modules/taxonomy/src/Tests/TaxonomyTermPagerTest.php
Normal file
72
core/modules/taxonomy/src/Tests/TaxonomyTermPagerTest.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\TaxonomyTermPagerTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
/**
|
||||
* Ensures that the term pager works properly.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TaxonomyTermPagerTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['taxonomy'];
|
||||
|
||||
/**
|
||||
* Vocabulary for testing.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyInterface
|
||||
*/
|
||||
protected $vocabulary;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer taxonomy', 'bypass node access']));
|
||||
$this->vocabulary = $this->createVocabulary();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the pager is displayed properly on the term overview page.
|
||||
*/
|
||||
public function testTaxonomyTermOverviewPager() {
|
||||
// Set limit to 3 terms per page.
|
||||
$this->config('taxonomy.settings')
|
||||
->set('terms_per_page_admin', '3')
|
||||
->save();
|
||||
|
||||
// Create 3 terms.
|
||||
for ($x = 1; $x <= 3; $x++) {
|
||||
$this->createTerm($this->vocabulary);
|
||||
}
|
||||
|
||||
// Get Page 1.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
|
||||
$this->assertNoPattern('|<nav class="pager" [^>]*>|', 'Pager is not visible on page 1');
|
||||
|
||||
// Create 3 more terms to show pager.
|
||||
for ($x = 1; $x <= 3; $x++) {
|
||||
$this->createTerm($this->vocabulary);
|
||||
}
|
||||
|
||||
// Get Page 1.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
|
||||
$this->assertPattern('|<nav class="pager" [^>]*>|', 'Pager is visible on page 1');
|
||||
|
||||
// Get Page 2.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview', ['query' => ['page' => 1]]);
|
||||
$this->assertPattern('|<nav class="pager" [^>]*>|', 'Pager is visible on page 2');
|
||||
}
|
||||
|
||||
}
|
40
core/modules/taxonomy/src/Tests/TaxonomyTestBase.php
Normal file
40
core/modules/taxonomy/src/Tests/TaxonomyTestBase.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\TaxonomyTestBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\entity_reference\Tests\EntityReferenceTestTrait;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Provides common helper methods for Taxonomy module tests.
|
||||
*/
|
||||
abstract class TaxonomyTestBase extends WebTestBase {
|
||||
|
||||
use TaxonomyTestTrait;
|
||||
use EntityReferenceTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('taxonomy', 'block');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalPlaceBlock('system_breadcrumb_block');
|
||||
|
||||
// Create Basic page and Article node types.
|
||||
if ($this->profile != 'standard') {
|
||||
$this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
|
||||
}
|
||||
}
|
||||
}
|
63
core/modules/taxonomy/src/Tests/TaxonomyTestTrait.php
Normal file
63
core/modules/taxonomy/src/Tests/TaxonomyTestTrait.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\TaxonomyTestTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
|
||||
/**
|
||||
* Provides common helper methods for Taxonomy module tests.
|
||||
*/
|
||||
trait TaxonomyTestTrait {
|
||||
|
||||
/**
|
||||
* Returns a new vocabulary with random properties.
|
||||
*/
|
||||
function createVocabulary() {
|
||||
// Create a vocabulary.
|
||||
$vocabulary = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'description' => $this->randomMachineName(),
|
||||
'vid' => Unicode::strtolower($this->randomMachineName()),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
'weight' => mt_rand(0, 10),
|
||||
));
|
||||
$vocabulary->save();
|
||||
return $vocabulary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new term with random properties in vocabulary $vid.
|
||||
*
|
||||
* @param \Drupal\taxonomy\Entity\Vocabulary $vocabulary
|
||||
* The vocabulary object.
|
||||
* @param array $values
|
||||
* (optional) An array of values to set, keyed by property name. If the
|
||||
* entity type has bundles, the bundle key has to be specified.
|
||||
*
|
||||
* @return \Drupal\taxonomy\Entity\Term
|
||||
* The new taxonomy term object.
|
||||
*/
|
||||
function createTerm(Vocabulary $vocabulary, $values = array()) {
|
||||
$filter_formats = filter_formats();
|
||||
$format = array_pop($filter_formats);
|
||||
$term = entity_create('taxonomy_term', $values + array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'description' => array(
|
||||
'value' => $this->randomMachineName(),
|
||||
// Use the first available text format.
|
||||
'format' => $format->id(),
|
||||
),
|
||||
'vid' => $vocabulary->id(),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
));
|
||||
$term->save();
|
||||
return $term;
|
||||
}
|
||||
}
|
106
core/modules/taxonomy/src/Tests/TaxonomyTranslationTestTrait.php
Normal file
106
core/modules/taxonomy/src/Tests/TaxonomyTranslationTestTrait.php
Normal file
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\TaxonomyTranslationTestTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\entity_reference\Tests\EntityReferenceTestTrait;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Provides common testing base for translated taxonomy terms.
|
||||
*/
|
||||
trait TaxonomyTranslationTestTrait {
|
||||
|
||||
use EntityReferenceTestTrait;
|
||||
|
||||
/**
|
||||
* The vocabulary.
|
||||
*
|
||||
* @var \Drupal\taxonomy\Entity\Vocabulary;
|
||||
*/
|
||||
protected $vocabulary;
|
||||
|
||||
/**
|
||||
* The field name for our taxonomy term field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $termFieldName = 'field_tag';
|
||||
|
||||
/**
|
||||
* The langcode of the source language.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseLangcode = 'en';
|
||||
|
||||
/**
|
||||
* Target langcode for translation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $translateToLangcode = 'hu';
|
||||
|
||||
/**
|
||||
* The node to check the translated value on.
|
||||
*
|
||||
* @var \Drupal\node\Entity\Node
|
||||
*/
|
||||
protected $node;
|
||||
|
||||
/**
|
||||
* Adds additional languages.
|
||||
*/
|
||||
protected function setupLanguages() {
|
||||
ConfigurableLanguage::createFromLangcode($this->translateToLangcode)->save();
|
||||
$this->rebuildContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables translations where it needed.
|
||||
*/
|
||||
protected function enableTranslation() {
|
||||
// Enable translation for the current entity type and ensure the change is
|
||||
// picked up.
|
||||
\Drupal::service('content_translation.manager')->setEnabled('node', 'article', TRUE);
|
||||
\Drupal::service('content_translation.manager')->setEnabled('taxonomy_term', $this->vocabulary->id(), TRUE);
|
||||
drupal_static_reset();
|
||||
\Drupal::entityManager()->clearCachedDefinitions();
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
\Drupal::service('entity.definition_update_manager')->applyUpdates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds term reference field for the article content type.
|
||||
*/
|
||||
protected function setUpTermReferenceField() {
|
||||
$handler_settings = array(
|
||||
'target_bundles' => array(
|
||||
$this->vocabulary->id() => $this->vocabulary->id(),
|
||||
),
|
||||
'auto_create' => TRUE,
|
||||
);
|
||||
$this->createEntityReferenceField('node', 'article', $this->termFieldName, NULL, 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
$field_storage = FieldStorageConfig::loadByName('node', $this->termFieldName);
|
||||
$field_storage->setTranslatable(FALSE);
|
||||
$field_storage->save();
|
||||
|
||||
entity_get_form_display('node', 'article', 'default')
|
||||
->setComponent($this->termFieldName, array(
|
||||
'type' => 'entity_reference_autocomplete_tags',
|
||||
))
|
||||
->save();
|
||||
entity_get_display('node', 'article', 'default')
|
||||
->setComponent($this->termFieldName, array(
|
||||
'type' => 'entity_reference_label',
|
||||
))
|
||||
->save();
|
||||
}
|
||||
|
||||
}
|
45
core/modules/taxonomy/src/Tests/TermCacheTagsTest.php
Normal file
45
core/modules/taxonomy/src/Tests/TermCacheTagsTest.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\TermCacheTagsTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\system\Tests\Entity\EntityWithUriCacheTagsTestBase;
|
||||
|
||||
/**
|
||||
* Tests the Taxonomy term entity's cache tags.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TermCacheTagsTest extends EntityWithUriCacheTagsTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('taxonomy');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
// Create a "Camelids" vocabulary.
|
||||
$vocabulary = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => 'Camelids',
|
||||
'vid' => 'camelids',
|
||||
));
|
||||
$vocabulary->save();
|
||||
|
||||
// Create a "Llama" taxonomy term.
|
||||
$term = entity_create('taxonomy_term', array(
|
||||
'name' => 'Llama',
|
||||
'vid' => $vocabulary->id(),
|
||||
));
|
||||
$term->save();
|
||||
|
||||
return $term;
|
||||
}
|
||||
|
||||
}
|
83
core/modules/taxonomy/src/Tests/TermEntityReferenceTest.php
Normal file
83
core/modules/taxonomy/src/Tests/TermEntityReferenceTest.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\TermEntityReferenceTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
|
||||
/**
|
||||
* Tests the settings of restricting term selection to a single vocabulary.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TermEntityReferenceTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('entity_reference', 'entity_reference_test', 'entity_test');
|
||||
|
||||
/**
|
||||
* Tests an entity reference field restricted to a single vocabulary.
|
||||
*
|
||||
* Creates two vocabularies with a term, then set up the entity reference
|
||||
* field to limit the target vocabulary to one of them, ensuring that
|
||||
* the restriction applies.
|
||||
*/
|
||||
function testSelectionTestVocabularyRestriction() {
|
||||
|
||||
// Create two vocabularies.
|
||||
$vocabulary = $this->createVocabulary();
|
||||
$vocabulary2 = $this->createVocabulary();
|
||||
|
||||
$term = $this->createTerm($vocabulary);
|
||||
$term2 = $this->createTerm($vocabulary2);
|
||||
|
||||
// Create an entity reference field.
|
||||
$field_name = 'taxonomy_' . $vocabulary->id();
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'translatable' => FALSE,
|
||||
'settings' => array(
|
||||
'target_type' => 'taxonomy_term',
|
||||
),
|
||||
'type' => 'entity_reference',
|
||||
'cardinality' => 1,
|
||||
));
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create(array(
|
||||
'field_storage' => $field_storage,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'test_bundle',
|
||||
'settings' => array(
|
||||
'handler' => 'default',
|
||||
'handler_settings' => array(
|
||||
// Restrict selection of terms to a single vocabulary.
|
||||
'target_bundles' => array(
|
||||
$vocabulary->id() => $vocabulary->id(),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
$field->save();
|
||||
|
||||
$handler = $this->container->get('plugin.manager.entity_reference_selection')->getSelectionHandler($field);
|
||||
$result = $handler->getReferenceableEntities();
|
||||
|
||||
$expected_result = array(
|
||||
$vocabulary->id() => array(
|
||||
$term->id() => $term->getName(),
|
||||
),
|
||||
);
|
||||
|
||||
$this->assertIdentical($result, $expected_result, 'Terms selection restricted to a single vocabulary.');
|
||||
}
|
||||
}
|
218
core/modules/taxonomy/src/Tests/TermIndexTest.php
Normal file
218
core/modules/taxonomy/src/Tests/TermIndexTest.php
Normal file
|
@ -0,0 +1,218 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\TermIndexTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Tests the hook implementations that maintain the taxonomy index.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TermIndexTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('views');
|
||||
|
||||
/**
|
||||
* Vocabulary for testing.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyInterface
|
||||
*/
|
||||
protected $vocabulary;
|
||||
|
||||
/**
|
||||
* Name of the taxonomy term reference field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName1;
|
||||
|
||||
/**
|
||||
* Name of the taxonomy term reference field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName2;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create an administrative user.
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer taxonomy', 'bypass node access']));
|
||||
|
||||
// Create a vocabulary and add two term reference fields to article nodes.
|
||||
$this->vocabulary = $this->createVocabulary();
|
||||
|
||||
$this->fieldName1 = Unicode::strtolower($this->randomMachineName());
|
||||
$handler_settings = array(
|
||||
'target_bundles' => array(
|
||||
$this->vocabulary->id() => $this->vocabulary->id(),
|
||||
),
|
||||
'auto_create' => TRUE,
|
||||
);
|
||||
$this->createEntityReferenceField('node', 'article', $this->fieldName1, NULL, 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
|
||||
entity_get_form_display('node', 'article', 'default')
|
||||
->setComponent($this->fieldName1, array(
|
||||
'type' => 'options_select',
|
||||
))
|
||||
->save();
|
||||
entity_get_display('node', 'article', 'default')
|
||||
->setComponent($this->fieldName1, array(
|
||||
'type' => 'entity_reference_label',
|
||||
))
|
||||
->save();
|
||||
|
||||
$this->fieldName2 = Unicode::strtolower($this->randomMachineName());
|
||||
$this->createEntityReferenceField('node', 'article', $this->fieldName2, NULL, 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
|
||||
entity_get_form_display('node', 'article', 'default')
|
||||
->setComponent($this->fieldName2, array(
|
||||
'type' => 'options_select',
|
||||
))
|
||||
->save();
|
||||
entity_get_display('node', 'article', 'default')
|
||||
->setComponent($this->fieldName2, array(
|
||||
'type' => 'entity_reference_label',
|
||||
))
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the taxonomy index is maintained properly.
|
||||
*/
|
||||
function testTaxonomyIndex() {
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
// Create terms in the vocabulary.
|
||||
$term_1 = $this->createTerm($this->vocabulary);
|
||||
$term_2 = $this->createTerm($this->vocabulary);
|
||||
|
||||
// Post an article.
|
||||
$edit = array();
|
||||
$edit['title[0][value]'] = $this->randomMachineName();
|
||||
$edit['body[0][value]'] = $this->randomMachineName();
|
||||
$edit["{$this->fieldName1}[]"] = $term_1->id();
|
||||
$edit["{$this->fieldName2}[]"] = $term_1->id();
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save'));
|
||||
|
||||
// Check that the term is indexed, and only once.
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
$index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
|
||||
':nid' => $node->id(),
|
||||
':tid' => $term_1->id(),
|
||||
))->fetchField();
|
||||
$this->assertEqual(1, $index_count, 'Term 1 is indexed once.');
|
||||
|
||||
// Update the article to change one term.
|
||||
$edit["{$this->fieldName1}[]"] = $term_2->id();
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
// Check that both terms are indexed.
|
||||
$index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
|
||||
':nid' => $node->id(),
|
||||
':tid' => $term_1->id(),
|
||||
))->fetchField();
|
||||
$this->assertEqual(1, $index_count, 'Term 1 is indexed.');
|
||||
$index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
|
||||
':nid' => $node->id(),
|
||||
':tid' => $term_2->id(),
|
||||
))->fetchField();
|
||||
$this->assertEqual(1, $index_count, 'Term 2 is indexed.');
|
||||
|
||||
// Update the article to change another term.
|
||||
$edit["{$this->fieldName2}[]"] = $term_2->id();
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
// Check that only one term is indexed.
|
||||
$index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
|
||||
':nid' => $node->id(),
|
||||
':tid' => $term_1->id(),
|
||||
))->fetchField();
|
||||
$this->assertEqual(0, $index_count, 'Term 1 is not indexed.');
|
||||
$index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
|
||||
':nid' => $node->id(),
|
||||
':tid' => $term_2->id(),
|
||||
))->fetchField();
|
||||
$this->assertEqual(1, $index_count, 'Term 2 is indexed once.');
|
||||
|
||||
// Redo the above tests without interface.
|
||||
$node_storage->resetCache(array($node->id()));
|
||||
$node = $node_storage->load($node->id());
|
||||
$node->title = $this->randomMachineName();
|
||||
|
||||
// Update the article with no term changed.
|
||||
$node->save();
|
||||
|
||||
// Check that the index was not changed.
|
||||
$index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
|
||||
':nid' => $node->id(),
|
||||
':tid' => $term_1->id(),
|
||||
))->fetchField();
|
||||
$this->assertEqual(0, $index_count, 'Term 1 is not indexed.');
|
||||
$index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
|
||||
':nid' => $node->id(),
|
||||
':tid' => $term_2->id(),
|
||||
))->fetchField();
|
||||
$this->assertEqual(1, $index_count, 'Term 2 is indexed once.');
|
||||
|
||||
// Update the article to change one term.
|
||||
$node->{$this->fieldName1} = array(array('target_id' => $term_1->id()));
|
||||
$node->save();
|
||||
|
||||
// Check that both terms are indexed.
|
||||
$index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
|
||||
':nid' => $node->id(),
|
||||
':tid' => $term_1->id(),
|
||||
))->fetchField();
|
||||
$this->assertEqual(1, $index_count, 'Term 1 is indexed.');
|
||||
$index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
|
||||
':nid' => $node->id(),
|
||||
':tid' => $term_2->id(),
|
||||
))->fetchField();
|
||||
$this->assertEqual(1, $index_count, 'Term 2 is indexed.');
|
||||
|
||||
// Update the article to change another term.
|
||||
$node->{$this->fieldName2} = array(array('target_id' => $term_1->id()));
|
||||
$node->save();
|
||||
|
||||
// Check that only one term is indexed.
|
||||
$index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
|
||||
':nid' => $node->id(),
|
||||
':tid' => $term_1->id(),
|
||||
))->fetchField();
|
||||
$this->assertEqual(1, $index_count, 'Term 1 is indexed once.');
|
||||
$index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
|
||||
':nid' => $node->id(),
|
||||
':tid' => $term_2->id(),
|
||||
))->fetchField();
|
||||
$this->assertEqual(0, $index_count, 'Term 2 is not indexed.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that there is a link to the parent term on the child term page.
|
||||
*/
|
||||
function testTaxonomyTermHierarchyBreadcrumbs() {
|
||||
// Create two taxonomy terms and set term2 as the parent of term1.
|
||||
$term1 = $this->createTerm($this->vocabulary);
|
||||
$term2 = $this->createTerm($this->vocabulary);
|
||||
$term1->parent = array($term2->id());
|
||||
$term1->save();
|
||||
|
||||
// Verify that the page breadcrumbs include a link to the parent term.
|
||||
$this->drupalGet('taxonomy/term/' . $term1->id());
|
||||
// Breadcrumbs are not rendered with a language, prevent the term
|
||||
// language from being added to the options.
|
||||
$this->assertRaw(\Drupal::l($term2->getName(), $term2->urlInfo('canonical', ['language' => NULL])), 'Parent term link is displayed when viewing the node.');
|
||||
}
|
||||
}
|
156
core/modules/taxonomy/src/Tests/TermKernelTest.php
Normal file
156
core/modules/taxonomy/src/Tests/TermKernelTest.php
Normal file
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\TermKernelTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\taxonomy\Tests\TaxonomyTestTrait;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Kernel tests for taxonomy term functions.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TermKernelTest extends KernelTestBase {
|
||||
|
||||
use TaxonomyTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array( 'filter', 'taxonomy', 'text', 'user' );
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(array('filter'));
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
}
|
||||
|
||||
/**
|
||||
* Deleting terms should also remove related vocabulary.
|
||||
* Deleting an invalid term should silently fail.
|
||||
*/
|
||||
public function testTermDelete() {
|
||||
$vocabulary = $this->createVocabulary();
|
||||
$valid_term = $this->createTerm($vocabulary);
|
||||
// Delete a valid term.
|
||||
$valid_term->delete();
|
||||
$terms = entity_load_multiple_by_properties('taxonomy_term', array('vid' => $vocabulary->id()));
|
||||
$this->assertTrue(empty($terms), 'Vocabulary is empty after deletion');
|
||||
|
||||
// Delete an invalid term. Should not throw any notices.
|
||||
entity_delete_multiple('taxonomy_term', array(42));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deleting a parent of a term with multiple parents does not delete the term.
|
||||
*/
|
||||
public function testMultipleParentDelete() {
|
||||
$vocabulary = $this->createVocabulary();
|
||||
$parent_term1 = $this->createTerm($vocabulary);
|
||||
$parent_term2 = $this->createTerm($vocabulary);
|
||||
$child_term = $this->createTerm($vocabulary);
|
||||
$child_term->parent = array($parent_term1->id(), $parent_term2->id());
|
||||
$child_term->save();
|
||||
$child_term_id = $child_term->id();
|
||||
|
||||
$parent_term1->delete();
|
||||
$term_storage = $this->container->get('entity.manager')->getStorage('taxonomy_term');
|
||||
$term_storage->resetCache(array($child_term_id));
|
||||
$child_term = Term::load($child_term_id);
|
||||
$this->assertTrue(!empty($child_term), 'Child term is not deleted if only one of its parents is removed.');
|
||||
|
||||
$parent_term2->delete();
|
||||
$term_storage->resetCache(array($child_term_id));
|
||||
$child_term = Term::load($child_term_id);
|
||||
$this->assertTrue(empty($child_term), 'Child term is deleted if all of its parents are removed.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a taxonomy with terms that have multiple parents of different depths.
|
||||
*/
|
||||
public function testTaxonomyVocabularyTree() {
|
||||
// Create a new vocabulary with 6 terms.
|
||||
$vocabulary = $this->createVocabulary();
|
||||
$term = array();
|
||||
for ($i = 0; $i < 6; $i++) {
|
||||
$term[$i] = $this->createTerm($vocabulary);
|
||||
}
|
||||
|
||||
// Get the taxonomy storage.
|
||||
$taxonomy_storage = $this->container->get('entity.manager')->getStorage('taxonomy_term');
|
||||
|
||||
// Set the weight on $term[1] so it appears before $term[5] when fetching
|
||||
// the parents for $term[2], in order to test for a regression on
|
||||
// \Drupal\taxonomy\TermStorageInterface::loadAllParents().
|
||||
$term[1]->weight = -1;
|
||||
$term[1]->save();
|
||||
|
||||
// $term[2] is a child of 1 and 5.
|
||||
$term[2]->parent = array($term[1]->id(), $term[5]->id());
|
||||
$term[2]->save();
|
||||
// $term[3] is a child of 2.
|
||||
$term[3]->parent = array($term[2]->id());
|
||||
$term[3]->save();
|
||||
// $term[5] is a child of 4.
|
||||
$term[5]->parent = array($term[4]->id());
|
||||
$term[5]->save();
|
||||
|
||||
/**
|
||||
* Expected tree:
|
||||
* term[0] | depth: 0
|
||||
* term[1] | depth: 0
|
||||
* -- term[2] | depth: 1
|
||||
* ---- term[3] | depth: 2
|
||||
* term[4] | depth: 0
|
||||
* -- term[5] | depth: 1
|
||||
* ---- term[2] | depth: 2
|
||||
* ------ term[3] | depth: 3
|
||||
*/
|
||||
// Count $term[1] parents with $max_depth = 1.
|
||||
$tree = $taxonomy_storage->loadTree($vocabulary->id(), $term[1]->id(), 1);
|
||||
$this->assertEqual(1, count($tree), 'We have one parent with depth 1.');
|
||||
|
||||
// Count all vocabulary tree elements.
|
||||
$tree = $taxonomy_storage->loadTree($vocabulary->id());
|
||||
$this->assertEqual(8, count($tree), 'We have all vocabulary tree elements.');
|
||||
|
||||
// Count elements in every tree depth.
|
||||
foreach ($tree as $element) {
|
||||
if (!isset($depth_count[$element->depth])) {
|
||||
$depth_count[$element->depth] = 0;
|
||||
}
|
||||
$depth_count[$element->depth]++;
|
||||
}
|
||||
$this->assertEqual(3, $depth_count[0], 'Three elements in taxonomy tree depth 0.');
|
||||
$this->assertEqual(2, $depth_count[1], 'Two elements in taxonomy tree depth 1.');
|
||||
$this->assertEqual(2, $depth_count[2], 'Two elements in taxonomy tree depth 2.');
|
||||
$this->assertEqual(1, $depth_count[3], 'One element in taxonomy tree depth 3.');
|
||||
|
||||
/** @var \Drupal\taxonomy\TermStorageInterface $storage */
|
||||
$storage = \Drupal::entityManager()->getStorage('taxonomy_term');
|
||||
// Count parents of $term[2].
|
||||
$parents = $storage->loadParents($term[2]->id());
|
||||
$this->assertEqual(2, count($parents), 'The term has two parents.');
|
||||
|
||||
// Count parents of $term[3].
|
||||
$parents = $storage->loadParents($term[3]->id());
|
||||
$this->assertEqual(1, count($parents), 'The term has one parent.');
|
||||
|
||||
// Identify all ancestors of $term[2].
|
||||
$ancestors = $storage->loadAllParents($term[2]->id());
|
||||
$this->assertEqual(4, count($ancestors), 'The term has four ancestors including the term itself.');
|
||||
|
||||
// Identify all ancestors of $term[3].
|
||||
$ancestors = $storage->loadAllParents($term[3]->id());
|
||||
$this->assertEqual(5, count($ancestors), 'The term has five ancestors including the term itself.');
|
||||
}
|
||||
}
|
114
core/modules/taxonomy/src/Tests/TermLanguageTest.php
Normal file
114
core/modules/taxonomy/src/Tests/TermLanguageTest.php
Normal file
|
@ -0,0 +1,114 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\TermLanguageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests the language functionality for the taxonomy terms.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TermLanguageTest extends TaxonomyTestBase {
|
||||
|
||||
public static $modules = array('language');
|
||||
|
||||
/**
|
||||
* Vocabulary for testing.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyInterface
|
||||
*/
|
||||
protected $vocabulary;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create an administrative user.
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer taxonomy']));
|
||||
|
||||
// Create a vocabulary to which the terms will be assigned.
|
||||
$this->vocabulary = $this->createVocabulary();
|
||||
|
||||
// Add some custom languages.
|
||||
foreach (array('aa', 'bb', 'cc') as $language_code) {
|
||||
ConfigurableLanguage::create(array(
|
||||
'id' => $language_code,
|
||||
'label' => $this->randomMachineName(),
|
||||
))->save();
|
||||
}
|
||||
}
|
||||
|
||||
function testTermLanguage() {
|
||||
// Configure the vocabulary to not hide the language selector.
|
||||
$edit = array(
|
||||
'default_language[language_alterable]' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/taxonomy/manage/' . $this->vocabulary->id(), $edit, t('Save'));
|
||||
|
||||
// Add a term.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
|
||||
// Check that we have the language selector.
|
||||
$this->assertField('edit-langcode-0-value', t('The language selector field was found on the page.'));
|
||||
// Submit the term.
|
||||
$edit = array(
|
||||
'name[0][value]' => $this->randomMachineName(),
|
||||
'langcode[0][value]' => 'aa',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$terms = taxonomy_term_load_multiple_by_name($edit['name[0][value]']);
|
||||
$term = reset($terms);
|
||||
$this->assertEqual($term->language()->getId(), $edit['langcode[0][value]'], 'The term contains the correct langcode.');
|
||||
|
||||
// Check if on the edit page the language is correct.
|
||||
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
|
||||
$this->assertOptionSelected('edit-langcode-0-value', $edit['langcode[0][value]'], 'The term language was correctly selected.');
|
||||
|
||||
// Change the language of the term.
|
||||
$edit['langcode[0][value]'] = 'bb';
|
||||
$this->drupalPostForm('taxonomy/term/' . $term->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
// Check again that on the edit page the language is correct.
|
||||
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
|
||||
$this->assertOptionSelected('edit-langcode-0-value', $edit['langcode[0][value]'], 'The term language was correctly selected.');
|
||||
}
|
||||
|
||||
function testDefaultTermLanguage() {
|
||||
// Configure the vocabulary to not hide the language selector, and make the
|
||||
// default language of the terms fixed.
|
||||
$edit = array(
|
||||
'default_language[langcode]' => 'bb',
|
||||
'default_language[language_alterable]' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/taxonomy/manage/' . $this->vocabulary->id(), $edit, t('Save'));
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
|
||||
$this->assertOptionSelected('edit-langcode-0-value', 'bb', 'The expected langcode was selected.');
|
||||
|
||||
// Make the default language of the terms to be the current interface.
|
||||
$edit = array(
|
||||
'default_language[langcode]' => 'current_interface',
|
||||
'default_language[language_alterable]' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/taxonomy/manage/' . $this->vocabulary->id(), $edit, t('Save'));
|
||||
$this->drupalGet('aa/admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
|
||||
$this->assertOptionSelected('edit-langcode-0-value', 'aa', "The expected langcode, 'aa', was selected.");
|
||||
$this->drupalGet('bb/admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
|
||||
$this->assertOptionSelected('edit-langcode-0-value', 'bb', "The expected langcode, 'bb', was selected.");
|
||||
|
||||
// Change the default language of the site and check if the default terms
|
||||
// language is still correctly selected.
|
||||
$this->config('system.site')->set('default_langcode', 'cc')->save();
|
||||
$edit = array(
|
||||
'default_language[langcode]' => LanguageInterface::LANGCODE_SITE_DEFAULT,
|
||||
'default_language[language_alterable]' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/taxonomy/manage/' . $this->vocabulary->id(), $edit, t('Save'));
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add');
|
||||
$this->assertOptionSelected('edit-langcode-0-value', 'cc', "The expected langcode, 'cc', was selected.");
|
||||
}
|
||||
}
|
542
core/modules/taxonomy/src/Tests/TermTest.php
Normal file
542
core/modules/taxonomy/src/Tests/TermTest.php
Normal file
|
@ -0,0 +1,542 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\TermTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Tags;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
|
||||
/**
|
||||
* Tests load, save and delete for taxonomy terms.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TermTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* Vocabulary for testing.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyInterface
|
||||
*/
|
||||
protected $vocabulary;
|
||||
|
||||
/**
|
||||
* Taxonomy term reference field for testing.
|
||||
*
|
||||
* @var \Drupal\field\FieldConfigInterface
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer taxonomy', 'bypass node access']));
|
||||
$this->vocabulary = $this->createVocabulary();
|
||||
|
||||
$field_name = 'taxonomy_' . $this->vocabulary->id();
|
||||
|
||||
$handler_settings = array(
|
||||
'target_bundles' => array(
|
||||
$this->vocabulary->id() => $this->vocabulary->id(),
|
||||
),
|
||||
'auto_create' => TRUE,
|
||||
);
|
||||
$this->createEntityReferenceField('node', 'article', $field_name, NULL, 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
$this->field = FieldConfig::loadByName('node', 'article', $field_name);
|
||||
|
||||
entity_get_form_display('node', 'article', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'options_select',
|
||||
))
|
||||
->save();
|
||||
entity_get_display('node', 'article', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'entity_reference_label',
|
||||
))
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test terms in a single and multiple hierarchy.
|
||||
*/
|
||||
function testTaxonomyTermHierarchy() {
|
||||
// Create two taxonomy terms.
|
||||
$term1 = $this->createTerm($this->vocabulary);
|
||||
$term2 = $this->createTerm($this->vocabulary);
|
||||
|
||||
// Get the taxonomy storage.
|
||||
$taxonomy_storage = $this->container->get('entity.manager')->getStorage('taxonomy_term');
|
||||
|
||||
// Check that hierarchy is flat.
|
||||
$vocabulary = Vocabulary::load($this->vocabulary->id());
|
||||
$this->assertEqual(0, $vocabulary->getHierarchy(), 'Vocabulary is flat.');
|
||||
|
||||
// Edit $term2, setting $term1 as parent.
|
||||
$edit = array();
|
||||
$edit['parent[]'] = array($term1->id());
|
||||
$this->drupalPostForm('taxonomy/term/' . $term2->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
// Check the hierarchy.
|
||||
$children = $taxonomy_storage->loadChildren($term1->id());
|
||||
$parents = $taxonomy_storage->loadParents($term2->id());
|
||||
$this->assertTrue(isset($children[$term2->id()]), 'Child found correctly.');
|
||||
$this->assertTrue(isset($parents[$term1->id()]), 'Parent found correctly.');
|
||||
|
||||
// Load and save a term, confirming that parents are still set.
|
||||
$term = Term::load($term2->id());
|
||||
$term->save();
|
||||
$parents = $taxonomy_storage->loadParents($term2->id());
|
||||
$this->assertTrue(isset($parents[$term1->id()]), 'Parent found correctly.');
|
||||
|
||||
// Create a third term and save this as a parent of term2.
|
||||
$term3 = $this->createTerm($this->vocabulary);
|
||||
$term2->parent = array($term1->id(), $term3->id());
|
||||
$term2->save();
|
||||
$parents = $taxonomy_storage->loadParents($term2->id());
|
||||
$this->assertTrue(isset($parents[$term1->id()]) && isset($parents[$term3->id()]), 'Both parents found successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that many terms with parents show on each page
|
||||
*/
|
||||
function testTaxonomyTermChildTerms() {
|
||||
// Set limit to 10 terms per page. Set variable to 9 so 10 terms appear.
|
||||
$this->config('taxonomy.settings')->set('terms_per_page_admin', '9')->save();
|
||||
$term1 = $this->createTerm($this->vocabulary);
|
||||
$terms_array = '';
|
||||
|
||||
$taxonomy_storage = $this->container->get('entity.manager')->getStorage('taxonomy_term');
|
||||
|
||||
// Create 40 terms. Terms 1-12 get parent of $term1. All others are
|
||||
// individual terms.
|
||||
for ($x = 1; $x <= 40; $x++) {
|
||||
$edit = array();
|
||||
// Set terms in order so we know which terms will be on which pages.
|
||||
$edit['weight'] = $x;
|
||||
|
||||
// Set terms 1-20 to be children of first term created.
|
||||
if ($x <= 12) {
|
||||
$edit['parent'] = $term1->id();
|
||||
}
|
||||
$term = $this->createTerm($this->vocabulary, $edit);
|
||||
$children = $taxonomy_storage->loadChildren($term1->id());
|
||||
$parents = $taxonomy_storage->loadParents($term->id());
|
||||
$terms_array[$x] = Term::load($term->id());
|
||||
}
|
||||
|
||||
// Get Page 1.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
|
||||
$this->assertText($term1->getName(), 'Parent Term is displayed on Page 1');
|
||||
for ($x = 1; $x <= 13; $x++) {
|
||||
$this->assertText($terms_array[$x]->getName(), $terms_array[$x]->getName() . ' found on Page 1');
|
||||
}
|
||||
|
||||
// Get Page 2.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview', array('query' => array('page' => 1)));
|
||||
$this->assertText($term1->getName(), 'Parent Term is displayed on Page 2');
|
||||
for ($x = 1; $x <= 18; $x++) {
|
||||
$this->assertText($terms_array[$x]->getName(), $terms_array[$x]->getName() . ' found on Page 2');
|
||||
}
|
||||
|
||||
// Get Page 3.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview', array('query' => array('page' => 2)));
|
||||
$this->assertNoText($term1->getName(), 'Parent Term is not displayed on Page 3');
|
||||
for ($x = 1; $x <= 17; $x++) {
|
||||
$this->assertNoText($terms_array[$x]->getName(), $terms_array[$x]->getName() . ' not found on Page 3');
|
||||
}
|
||||
for ($x =18; $x <= 25; $x++) {
|
||||
$this->assertText($terms_array[$x]->getName(), $terms_array[$x]->getName() . ' found on Page 3');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that hook_node_$op implementations work correctly.
|
||||
*
|
||||
* Save & edit a node and assert that taxonomy terms are saved/loaded properly.
|
||||
*/
|
||||
function testTaxonomyNode() {
|
||||
// Create two taxonomy terms.
|
||||
$term1 = $this->createTerm($this->vocabulary);
|
||||
$term2 = $this->createTerm($this->vocabulary);
|
||||
|
||||
// Post an article.
|
||||
$edit = array();
|
||||
$edit['title[0][value]'] = $this->randomMachineName();
|
||||
$edit['body[0][value]'] = $this->randomMachineName();
|
||||
$edit[$this->field->getName() . '[]'] = $term1->id();
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save'));
|
||||
|
||||
// Check that the term is displayed when the node is viewed.
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertText($term1->getName(), 'Term is displayed when viewing the node.');
|
||||
|
||||
$this->clickLink(t('Edit'));
|
||||
$this->assertText($term1->getName(), 'Term is displayed when editing the node.');
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
$this->assertText($term1->getName(), 'Term is displayed after saving the node with no changes.');
|
||||
|
||||
// Edit the node with a different term.
|
||||
$edit[$this->field->getName() . '[]'] = $term2->id();
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertText($term2->getName(), 'Term is displayed when viewing the node.');
|
||||
|
||||
// Preview the node.
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Preview'));
|
||||
$this->assertUniqueText($term2->getName(), 'Term is displayed when previewing the node.');
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', NULL, t('Preview'));
|
||||
$this->assertUniqueText($term2->getName(), 'Term is displayed when previewing the node again.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test term creation with a free-tagging vocabulary from the node form.
|
||||
*/
|
||||
function testNodeTermCreationAndDeletion() {
|
||||
// Enable tags in the vocabulary.
|
||||
$field = $this->field;
|
||||
entity_get_form_display($field->getTargetEntityTypeId(), $field->getTargetBundle(), 'default')
|
||||
->setComponent($field->getName(), array(
|
||||
'type' => 'entity_reference_autocomplete_tags',
|
||||
'settings' => array(
|
||||
'placeholder' => 'Start typing here.',
|
||||
),
|
||||
))
|
||||
->save();
|
||||
// Prefix the terms with a letter to ensure there is no clash in the first
|
||||
// three letters.
|
||||
// @see https://www.drupal.org/node/2397691
|
||||
$terms = array(
|
||||
'term1' => 'a'. $this->randomMachineName(),
|
||||
'term2' => 'b'. $this->randomMachineName(),
|
||||
'term3' => 'c'. $this->randomMachineName() . ', ' . $this->randomMachineName(),
|
||||
'term4' => 'd'. $this->randomMachineName(),
|
||||
);
|
||||
|
||||
$edit = array();
|
||||
$edit['title[0][value]'] = $this->randomMachineName();
|
||||
$edit['body[0][value]'] = $this->randomMachineName();
|
||||
// Insert the terms in a comma separated list. Vocabulary 1 is a
|
||||
// free-tagging field created by the default profile.
|
||||
$edit[$field->getName() . '[target_id]'] = Tags::implode($terms);
|
||||
|
||||
// Verify the placeholder is there.
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertRaw('placeholder="Start typing here."', 'Placeholder is present.');
|
||||
|
||||
// Preview and verify the terms appear but are not created.
|
||||
$this->drupalPostForm(NULL, $edit, t('Preview'));
|
||||
foreach ($terms as $term) {
|
||||
$this->assertText($term, 'The term appears on the node preview.');
|
||||
}
|
||||
$tree = $this->container->get('entity.manager')->getStorage('taxonomy_term')->loadTree($this->vocabulary->id());
|
||||
$this->assertTrue(empty($tree), 'The terms are not created on preview.');
|
||||
|
||||
// taxonomy.module does not maintain its static caches.
|
||||
taxonomy_terms_static_reset();
|
||||
|
||||
// Save, creating the terms.
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save'));
|
||||
$this->assertRaw(t('@type %title has been created.', array('@type' => t('Article'), '%title' => $edit['title[0][value]'])), 'The node was created successfully.');
|
||||
foreach ($terms as $term) {
|
||||
$this->assertText($term, 'The term was saved and appears on the node page.');
|
||||
}
|
||||
|
||||
// Get the created terms.
|
||||
$term_objects = array();
|
||||
foreach ($terms as $key => $term) {
|
||||
$term_objects[$key] = taxonomy_term_load_multiple_by_name($term);
|
||||
$term_objects[$key] = reset($term_objects[$key]);
|
||||
}
|
||||
|
||||
// Get the node.
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
|
||||
// Test editing the node.
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
foreach ($terms as $term) {
|
||||
$this->assertText($term, 'The term was retained after edit and still appears on the node page.');
|
||||
}
|
||||
|
||||
// Delete term 1 from the term edit page.
|
||||
$this->drupalGet('taxonomy/term/' . $term_objects['term1']->id() . '/edit');
|
||||
$this->clickLink(t('Delete'));
|
||||
$this->drupalPostForm(NULL, NULL, t('Delete'));
|
||||
|
||||
// Delete term 2 from the term delete page.
|
||||
$this->drupalGet('taxonomy/term/' . $term_objects['term2']->id() . '/delete');
|
||||
$this->drupalPostForm(NULL, array(), t('Delete'));
|
||||
$term_names = array($term_objects['term3']->getName(), $term_objects['term4']->getName());
|
||||
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
|
||||
foreach ($term_names as $term_name) {
|
||||
$this->assertText($term_name, format_string('The term %name appears on the node page after two terms, %deleted1 and %deleted2, were deleted.', array('%name' => $term_name, '%deleted1' => $term_objects['term1']->getName(), '%deleted2' => $term_objects['term2']->getName())));
|
||||
}
|
||||
$this->assertNoText($term_objects['term1']->getName(), format_string('The deleted term %name does not appear on the node page.', array('%name' => $term_objects['term1']->getName())));
|
||||
$this->assertNoText($term_objects['term2']->getName(), format_string('The deleted term %name does not appear on the node page.', array('%name' => $term_objects['term2']->getName())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save, edit and delete a term using the user interface.
|
||||
*/
|
||||
function testTermInterface() {
|
||||
\Drupal::service('module_installer')->install(array('views'));
|
||||
$edit = array(
|
||||
'name[0][value]' => $this->randomMachineName(12),
|
||||
'description[0][value]' => $this->randomMachineName(100),
|
||||
);
|
||||
// Explicitly set the parents field to 'root', to ensure that
|
||||
// TermForm::save() handles the invalid term ID correctly.
|
||||
$edit['parent[]'] = array(0);
|
||||
|
||||
// Create the term to edit.
|
||||
$this->drupalPostForm('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add', $edit, t('Save'));
|
||||
|
||||
$terms = taxonomy_term_load_multiple_by_name($edit['name[0][value]']);
|
||||
$term = reset($terms);
|
||||
$this->assertNotNull($term, 'Term found in database.');
|
||||
|
||||
// Submitting a term takes us to the add page; we need the List page.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
|
||||
|
||||
// Test edit link as accessed from Taxonomy administration pages.
|
||||
// Because Simpletest creates its own database when running tests, we know
|
||||
// the first edit link found on the listing page is to our term.
|
||||
$this->clickLink(t('Edit'), 1);
|
||||
|
||||
$this->assertRaw($edit['name[0][value]'], 'The randomly generated term name is present.');
|
||||
$this->assertText($edit['description[0][value]'], 'The randomly generated term description is present.');
|
||||
|
||||
$edit = array(
|
||||
'name[0][value]' => $this->randomMachineName(14),
|
||||
'description[0][value]' => $this->randomMachineName(102),
|
||||
);
|
||||
|
||||
// Edit the term.
|
||||
$this->drupalPostForm('taxonomy/term/' . $term->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
// Check that the term is still present at admin UI after edit.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
|
||||
$this->assertText($edit['name[0][value]'], 'The randomly generated term name is present.');
|
||||
$this->assertLink(t('Edit'));
|
||||
|
||||
// Check the term link can be clicked through to the term page.
|
||||
$this->clickLink($edit['name[0][value]']);
|
||||
$this->assertResponse(200, 'Term page can be accessed via the listing link.');
|
||||
|
||||
// View the term and check that it is correct.
|
||||
$this->drupalGet('taxonomy/term/' . $term->id());
|
||||
$this->assertText($edit['name[0][value]'], 'The randomly generated term name is present.');
|
||||
$this->assertText($edit['description[0][value]'], 'The randomly generated term description is present.');
|
||||
|
||||
// Did this page request display a 'term-listing-heading'?
|
||||
$this->assertTrue($this->xpath('//div[contains(@class, "field-taxonomy-term--description")]'), 'Term page displayed the term description element.');
|
||||
// Check that it does NOT show a description when description is blank.
|
||||
$term->setDescription(NULL);
|
||||
$term->save();
|
||||
$this->drupalGet('taxonomy/term/' . $term->id());
|
||||
$this->assertFalse($this->xpath('//div[contains(@class, "field-taxonomy-term--description")]'), 'Term page did not display the term description when description was blank.');
|
||||
|
||||
// Check that the description value is processed.
|
||||
$value = $this->randomMachineName();
|
||||
$term->setDescription($value);
|
||||
$term->save();
|
||||
$this->assertEqual($term->description->processed, "<p>$value</p>\n");
|
||||
|
||||
// Check that the term feed page is working.
|
||||
$this->drupalGet('taxonomy/term/' . $term->id() . '/feed');
|
||||
|
||||
// Delete the term.
|
||||
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
|
||||
$this->clickLink(t('Delete'));
|
||||
$this->drupalPostForm(NULL, NULL, t('Delete'));
|
||||
|
||||
// Assert that the term no longer exists.
|
||||
$this->drupalGet('taxonomy/term/' . $term->id());
|
||||
$this->assertResponse(404, 'The taxonomy term page was not found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Save, edit and delete a term using the user interface.
|
||||
*/
|
||||
function testTermReorder() {
|
||||
$this->createTerm($this->vocabulary);
|
||||
$this->createTerm($this->vocabulary);
|
||||
$this->createTerm($this->vocabulary);
|
||||
|
||||
$taxonomy_storage = $this->container->get('entity.manager')->getStorage('taxonomy_term');
|
||||
|
||||
// Fetch the created terms in the default alphabetical order, i.e. term1
|
||||
// precedes term2 alphabetically, and term2 precedes term3.
|
||||
$taxonomy_storage->resetCache();
|
||||
list($term1, $term2, $term3) = $taxonomy_storage->loadTree($this->vocabulary->id(), 0, NULL, TRUE);
|
||||
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
|
||||
|
||||
// Each term has four hidden fields, "tid:1:0[tid]", "tid:1:0[parent]",
|
||||
// "tid:1:0[depth]", and "tid:1:0[weight]". Change the order to term2,
|
||||
// term3, term1 by setting weight property, make term3 a child of term2 by
|
||||
// setting the parent and depth properties, and update all hidden fields.
|
||||
$edit = array(
|
||||
'terms[tid:' . $term2->id() . ':0][term][tid]' => $term2->id(),
|
||||
'terms[tid:' . $term2->id() . ':0][term][parent]' => 0,
|
||||
'terms[tid:' . $term2->id() . ':0][term][depth]' => 0,
|
||||
'terms[tid:' . $term2->id() . ':0][weight]' => 0,
|
||||
'terms[tid:' . $term3->id() . ':0][term][tid]' => $term3->id(),
|
||||
'terms[tid:' . $term3->id() . ':0][term][parent]' => $term2->id(),
|
||||
'terms[tid:' . $term3->id() . ':0][term][depth]' => 1,
|
||||
'terms[tid:' . $term3->id() . ':0][weight]' => 1,
|
||||
'terms[tid:' . $term1->id() . ':0][term][tid]' => $term1->id(),
|
||||
'terms[tid:' . $term1->id() . ':0][term][parent]' => 0,
|
||||
'terms[tid:' . $term1->id() . ':0][term][depth]' => 0,
|
||||
'terms[tid:' . $term1->id() . ':0][weight]' => 2,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
$taxonomy_storage->resetCache();
|
||||
$terms = $taxonomy_storage->loadTree($this->vocabulary->id());
|
||||
$this->assertEqual($terms[0]->tid, $term2->id(), 'Term 2 was moved above term 1.');
|
||||
$this->assertEqual($terms[1]->parents, array($term2->id()), 'Term 3 was made a child of term 2.');
|
||||
$this->assertEqual($terms[2]->tid, $term1->id(), 'Term 1 was moved below term 2.');
|
||||
|
||||
$this->drupalPostForm('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview', array(), t('Reset to alphabetical'));
|
||||
// Submit confirmation form.
|
||||
$this->drupalPostForm(NULL, array(), t('Reset to alphabetical'));
|
||||
// Ensure form redirected back to overview.
|
||||
$this->assertUrl('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
|
||||
|
||||
$taxonomy_storage->resetCache();
|
||||
$terms = $taxonomy_storage->loadTree($this->vocabulary->id(), 0, NULL, TRUE);
|
||||
$this->assertEqual($terms[0]->id(), $term1->id(), 'Term 1 was moved to back above term 2.');
|
||||
$this->assertEqual($terms[1]->id(), $term2->id(), 'Term 2 was moved to back below term 1.');
|
||||
$this->assertEqual($terms[2]->id(), $term3->id(), 'Term 3 is still below term 2.');
|
||||
$this->assertEqual($terms[2]->parents, array($term2->id()), 'Term 3 is still a child of term 2.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test saving a term with multiple parents through the UI.
|
||||
*/
|
||||
function testTermMultipleParentsInterface() {
|
||||
// Add a new term to the vocabulary so that we can have multiple parents.
|
||||
$parent = $this->createTerm($this->vocabulary);
|
||||
|
||||
// Add a new term with multiple parents.
|
||||
$edit = array(
|
||||
'name[0][value]' => $this->randomMachineName(12),
|
||||
'description[0][value]' => $this->randomMachineName(100),
|
||||
'parent[]' => array(0, $parent->id()),
|
||||
);
|
||||
// Save the new term.
|
||||
$this->drupalPostForm('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add', $edit, t('Save'));
|
||||
|
||||
// Check that the term was successfully created.
|
||||
$terms = taxonomy_term_load_multiple_by_name($edit['name[0][value]']);
|
||||
$term = reset($terms);
|
||||
$this->assertNotNull($term, 'Term found in database.');
|
||||
$this->assertEqual($edit['name[0][value]'], $term->getName(), 'Term name was successfully saved.');
|
||||
$this->assertEqual($edit['description[0][value]'], $term->getDescription(), 'Term description was successfully saved.');
|
||||
// Check that the parent tid is still there. The other parent (<root>) is
|
||||
// not added by \Drupal\taxonomy\TermStorageInterface::loadParents().
|
||||
$parents = $this->container->get('entity.manager')->getStorage('taxonomy_term')->loadParents($term->id());
|
||||
$parent = reset($parents);
|
||||
$this->assertEqual($edit['parent[]'][1], $parent->id(), 'Term parents were successfully saved.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test taxonomy_term_load_multiple_by_name().
|
||||
*/
|
||||
function testTaxonomyGetTermByName() {
|
||||
$term = $this->createTerm($this->vocabulary);
|
||||
|
||||
// Load the term with the exact name.
|
||||
$terms = taxonomy_term_load_multiple_by_name($term->getName());
|
||||
$this->assertTrue(isset($terms[$term->id()]), 'Term loaded using exact name.');
|
||||
|
||||
// Load the term with space concatenated.
|
||||
$terms = taxonomy_term_load_multiple_by_name(' ' . $term->getName() . ' ');
|
||||
$this->assertTrue(isset($terms[$term->id()]), 'Term loaded with extra whitespace.');
|
||||
|
||||
// Load the term with name uppercased.
|
||||
$terms = taxonomy_term_load_multiple_by_name(strtoupper($term->getName()));
|
||||
$this->assertTrue(isset($terms[$term->id()]), 'Term loaded with uppercased name.');
|
||||
|
||||
// Load the term with name lowercased.
|
||||
$terms = taxonomy_term_load_multiple_by_name(strtolower($term->getName()));
|
||||
$this->assertTrue(isset($terms[$term->id()]), 'Term loaded with lowercased name.');
|
||||
|
||||
// Try to load an invalid term name.
|
||||
$terms = taxonomy_term_load_multiple_by_name('Banana');
|
||||
$this->assertFalse($terms, 'No term loaded with an invalid name.');
|
||||
|
||||
// Try to load the term using a substring of the name.
|
||||
$terms = taxonomy_term_load_multiple_by_name(Unicode::substr($term->getName(), 2), 'No term loaded with a substring of the name.');
|
||||
$this->assertFalse($terms);
|
||||
|
||||
// Create a new term in a different vocabulary with the same name.
|
||||
$new_vocabulary = $this->createVocabulary();
|
||||
$new_term = entity_create('taxonomy_term', array(
|
||||
'name' => $term->getName(),
|
||||
'vid' => $new_vocabulary->id(),
|
||||
));
|
||||
$new_term->save();
|
||||
|
||||
// Load multiple terms with the same name.
|
||||
$terms = taxonomy_term_load_multiple_by_name($term->getName());
|
||||
$this->assertEqual(count($terms), 2, 'Two terms loaded with the same name.');
|
||||
|
||||
// Load single term when restricted to one vocabulary.
|
||||
$terms = taxonomy_term_load_multiple_by_name($term->getName(), $this->vocabulary->id());
|
||||
$this->assertEqual(count($terms), 1, 'One term loaded when restricted by vocabulary.');
|
||||
$this->assertTrue(isset($terms[$term->id()]), 'Term loaded using exact name and vocabulary machine name.');
|
||||
|
||||
// Create a new term with another name.
|
||||
$term2 = $this->createTerm($this->vocabulary);
|
||||
|
||||
// Try to load a term by name that doesn't exist in this vocabulary but
|
||||
// exists in another vocabulary.
|
||||
$terms = taxonomy_term_load_multiple_by_name($term2->getName(), $new_vocabulary->id());
|
||||
$this->assertFalse($terms, 'Invalid term name restricted by vocabulary machine name not loaded.');
|
||||
|
||||
// Try to load terms filtering by a non-existing vocabulary.
|
||||
$terms = taxonomy_term_load_multiple_by_name($term2->getName(), 'non_existing_vocabulary');
|
||||
$this->assertEqual(count($terms), 0, 'No terms loaded when restricted by a non-existing vocabulary.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that editing and saving a node with no changes works correctly.
|
||||
*/
|
||||
function testReSavingTags() {
|
||||
// Enable tags in the vocabulary.
|
||||
$field = $this->field;
|
||||
entity_get_form_display($field->getTargetEntityTypeId(), $field->getTargetBundle(), 'default')
|
||||
->setComponent($field->getName(), array(
|
||||
'type' => 'entity_reference_autocomplete_tags',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Create a term and a node using it.
|
||||
$term = $this->createTerm($this->vocabulary);
|
||||
$edit = array();
|
||||
$edit['title[0][value]'] = $this->randomMachineName(8);
|
||||
$edit['body[0][value]'] = $this->randomMachineName(16);
|
||||
$edit[$this->field->getName() . '[target_id]'] = $term->getName();
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save'));
|
||||
|
||||
// Check that the term is displayed when editing and saving the node with no
|
||||
// changes.
|
||||
$this->clickLink(t('Edit'));
|
||||
$this->assertRaw($term->getName(), 'Term is displayed when editing the node.');
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
$this->assertRaw($term->getName(), 'Term is displayed after saving the node with no changes.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\TermTranslationBreadcrumbTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\system\Tests\Menu\AssertBreadcrumbTrait;
|
||||
|
||||
/**
|
||||
* Tests for proper breadcrumb translation.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TermTranslationBreadcrumbTest extends TaxonomyTestBase {
|
||||
|
||||
use AssertBreadcrumbTrait;
|
||||
use TaxonomyTranslationTestTrait;
|
||||
|
||||
/**
|
||||
* Term to translated term mapping.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $termTranslationMap = array(
|
||||
'one' => 'translatedOne',
|
||||
'two' => 'translatedTwo',
|
||||
'three' => 'translatedThree',
|
||||
);
|
||||
|
||||
/**
|
||||
* Created terms.
|
||||
*
|
||||
* @var \Drupal\taxonomy\Entity\Term[]
|
||||
*/
|
||||
protected $terms = array();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('taxonomy', 'language', 'content_translation');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->setupLanguages();
|
||||
$this->vocabulary = $this->createVocabulary();
|
||||
$this->enableTranslation();
|
||||
$this->setUpTerms();
|
||||
$this->setUpTermReferenceField();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test translated breadcrumbs.
|
||||
*/
|
||||
public function testTranslatedBreadcrumbs() {
|
||||
// Ensure non-translated breadcrumb is correct.
|
||||
$breadcrumb = array(Url::fromRoute('<front>')->toString() => 'Home');
|
||||
foreach ($this->terms as $term) {
|
||||
$breadcrumb[$term->url()] = $term->label();
|
||||
}
|
||||
// The last item will not be in the breadcrumb.
|
||||
array_pop($breadcrumb);
|
||||
|
||||
// Check the breadcrumb on the leaf term page.
|
||||
$term = $this->getLeafTerm();
|
||||
$this->assertBreadcrumb($term->urlInfo(), $breadcrumb, $term->label());
|
||||
|
||||
$languages = \Drupal::languageManager()->getLanguages();
|
||||
|
||||
// Construct the expected translated breadcrumb.
|
||||
$breadcrumb = array(Url::fromRoute('<front>', [], ['language' => $languages[$this->translateToLangcode]])->toString() => 'Home');
|
||||
foreach ($this->terms as $term) {
|
||||
$translated = $term->getTranslation($this->translateToLangcode);
|
||||
$url = $translated->url('canonical', ['language' => $languages[$this->translateToLangcode]]);
|
||||
$breadcrumb[$url] = $translated->label();
|
||||
}
|
||||
array_pop($breadcrumb);
|
||||
|
||||
// Check for the translated breadcrumb on the translated leaf term page.
|
||||
$term = $this->getLeafTerm();
|
||||
$translated = $term->getTranslation($this->translateToLangcode);
|
||||
$this->assertBreadcrumb($translated->urlInfo('canonical', ['language' => $languages[$this->translateToLangcode]]), $breadcrumb, $translated->label());
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup translated terms in a hierarchy.
|
||||
*/
|
||||
protected function setUpTerms() {
|
||||
$parent_vid = 0;
|
||||
foreach ($this->termTranslationMap as $name => $translation) {
|
||||
|
||||
$term = $this->createTerm($this->vocabulary, array(
|
||||
'name' => $name,
|
||||
'langcode' => $this->baseLangcode,
|
||||
'parent' => $parent_vid,
|
||||
));
|
||||
|
||||
$term->addTranslation($this->translateToLangcode, array(
|
||||
'name' => $translation,
|
||||
));
|
||||
$term->save();
|
||||
|
||||
// Each term is nested under the last.
|
||||
$parent_vid = $term->id();
|
||||
|
||||
$this->terms[] = $term;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the final (leaf) term in the hierarchy.
|
||||
*
|
||||
* @return \Drupal\taxonomy\Entity\Term
|
||||
* The final term in the hierarchy.
|
||||
*/
|
||||
protected function getLeafTerm() {
|
||||
return $this->terms[count($this->termTranslationMap) - 1];
|
||||
}
|
||||
|
||||
}
|
108
core/modules/taxonomy/src/Tests/TermTranslationFieldViewTest.php
Normal file
108
core/modules/taxonomy/src/Tests/TermTranslationFieldViewTest.php
Normal file
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\TermTranslationFieldViewTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
/**
|
||||
* Tests the translation of taxonomy terms field on nodes.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TermTranslationFieldViewTest extends TaxonomyTestBase {
|
||||
|
||||
use TaxonomyTranslationTestTrait;
|
||||
|
||||
/**
|
||||
* The term that should be translated.
|
||||
*
|
||||
* @var \Drupal\taxonomy\Entity\Term
|
||||
*/
|
||||
protected $term;
|
||||
|
||||
/**
|
||||
* The tag in the source language.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseTagName = 'OriginalTagName';
|
||||
|
||||
/**
|
||||
* The translated value for the tag.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $translatedTagName = 'TranslatedTagName';
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('language', 'content_translation', 'taxonomy');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->setupLanguages();
|
||||
$this->vocabulary = $this->createVocabulary();
|
||||
$this->enableTranslation();
|
||||
$this->setUpTerm();
|
||||
$this->setUpTermReferenceField();
|
||||
$this->setUpNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the translated taxonomy term is displayed.
|
||||
*/
|
||||
public function testTranslatedTaxonomyTermReferenceDisplay() {
|
||||
$path = 'node/' . $this->node->id();
|
||||
$translation_path = $this->translateToLangcode . '/' . $path;
|
||||
|
||||
$this->drupalGet($path);
|
||||
$this->assertNoText($this->translatedTagName);
|
||||
$this->assertText($this->baseTagName);
|
||||
$this->drupalGet($translation_path);
|
||||
$this->assertText($this->translatedTagName);
|
||||
$this->assertNoText($this->baseTagName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a test subject node, with translation.
|
||||
*/
|
||||
protected function setUpNode() {
|
||||
/** @var \Drupal\node\Entity\Node $node */
|
||||
$node = entity_create('node', array(
|
||||
'title' => $this->randomMachineName(),
|
||||
'type' => 'article',
|
||||
'description' => array(
|
||||
'value' => $this->randomMachineName(),
|
||||
'format' => 'basic_html',
|
||||
),
|
||||
$this->termFieldName => array(array('target_id' => $this->term->id())),
|
||||
'langcode' => $this->baseLangcode,
|
||||
));
|
||||
$node->save();
|
||||
$node->addTranslation($this->translateToLangcode, array());
|
||||
$node->save();
|
||||
$this->node = $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a test subject term, with translation.
|
||||
*/
|
||||
protected function setUpTerm() {
|
||||
$this->term = $this->createTerm($this->vocabulary, array(
|
||||
'name' => $this->baseTagName,
|
||||
'langcode' => $this->baseLangcode,
|
||||
));
|
||||
|
||||
$this->term->addTranslation($this->translateToLangcode, array(
|
||||
'name' => $this->translatedTagName,
|
||||
));
|
||||
$this->term->save();
|
||||
}
|
||||
|
||||
}
|
165
core/modules/taxonomy/src/Tests/TermTranslationUITest.php
Normal file
165
core/modules/taxonomy/src/Tests/TermTranslationUITest.php
Normal file
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\TermTranslationUITest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\content_translation\Tests\ContentTranslationUITestBase;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
|
||||
/**
|
||||
* Tests the Term Translation UI.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TermTranslationUITest extends ContentTranslationUITestBase {
|
||||
|
||||
/**
|
||||
* The vocabulary used for creating terms.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyInterface
|
||||
*/
|
||||
protected $vocabulary;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('language', 'content_translation', 'taxonomy');
|
||||
|
||||
protected function setUp() {
|
||||
$this->entityTypeId = 'taxonomy_term';
|
||||
$this->bundle = 'tags';
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\content_translation\Tests\ContentTranslationUITestBase::setupBundle().
|
||||
*/
|
||||
protected function setupBundle() {
|
||||
parent::setupBundle();
|
||||
|
||||
// Create a vocabulary.
|
||||
$this->vocabulary = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => $this->bundle,
|
||||
'description' => $this->randomMachineName(),
|
||||
'vid' => $this->bundle,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
'weight' => mt_rand(0, 10),
|
||||
));
|
||||
$this->vocabulary->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\content_translation\Tests\ContentTranslationUITestBase::getTranslatorPermission().
|
||||
*/
|
||||
protected function getTranslatorPermissions() {
|
||||
return array_merge(parent::getTranslatorPermissions(), array('administer taxonomy'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\content_translation\Tests\ContentTranslationUITestBase::getNewEntityValues().
|
||||
*/
|
||||
protected function getNewEntityValues($langcode) {
|
||||
return array('name' => $this->randomMachineName()) + parent::getNewEntityValues($langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an edit array containing the values to be posted.
|
||||
*/
|
||||
protected function getEditValues($values, $langcode, $new = FALSE) {
|
||||
$edit = parent::getEditValues($values, $langcode, $new);
|
||||
|
||||
// To be able to post values for the configurable base fields (name,
|
||||
// description) have to be suffixed with [0][value].
|
||||
foreach ($edit as $property => $value) {
|
||||
foreach (array('name', 'description') as $key) {
|
||||
if ($property == $key) {
|
||||
$edit[$key . '[0][value]'] = $value;
|
||||
unset($edit[$property]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $edit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\content_translation\Tests\ContentTranslationUITestBase::testTranslationUI().
|
||||
*/
|
||||
public function testTranslationUI() {
|
||||
parent::testTranslationUI();
|
||||
|
||||
// Make sure that no row was inserted for taxonomy vocabularies which do
|
||||
// not have translations enabled.
|
||||
$rows = db_query('SELECT tid, count(tid) AS count FROM {taxonomy_term_field_data} WHERE vid <> :vid GROUP BY tid', array(':vid' => $this->bundle))->fetchAll();
|
||||
foreach ($rows as $row) {
|
||||
$this->assertTrue($row->count < 2, 'Term does not have translations.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests translate link on vocabulary term list.
|
||||
*/
|
||||
function testTranslateLinkVocabularyAdminPage() {
|
||||
$this->drupalLogin($this->drupalCreateUser(array_merge(parent::getTranslatorPermissions(), ['access administration pages', 'administer taxonomy'])));
|
||||
|
||||
$values = array(
|
||||
'name' => $this->randomMachineName(),
|
||||
);
|
||||
$translatable_tid = $this->createEntity($values, $this->langcodes[0], $this->vocabulary->id());
|
||||
|
||||
// Create an untranslatable vocabulary.
|
||||
$untranslatable_vocabulary = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => 'untranslatable_voc',
|
||||
'description' => $this->randomMachineName(),
|
||||
'vid' => 'untranslatable_voc',
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
'weight' => mt_rand(0, 10),
|
||||
));
|
||||
$untranslatable_vocabulary->save();
|
||||
|
||||
$values = array(
|
||||
'name' => $this->randomMachineName(),
|
||||
);
|
||||
$untranslatable_tid = $this->createEntity($values, $this->langcodes[0], $untranslatable_vocabulary->id());
|
||||
|
||||
// Verify translation links.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/overview');
|
||||
$this->assertResponse(200, 'The translatable vocabulary page was found.');
|
||||
$this->assertLinkByHref('term/' . $translatable_tid . '/translations', 0, 'The translations link exists for a translatable vocabulary.');
|
||||
$this->assertLinkByHref('term/' . $translatable_tid . '/edit', 0, 'The edit link exists for a translatable vocabulary.');
|
||||
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $untranslatable_vocabulary->id() . '/overview');
|
||||
$this->assertResponse(200);
|
||||
$this->assertLinkByHref('term/' . $untranslatable_tid . '/edit');
|
||||
$this->assertNoLinkByHref('term/' . $untranslatable_tid . '/translations');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTestTranslationEdit() {
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
// We only want to test the title for non-english translations.
|
||||
if ($langcode != 'en') {
|
||||
$options = array('language' => $languages[$langcode]);
|
||||
$url = $entity->urlInfo('edit-form', $options);
|
||||
$this->drupalGet($url);
|
||||
|
||||
$title = t('@title [%language translation]', array(
|
||||
'@title' => $entity->getTranslation($langcode)->label(),
|
||||
'%language' => $languages[$langcode]->getName(),
|
||||
));
|
||||
$this->assertRaw($title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
72
core/modules/taxonomy/src/Tests/TermValidationTest.php
Normal file
72
core/modules/taxonomy/src/Tests/TermValidationTest.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\TermValidationTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\system\Tests\Entity\EntityUnitTestBase;
|
||||
|
||||
/**
|
||||
* Tests term validation constraints.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TermValidationTest extends EntityUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('taxonomy');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the term validation constraints.
|
||||
*/
|
||||
public function testValidation() {
|
||||
$this->entityManager->getStorage('taxonomy_vocabulary')->create(array(
|
||||
'vid' => 'tags',
|
||||
'name' => 'Tags',
|
||||
))->save();
|
||||
$term = $this->entityManager->getStorage('taxonomy_term')->create(array(
|
||||
'name' => 'test',
|
||||
'vid' => 'tags',
|
||||
));
|
||||
$violations = $term->validate();
|
||||
$this->assertEqual(count($violations), 0, 'No violations when validating a default term.');
|
||||
|
||||
$term->set('name', $this->randomString(256));
|
||||
$violations = $term->validate();
|
||||
$this->assertEqual(count($violations), 1, 'Violation found when name is too long.');
|
||||
$this->assertEqual($violations[0]->getPropertyPath(), 'name.0.value');
|
||||
$field_label = $term->get('name')->getFieldDefinition()->getLabel();
|
||||
$this->assertEqual($violations[0]->getMessage(), t('%name: may not be longer than @max characters.', array('%name' => $field_label, '@max' => 255)));
|
||||
|
||||
$term->set('name', NULL);
|
||||
$violations = $term->validate();
|
||||
$this->assertEqual(count($violations), 1, 'Violation found when name is NULL.');
|
||||
$this->assertEqual($violations[0]->getPropertyPath(), 'name');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('This value should not be null.'));
|
||||
$term->set('name', 'test');
|
||||
|
||||
$term->set('parent', 9999);
|
||||
$violations = $term->validate();
|
||||
$this->assertEqual(count($violations), 1, 'Violation found when term parent is invalid.');
|
||||
$this->assertEqual($violations[0]->getMessage(), format_string('The referenced entity (%type: %id) does not exist.', array('%type' => 'taxonomy_term', '%id' => 9999)));
|
||||
|
||||
$term->set('parent', 0);
|
||||
$violations = $term->validate();
|
||||
$this->assertEqual(count($violations), 0, 'No violations for parent id 0.');
|
||||
}
|
||||
}
|
53
core/modules/taxonomy/src/Tests/ThemeTest.php
Normal file
53
core/modules/taxonomy/src/Tests/ThemeTest.php
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\ThemeTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
/**
|
||||
* Verifies that various taxonomy pages use the expected theme.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class ThemeTest extends TaxonomyTestBase {
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Make sure we are using distinct default and administrative themes for
|
||||
// the duration of these tests.
|
||||
\Drupal::service('theme_handler')->install(array('bartik', 'seven'));
|
||||
$this->config('system.theme')
|
||||
->set('default', 'bartik')
|
||||
->set('admin', 'seven')
|
||||
->save();
|
||||
|
||||
// Create and log in as a user who has permission to add and edit taxonomy
|
||||
// terms and view the administrative theme.
|
||||
$admin_user = $this->drupalCreateUser(array('administer taxonomy', 'view the administration theme'));
|
||||
$this->drupalLogin($admin_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the theme used when adding, viewing and editing taxonomy terms.
|
||||
*/
|
||||
function testTaxonomyTermThemes() {
|
||||
// Adding a term to a vocabulary is considered an administrative action and
|
||||
// should use the administrative theme.
|
||||
$vocabulary = $this->createVocabulary();
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id() . '/add');
|
||||
$this->assertRaw('seven/css/base/elements.css', t("The administrative theme's CSS appears on the page for adding a taxonomy term."));
|
||||
|
||||
// Viewing a taxonomy term should use the default theme.
|
||||
$term = $this->createTerm($vocabulary);
|
||||
$this->drupalGet('taxonomy/term/' . $term->id());
|
||||
$this->assertRaw('bartik/css/base/elements.css', t("The default theme's CSS appears on the page for viewing a taxonomy term."));
|
||||
|
||||
// Editing a taxonomy term should use the same theme as adding one.
|
||||
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
|
||||
$this->assertRaw('seven/css/base/elements.css', t("The administrative theme's CSS appears on the page for editing a taxonomy term."));
|
||||
}
|
||||
}
|
157
core/modules/taxonomy/src/Tests/TokenReplaceTest.php
Normal file
157
core/modules/taxonomy/src/Tests/TokenReplaceTest.php
Normal file
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\TokenReplaceTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Generates text using placeholders for dummy content to check taxonomy token
|
||||
* replacement.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TokenReplaceTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* The vocabulary used for creating terms.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyInterface
|
||||
*/
|
||||
protected $vocabulary;
|
||||
|
||||
/**
|
||||
* Name of the taxonomy term reference field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer taxonomy', 'bypass node access']));
|
||||
$this->vocabulary = $this->createVocabulary();
|
||||
$this->fieldName = 'taxonomy_' . $this->vocabulary->id();
|
||||
|
||||
$handler_settings = array(
|
||||
'target_bundles' => array(
|
||||
$this->vocabulary->id() => $this->vocabulary->id(),
|
||||
),
|
||||
'auto_create' => TRUE,
|
||||
);
|
||||
$this->createEntityReferenceField('node', 'article', $this->fieldName, NULL, 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
|
||||
entity_get_form_display('node', 'article', 'default')
|
||||
->setComponent($this->fieldName, array(
|
||||
'type' => 'options_select',
|
||||
))
|
||||
->save();
|
||||
entity_get_display('node', 'article', 'default')
|
||||
->setComponent($this->fieldName, array(
|
||||
'type' => 'entity_reference_label',
|
||||
))
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates some terms and a node, then tests the tokens generated from them.
|
||||
*/
|
||||
function testTaxonomyTokenReplacement() {
|
||||
$token_service = \Drupal::token();
|
||||
$language_interface = \Drupal::languageManager()->getCurrentLanguage();
|
||||
|
||||
// Create two taxonomy terms.
|
||||
$term1 = $this->createTerm($this->vocabulary);
|
||||
$term2 = $this->createTerm($this->vocabulary);
|
||||
|
||||
// Edit $term2, setting $term1 as parent.
|
||||
$edit = array();
|
||||
$edit['name[0][value]'] = '<blink>Blinking Text</blink>';
|
||||
$edit['parent[]'] = array($term1->id());
|
||||
$this->drupalPostForm('taxonomy/term/' . $term2->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
// Create node with term2.
|
||||
$edit = array();
|
||||
$node = $this->drupalCreateNode(array('type' => 'article'));
|
||||
$edit[$this->fieldName . '[]'] = $term2->id();
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
// Generate and test sanitized tokens for term1.
|
||||
$tests = array();
|
||||
$tests['[term:tid]'] = $term1->id();
|
||||
$tests['[term:name]'] = SafeMarkup::checkPlain($term1->getName());
|
||||
$tests['[term:description]'] = $term1->description->processed;
|
||||
$tests['[term:url]'] = $term1->url('canonical', array('absolute' => TRUE));
|
||||
$tests['[term:node-count]'] = 0;
|
||||
$tests['[term:parent:name]'] = '[term:parent:name]';
|
||||
$tests['[term:vocabulary:name]'] = SafeMarkup::checkPlain($this->vocabulary->label());
|
||||
|
||||
foreach ($tests as $input => $expected) {
|
||||
$output = $token_service->replace($input, array('term' => $term1), array('langcode' => $language_interface->getId()));
|
||||
$this->assertEqual($output, $expected, format_string('Sanitized taxonomy term token %token replaced.', array('%token' => $input)));
|
||||
}
|
||||
|
||||
// Generate and test sanitized tokens for term2.
|
||||
$tests = array();
|
||||
$tests['[term:tid]'] = $term2->id();
|
||||
$tests['[term:name]'] = SafeMarkup::checkPlain($term2->getName());
|
||||
$tests['[term:description]'] = $term2->description->processed;
|
||||
$tests['[term:url]'] = $term2->url('canonical', array('absolute' => TRUE));
|
||||
$tests['[term:node-count]'] = 1;
|
||||
$tests['[term:parent:name]'] = SafeMarkup::checkPlain($term1->getName());
|
||||
$tests['[term:parent:url]'] = $term1->url('canonical', array('absolute' => TRUE));
|
||||
$tests['[term:parent:parent:name]'] = '[term:parent:parent:name]';
|
||||
$tests['[term:vocabulary:name]'] = SafeMarkup::checkPlain($this->vocabulary->label());
|
||||
|
||||
// Test to make sure that we generated something for each token.
|
||||
$this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
|
||||
|
||||
foreach ($tests as $input => $expected) {
|
||||
$output = $token_service->replace($input, array('term' => $term2), array('langcode' => $language_interface->getId()));
|
||||
$this->assertEqual($output, $expected, format_string('Sanitized taxonomy term token %token replaced.', array('%token' => $input)));
|
||||
}
|
||||
|
||||
// Generate and test unsanitized tokens.
|
||||
$tests['[term:name]'] = $term2->getName();
|
||||
$tests['[term:description]'] = $term2->getDescription();
|
||||
$tests['[term:parent:name]'] = $term1->getName();
|
||||
$tests['[term:vocabulary:name]'] = $this->vocabulary->label();
|
||||
|
||||
foreach ($tests as $input => $expected) {
|
||||
$output = $token_service->replace($input, array('term' => $term2), array('langcode' => $language_interface->getId(), 'sanitize' => FALSE));
|
||||
$this->assertEqual($output, $expected, format_string('Unsanitized taxonomy term token %token replaced.', array('%token' => $input)));
|
||||
}
|
||||
|
||||
// Generate and test sanitized tokens.
|
||||
$tests = array();
|
||||
$tests['[vocabulary:vid]'] = $this->vocabulary->id();
|
||||
$tests['[vocabulary:name]'] = SafeMarkup::checkPlain($this->vocabulary->label());
|
||||
$tests['[vocabulary:description]'] = Xss::filter($this->vocabulary->getDescription());
|
||||
$tests['[vocabulary:node-count]'] = 1;
|
||||
$tests['[vocabulary:term-count]'] = 2;
|
||||
|
||||
// Test to make sure that we generated something for each token.
|
||||
$this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
|
||||
|
||||
foreach ($tests as $input => $expected) {
|
||||
$output = $token_service->replace($input, array('vocabulary' => $this->vocabulary), array('langcode' => $language_interface->getId()));
|
||||
$this->assertEqual($output, $expected, format_string('Sanitized taxonomy vocabulary token %token replaced.', array('%token' => $input)));
|
||||
}
|
||||
|
||||
// Generate and test unsanitized tokens.
|
||||
$tests['[vocabulary:name]'] = $this->vocabulary->label();
|
||||
$tests['[vocabulary:description]'] = $this->vocabulary->getDescription();
|
||||
|
||||
foreach ($tests as $input => $expected) {
|
||||
$output = $token_service->replace($input, array('vocabulary' => $this->vocabulary), array('langcode' => $language_interface->getId(), 'sanitize' => FALSE));
|
||||
$this->assertEqual($output, $expected, format_string('Unsanitized taxonomy vocabulary token %token replaced.', array('%token' => $input)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\Views\RelationshipNodeTermDataTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests\Views;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the taxonomy term on node relationship handler.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class RelationshipNodeTermDataTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_taxonomy_node_term_data');
|
||||
|
||||
function testViewsHandlerRelationshipNodeTermData() {
|
||||
$view = Views::getView('test_taxonomy_node_term_data');
|
||||
// Tests \Drupal\taxonomy\Plugin\views\relationship\NodeTermData::calculateDependencies().
|
||||
$expected = [
|
||||
'config' => ['core.entity_view_mode.node.teaser'],
|
||||
'module' => [
|
||||
'node',
|
||||
'taxonomy',
|
||||
'user',
|
||||
],
|
||||
];
|
||||
$this->assertIdentical($expected, $view->calculateDependencies());
|
||||
$this->executeView($view, array($this->term1->id(), $this->term2->id()));
|
||||
$expected_result = array(
|
||||
array(
|
||||
'nid' => $this->nodes[1]->id(),
|
||||
),
|
||||
array(
|
||||
'nid' => $this->nodes[0]->id(),
|
||||
),
|
||||
);
|
||||
$column_map = array('nid' => 'nid');
|
||||
$this->assertIdenticalResultset($view, $expected_result, $column_map);
|
||||
|
||||
// Change the view to test relation limited by vocabulary.
|
||||
$this->config('views.view.test_taxonomy_node_term_data')
|
||||
->set('display.default.display_options.relationships.term_node_tid.vids', ['views_testing_tags'])
|
||||
->save();
|
||||
|
||||
$view = Views::getView('test_taxonomy_node_term_data');
|
||||
// Tests \Drupal\taxonomy\Plugin\views\relationship\NodeTermData::calculateDependencies().
|
||||
$expected['config'][] = 'taxonomy.vocabulary.views_testing_tags';
|
||||
$this->assertIdentical($expected, $view->calculateDependencies());
|
||||
$this->executeView($view, array($this->term1->id(), $this->term2->id()));
|
||||
$this->assertIdenticalResultset($view, $expected_result, $column_map);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\Views\RelationshipRepresentativeNodeTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests\Views;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the representative node relationship for terms.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class RelationshipRepresentativeNodeTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_groupwise_term');
|
||||
|
||||
/**
|
||||
* Tests the relationship.
|
||||
*/
|
||||
public function testRelationship() {
|
||||
$view = Views::getView('test_groupwise_term');
|
||||
$this->executeView($view);
|
||||
$map = array('node_field_data_taxonomy_term_field_data_nid' => 'nid', 'tid' => 'tid');
|
||||
$expected_result = array(
|
||||
array(
|
||||
'nid' => $this->nodes[1]->id(),
|
||||
'tid' => $this->term2->id(),
|
||||
),
|
||||
array(
|
||||
'nid' => $this->nodes[1]->id(),
|
||||
'tid' => $this->term1->id(),
|
||||
),
|
||||
);
|
||||
$this->assertIdenticalResultset($view, $expected_result, $map);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\Views\TaxonomyDefaultArgumentTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests\Views;
|
||||
|
||||
use Drupal\views\Views;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
|
||||
/**
|
||||
* Tests the representative node relationship for terms.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TaxonomyDefaultArgumentTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('taxonomy_default_argument_test');
|
||||
|
||||
/**
|
||||
* Tests the relationship.
|
||||
*/
|
||||
public function testNodePath() {
|
||||
$view = Views::getView('taxonomy_default_argument_test');
|
||||
|
||||
$request = Request::create($this->nodes[0]->url());
|
||||
$request->server->set('SCRIPT_NAME', $GLOBALS['base_path'] . 'index.php');
|
||||
$request->server->set('SCRIPT_FILENAME', 'index.php');
|
||||
|
||||
$response = $this->container->get('http_kernel')->handle($request, HttpKernelInterface::SUB_REQUEST);
|
||||
$view->setRequest($request);
|
||||
$view->setResponse($response);
|
||||
|
||||
$view->initHandlers();
|
||||
$expected = implode(',', array($this->term1->id(), $this->term2->id()));
|
||||
$this->assertEqual($expected, $view->argument['tid']->getDefaultArgument());
|
||||
}
|
||||
|
||||
public function testTermPath() {
|
||||
$view = Views::getView('taxonomy_default_argument_test');
|
||||
|
||||
$request = Request::create($this->term1->url());
|
||||
$request->server->set('SCRIPT_NAME', $GLOBALS['base_path'] . 'index.php');
|
||||
$request->server->set('SCRIPT_FILENAME', 'index.php');
|
||||
|
||||
$response = $this->container->get('http_kernel')->handle($request, HttpKernelInterface::SUB_REQUEST);
|
||||
$view->setRequest($request);
|
||||
$view->setResponse($response);
|
||||
$view->initHandlers();
|
||||
|
||||
$expected = $this->term1->id();
|
||||
$this->assertEqual($expected, $view->argument['tid']->getDefaultArgument());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\Views\TaxonomyFieldFilterTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests\Views;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests taxonomy field filters with translations.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TaxonomyFieldFilterTest extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('language', 'taxonomy', 'taxonomy_test_views', 'text', 'views', 'node');
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_field_filters');
|
||||
|
||||
/**
|
||||
* The vocabulary used for creating terms.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyInterface
|
||||
*/
|
||||
protected $vocabulary;
|
||||
|
||||
/**
|
||||
* List of taxonomy term names by language.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $termNames = [];
|
||||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Add two new languages.
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
ConfigurableLanguage::createFromLangcode('es')->save();
|
||||
|
||||
// Set up term names.
|
||||
$this->termNames = array(
|
||||
'en' => 'Food in Paris',
|
||||
'es' => 'Comida en Paris',
|
||||
'fr' => 'Nouriture en Paris',
|
||||
);
|
||||
|
||||
// Create a vocabulary.
|
||||
$this->vocabulary = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => 'Views testing tags',
|
||||
'vid' => 'views_testing_tags',
|
||||
));
|
||||
$this->vocabulary->save();
|
||||
|
||||
// Add a translatable field to the vocabulary.
|
||||
$field = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_foo',
|
||||
'entity_type' => 'taxonomy_term',
|
||||
'type' => 'text',
|
||||
));
|
||||
$field->save();
|
||||
entity_create('field_config', array(
|
||||
'field_name' => 'field_foo',
|
||||
'entity_type' => 'taxonomy_term',
|
||||
'label' => 'Foo',
|
||||
'bundle' => 'views_testing_tags',
|
||||
))->save();
|
||||
|
||||
// Create term with translations.
|
||||
$taxonomy = $this->createTermWithProperties(array('name' => $this->termNames['en'], 'langcode' => 'en', 'description' => $this->termNames['en'], 'field_foo' => $this->termNames['en']));
|
||||
foreach (array('es', 'fr') as $langcode) {
|
||||
$translation = $taxonomy->addTranslation($langcode, array('name' => $this->termNames[$langcode]));
|
||||
$translation->description->value = $this->termNames[$langcode];
|
||||
$translation->field_foo->value = $this->termNames[$langcode];
|
||||
}
|
||||
$taxonomy->save();
|
||||
|
||||
Views::viewsData()->clear();
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), array('taxonomy_test_views'));
|
||||
$this->container->get('router.builder')->rebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests description and term name filters.
|
||||
*/
|
||||
public function testFilters() {
|
||||
// Test the name filter page, which filters for name contains 'Comida'.
|
||||
// Should show just the Spanish translation, once.
|
||||
$this->assertPageCounts('test-name-filter', array('es' => 1, 'fr' => 0, 'en' => 0), 'Comida name filter');
|
||||
|
||||
// Test the description filter page, which filters for description contains
|
||||
// 'Comida'. Should show just the Spanish translation, once.
|
||||
$this->assertPageCounts('test-desc-filter', array('es' => 1, 'fr' => 0, 'en' => 0), 'Comida description filter');
|
||||
|
||||
// Test the field filter page, which filters for field_foo contains
|
||||
// 'Comida'. Should show just the Spanish translation, once.
|
||||
$this->assertPageCounts('test-field-filter', array('es' => 1, 'fr' => 0, 'en' => 0), 'Comida field filter');
|
||||
|
||||
// Test the name Paris filter page, which filters for name contains
|
||||
// 'Paris'. Should show each translation once.
|
||||
$this->assertPageCounts('test-name-paris', array('es' => 1, 'fr' => 1, 'en' => 1), 'Paris name filter');
|
||||
|
||||
// Test the description Paris page, which filters for description contains
|
||||
// 'Paris'. Should show each translation, once.
|
||||
$this->assertPageCounts('test-desc-paris', array('es' => 1, 'fr' => 1, 'en' => 1), 'Paris description filter');
|
||||
|
||||
// Test the field Paris filter page, which filters for field_foo contains
|
||||
// 'Paris'. Should show each translation once.
|
||||
$this->assertPageCounts('test-field-paris', array('es' => 1, 'fr' => 1, 'en' => 1), 'Paris field filter');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given taxonomy translation counts are correct.
|
||||
*
|
||||
* @param string $path
|
||||
* Path of the page to test.
|
||||
* @param array $counts
|
||||
* Array whose keys are languages, and values are the number of times
|
||||
* that translation should be shown on the given page.
|
||||
* @param string $message
|
||||
* Message suffix to display.
|
||||
*/
|
||||
protected function assertPageCounts($path, $counts, $message) {
|
||||
// Get the text of the page.
|
||||
$this->drupalGet($path);
|
||||
$text = $this->getTextContent();
|
||||
|
||||
// Check the counts. Note that the title and body are both shown on the
|
||||
// page, and they are the same. So the title/body string should appear on
|
||||
// the page twice as many times as the input count.
|
||||
foreach ($counts as $langcode => $count) {
|
||||
$this->assertEqual(substr_count($text, $this->termNames[$langcode]), 2 * $count, 'Translation ' . $langcode . ' has count ' . $count . ' with ' . $message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a taxonomy term with specified name and other properties.
|
||||
*
|
||||
* @param array $properties
|
||||
* Array of properties and field values to set.
|
||||
*
|
||||
* @return \Drupal\taxonomy\TermInterface
|
||||
* The created taxonomy term.
|
||||
*/
|
||||
protected function createTermWithProperties($properties) {
|
||||
// Use the first available text format.
|
||||
$filter_formats = filter_formats();
|
||||
$format = array_pop($filter_formats);
|
||||
|
||||
$properties += array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'description' => $this->randomMachineName(),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
'field_foo' => $this->randomMachineName(),
|
||||
);
|
||||
|
||||
$term = entity_create('taxonomy_term', array(
|
||||
'name' => $properties['name'],
|
||||
'description' => $properties['description'],
|
||||
'format' => $format->id(),
|
||||
'vid' => $this->vocabulary->id(),
|
||||
'langcode' => $properties['langcode'],
|
||||
));
|
||||
$term->field_foo->value = $properties['field_foo'];
|
||||
$term->save();
|
||||
return $term;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\Views\TaxonomyFieldTidTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests\Views;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the taxonomy term TID field handler.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TaxonomyFieldTidTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_taxonomy_tid_field');
|
||||
|
||||
function testViewsHandlerTidField() {
|
||||
$view = Views::getView('test_taxonomy_tid_field');
|
||||
$this->executeView($view);
|
||||
|
||||
$actual = $view->field['name']->advancedRender($view->result[0]);
|
||||
$expected = \Drupal::l($this->term1->label(), $this->term1->urlInfo());
|
||||
|
||||
$this->assertEqual($expected, $actual);
|
||||
}
|
||||
|
||||
}
|
123
core/modules/taxonomy/src/Tests/Views/TaxonomyIndexTidUiTest.php
Normal file
123
core/modules/taxonomy/src/Tests/Views/TaxonomyIndexTidUiTest.php
Normal file
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\Views\TaxonomyIndexTidUiTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests\Views;
|
||||
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
use Drupal\views_ui\Tests\UITestBase;
|
||||
|
||||
/**
|
||||
* Tests the taxonomy index filter handler UI.
|
||||
*
|
||||
* @group taxonomy
|
||||
* @see \Drupal\taxonomy\Plugin\views\field\TaxonomyIndexTid
|
||||
*/
|
||||
class TaxonomyIndexTidUiTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_filter_taxonomy_index_tid');
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'taxonomy', 'taxonomy_test_views');
|
||||
|
||||
/**
|
||||
* A nested array of \Drupal\taxonomy\TermInterface objects.
|
||||
*
|
||||
* @var \Drupal\taxonomy\TermInterface[][]
|
||||
*/
|
||||
protected $terms = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
Vocabulary::create([
|
||||
'vid' => 'tags',
|
||||
'name' => 'Tags',
|
||||
])->save();
|
||||
|
||||
// Setup a hierarchy which looks like this:
|
||||
// term 0.0
|
||||
// term 1.0
|
||||
// - term 1.1
|
||||
// term 2.0
|
||||
// - term 2.1
|
||||
// - term 2.2
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
for ($j = 0; $j <= $i; $j++) {
|
||||
$this->terms[$i][$j] = $term = Term::create([
|
||||
'vid' => 'tags',
|
||||
'name' => "Term $i.$j",
|
||||
'parent' => isset($terms[$i][0]) ? $terms[$i][0]->id() : 0,
|
||||
]);
|
||||
$term->save();
|
||||
}
|
||||
}
|
||||
ViewTestData::createTestViews(get_class($this), array('taxonomy_test_views'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the filter UI.
|
||||
*/
|
||||
public function testFilterUI() {
|
||||
$this->drupalGet('admin/structure/views/nojs/handler/test_filter_taxonomy_index_tid/default/filter/tid');
|
||||
|
||||
$result = $this->xpath('//select[@id="edit-options-value"]/option');
|
||||
|
||||
// Ensure that the expected hierarchy is available in the UI.
|
||||
$counter = 0;
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
for ($j = 0; $j <= $i; $j++) {
|
||||
$option = $result[$counter++];
|
||||
$prefix = $this->terms[$i][$j]->parent->target_id ? '-' : '';
|
||||
$attributes = $option->attributes();
|
||||
$tid = (string) $attributes->value;
|
||||
|
||||
$this->assertEqual($prefix . $this->terms[$i][$j]->getName(), (string) $option);
|
||||
$this->assertEqual($this->terms[$i][$j]->id(), $tid);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the autocomplete input element appears when using the 'textfield'
|
||||
// type.
|
||||
$view = entity_load('view', 'test_filter_taxonomy_index_tid');
|
||||
$display =& $view->getDisplay('default');
|
||||
$display['display_options']['filters']['tid']['type'] = 'textfield';
|
||||
$view->save();
|
||||
$this->drupalGet('admin/structure/views/nojs/handler/test_filter_taxonomy_index_tid/default/filter/tid');
|
||||
$this->assertFieldByXPath('//input[@id="edit-options-value"]');
|
||||
|
||||
// Tests \Drupal\taxonomy\Plugin\views\filter\TaxonomyIndexTid::calculateDependencies().
|
||||
$expected = [
|
||||
'config' => [
|
||||
'taxonomy.vocabulary.tags',
|
||||
],
|
||||
'content' => [
|
||||
'taxonomy_term:tags:' . Term::load(2)->uuid(),
|
||||
],
|
||||
'module' => [
|
||||
'node',
|
||||
'taxonomy',
|
||||
'user',
|
||||
],
|
||||
];
|
||||
$this->assertIdentical($expected, $view->calculateDependencies());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\Views\TaxonomyParentUITest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests\Views;
|
||||
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
use Drupal\views_ui\Tests\UITestBase;
|
||||
|
||||
/**
|
||||
* Tests views taxonomy parent plugin UI.
|
||||
*
|
||||
* @group taxonomy
|
||||
* @see Drupal\taxonomy\Plugin\views\access\Role
|
||||
*/
|
||||
class TaxonomyParentUITest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_taxonomy_parent');
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('taxonomy', 'taxonomy_test_views');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), array('taxonomy_test_views'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the taxonomy parent plugin UI.
|
||||
*/
|
||||
public function testTaxonomyParentUI() {
|
||||
$this->drupalGet('admin/structure/views/nojs/handler/test_taxonomy_parent/default/relationship/parent');
|
||||
$this->assertNoText('The handler for this item is broken or missing.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\Views\TaxonomyRelationshipTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests\Views;
|
||||
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\taxonomy\TermInterface;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests taxonomy relationships with parent term and node.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TaxonomyRelationshipTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* Stores the terms used in the tests.
|
||||
*
|
||||
* @var \Drupal\taxonomy\TermInterface[]
|
||||
*/
|
||||
protected $terms = array();
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_taxonomy_term_relationship');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Make term2 parent of term1.
|
||||
$this->term1->set('parent', $this->term2->id());
|
||||
$this->term1->save();
|
||||
// Store terms in an array for testing.
|
||||
$this->terms[] = $this->term1;
|
||||
$this->terms[] = $this->term2;
|
||||
// Only set term1 on node1 and term2 on node2 for testing.
|
||||
unset($this->nodes[0]->field_views_testing_tags[1]);
|
||||
$this->nodes[0]->save();
|
||||
unset($this->nodes[1]->field_views_testing_tags[0]);
|
||||
$this->nodes[1]->save();
|
||||
|
||||
Views::viewsData()->clear();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the taxonomy parent plugin UI.
|
||||
*/
|
||||
public function testTaxonomyRelationships() {
|
||||
|
||||
// Check the generated views data of taxonomy_index.
|
||||
$views_data = Views::viewsData()->get('taxonomy_index');
|
||||
// Check the table join data.
|
||||
$this->assertEqual($views_data['table']['join']['taxonomy_term_field_data']['left_field'], 'tid');
|
||||
$this->assertEqual($views_data['table']['join']['taxonomy_term_field_data']['field'], 'tid');
|
||||
$this->assertEqual($views_data['table']['join']['node_field_data']['left_field'], 'nid');
|
||||
$this->assertEqual($views_data['table']['join']['node_field_data']['field'], 'nid');
|
||||
$this->assertEqual($views_data['table']['join']['taxonomy_term_hierarchy']['left_field'], 'tid');
|
||||
$this->assertEqual($views_data['table']['join']['taxonomy_term_hierarchy']['field'], 'tid');
|
||||
|
||||
// Check the generated views data of taxonomy_term_hierarchy.
|
||||
$views_data = Views::viewsData()->get('taxonomy_term_hierarchy');
|
||||
// Check the table join data.
|
||||
$this->assertEqual($views_data['table']['join']['taxonomy_term_hierarchy']['left_field'], 'tid');
|
||||
$this->assertEqual($views_data['table']['join']['taxonomy_term_hierarchy']['field'], 'parent');
|
||||
$this->assertEqual($views_data['table']['join']['taxonomy_term_field_data']['left_field'], 'tid');
|
||||
$this->assertEqual($views_data['table']['join']['taxonomy_term_field_data']['field'], 'tid');
|
||||
// Check the parent relationship data.
|
||||
$this->assertEqual($views_data['parent']['relationship']['base'], 'taxonomy_term_field_data');
|
||||
$this->assertEqual($views_data['parent']['relationship']['field'], 'parent');
|
||||
$this->assertEqual($views_data['parent']['relationship']['label'], t('Parent'));
|
||||
$this->assertEqual($views_data['parent']['relationship']['id'], 'standard');
|
||||
// Check the parent filter and argument data.
|
||||
$this->assertEqual($views_data['parent']['filter']['id'], 'numeric');
|
||||
$this->assertEqual($views_data['parent']['argument']['id'], 'taxonomy');
|
||||
|
||||
// Check an actual test view.
|
||||
$view = Views::getView('test_taxonomy_term_relationship');
|
||||
$this->executeView($view);
|
||||
/** @var \Drupal\views\ResultRow $row */
|
||||
foreach ($view->result as $index => $row) {
|
||||
// Check that the actual ID of the entity is the expected one.
|
||||
$this->assertEqual($row->tid, $this->terms[$index]->id());
|
||||
|
||||
// Also check that we have the correct result entity.
|
||||
$this->assertEqual($row->_entity->id(), $this->terms[$index]->id());
|
||||
$this->assertTrue($row->_entity instanceof TermInterface);
|
||||
|
||||
if (!$index) {
|
||||
$this->assertTrue($row->_relationship_entities['parent'] instanceof TermInterface);
|
||||
$this->assertEqual($row->_relationship_entities['parent']->id(), $this->term2->id());
|
||||
$this->assertEqual($row->taxonomy_term_field_data_taxonomy_term_hierarchy_tid, $this->term2->id());
|
||||
}
|
||||
$this->assertTrue($row->_relationship_entities['nid'] instanceof NodeInterface);
|
||||
$this->assertEqual($row->_relationship_entities['nid']->id(), $this->nodes[$index]->id());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\Views\TaxonomyTermFilterDepthTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests\Views;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Test the taxonomy term with depth filter.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TaxonomyTermFilterDepthTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['taxonomy', 'taxonomy_test_views', 'views', 'node'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $testViews = ['test_filter_taxonomy_index_tid_depth'];
|
||||
|
||||
/**
|
||||
* @var \Drupal\taxonomy\TermInterface[]
|
||||
*/
|
||||
protected $terms = [];
|
||||
|
||||
/**
|
||||
* @var \Drupal\views\ViewExecutable
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a hierarchy 3 deep. Note the parent setup function creates two
|
||||
// top-level terms w/o children.
|
||||
$first = $this->createTerm(['name' => 'First']);
|
||||
$second = $this->createTerm(['name' => 'Second', 'parent' => $first->id()]);
|
||||
$third = $this->createTerm(['name' => 'Third', 'parent' => $second->id()]);
|
||||
|
||||
// Create a node w/o any terms.
|
||||
$settings = ['type' => 'article'];
|
||||
$this->nodes[] = $this->drupalCreateNode($settings);
|
||||
|
||||
// Create a node with only the top level term.
|
||||
$settings['field_views_testing_tags'][0]['target_id'] = $first->id();
|
||||
$this->nodes[] = $this->drupalCreateNode($settings);
|
||||
|
||||
// Create a node with only the third level term.
|
||||
$settings['field_views_testing_tags'][0]['target_id'] = $third->id();
|
||||
$this->nodes[] = $this->drupalCreateNode($settings);
|
||||
|
||||
$this->terms[0] = $first;
|
||||
$this->terms[1] = $second;
|
||||
$this->terms[2] = $third;
|
||||
|
||||
$this->view = Views::getView('test_filter_taxonomy_index_tid_depth');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the terms with depth filter.
|
||||
*/
|
||||
public function testTermWithDepthFilter() {
|
||||
$column_map = ['nid' => 'nid'];
|
||||
$assert_method = 'assertIdentical';
|
||||
|
||||
// Default view has an empty value for this filter, so all nodes should be
|
||||
// returned.
|
||||
$expected = [
|
||||
['nid' => 1],
|
||||
['nid' => 2],
|
||||
['nid' => 3],
|
||||
['nid' => 4],
|
||||
['nid' => 5],
|
||||
];
|
||||
$this->executeView($this->view);
|
||||
$this->assertIdenticalResultsetHelper($this->view, $expected, $column_map, $assert_method);
|
||||
|
||||
// Set filter to search on top-level term, with depth 0.
|
||||
$expected = [['nid' => 4]];
|
||||
$this->assertTermWithDepthResult($this->terms[0]->id(), 0, $expected);
|
||||
|
||||
// Top-level term, depth 1.
|
||||
$expected = [['nid' => 4]];
|
||||
$this->assertTermWithDepthResult($this->terms[0]->id(), 0, $expected);
|
||||
|
||||
// Top-level term, depth 2.
|
||||
$expected = [['nid' => 4], ['nid' => 5]];
|
||||
$this->assertTermWithDepthResult($this->terms[0]->id(), 2, $expected);
|
||||
|
||||
// Second-level term, depth 1.
|
||||
$expected = [['nid' => 5]];
|
||||
$this->assertTermWithDepthResult($this->terms[1]->id(), 1, $expected);
|
||||
|
||||
// Third-level term, depth 0.
|
||||
$expected = [['nid' => 5]];
|
||||
$this->assertTermWithDepthResult($this->terms[2]->id(), 0, $expected);
|
||||
|
||||
// Third-level term, depth 1.
|
||||
$expected = [['nid' => 5]];
|
||||
$this->assertTermWithDepthResult($this->terms[2]->id(), 1, $expected);
|
||||
|
||||
// Third-level term, depth -2.
|
||||
$expected = [['nid' => 4], ['nid' => 5]];
|
||||
$this->assertTermWithDepthResult($this->terms[2]->id(), -2, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the tid filter to given term and depth.
|
||||
*
|
||||
* @param integer $tid
|
||||
* The term ID to filter on.
|
||||
* @param integer $depth
|
||||
* The depth to search.
|
||||
* @param array $expected
|
||||
* The expected views result.
|
||||
*/
|
||||
protected function assertTermWithDepthResult($tid, $depth, array $expected) {
|
||||
$this->view->destroy();
|
||||
$this->view->initDisplay();
|
||||
$filters = $this->view->displayHandlers->get('default')
|
||||
->getOption('filters');
|
||||
$filters['tid_depth']['depth'] = $depth;
|
||||
$filters['tid_depth']['value'] = [$tid];
|
||||
$this->view->displayHandlers->get('default')
|
||||
->setOption('filters', $filters);
|
||||
$this->executeView($this->view);
|
||||
$this->assertIdenticalResultsetHelper($this->view, $expected, ['nid' => 'nid'], 'assertIdentical');
|
||||
}
|
||||
|
||||
}
|
161
core/modules/taxonomy/src/Tests/Views/TaxonomyTermViewTest.php
Normal file
161
core/modules/taxonomy/src/Tests/Views/TaxonomyTermViewTest.php
Normal file
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\Views\TaxonomyTermViewTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests\Views;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\RoleInterface;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* 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');
|
||||
|
||||
/**
|
||||
* An user with permissions to administer taxonomy.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* Name of the taxonomy term reference field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName1;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create an administrative user.
|
||||
$this->adminUser = $this->drupalCreateUser(['administer taxonomy', 'bypass node access']);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Create a vocabulary and add two term reference fields to article nodes.
|
||||
|
||||
$this->fieldName1 = Unicode::strtolower($this->randomMachineName());
|
||||
|
||||
$handler_settings = array(
|
||||
'target_bundles' => array(
|
||||
$this->vocabulary->id() => $this->vocabulary->id(),
|
||||
),
|
||||
'auto_create' => TRUE,
|
||||
);
|
||||
$this->createEntityReferenceField('node', 'article', $this->fieldName1, NULL, 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
|
||||
entity_get_form_display('node', 'article', 'default')
|
||||
->setComponent($this->fieldName1, array(
|
||||
'type' => 'options_select',
|
||||
))
|
||||
->save();
|
||||
entity_get_display('node', 'article', 'default')
|
||||
->setComponent($this->fieldName1, array(
|
||||
'type' => 'entity_reference_label',
|
||||
))
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the taxonomy term view is working properly.
|
||||
*/
|
||||
public function testTaxonomyTermView() {
|
||||
// Create terms in the vocabulary.
|
||||
$term = $this->createTerm();
|
||||
|
||||
// Post an article.
|
||||
$edit = array();
|
||||
$edit['title[0][value]'] = $original_title = $this->randomMachineName();
|
||||
$edit['body[0][value]'] = $this->randomMachineName();
|
||||
$edit["{$this->fieldName1}[]"] = $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::service('module_installer')->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.
|
||||
\Drupal::service('content_translation.manager')->setEnabled('node', 'article', TRUE);
|
||||
$roles = $this->adminUser->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();
|
||||
\Drupal::service('entity.definition_update_manager')->applyUpdates();
|
||||
|
||||
$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);
|
||||
|
||||
// Uninstall language module and ensure that the language is not part of the
|
||||
// query anymore.
|
||||
// @see \Drupal\views\Plugin\views\filter\LanguageFilter::query()
|
||||
$node->delete();
|
||||
\Drupal::service('module_installer')->uninstall(['content_translation', 'language']);
|
||||
|
||||
$view = Views::getView('taxonomy_term');
|
||||
$view->initDisplay();
|
||||
$view->setArguments([$term->id()]);
|
||||
$view->build();
|
||||
/** @var \Drupal\Core\Database\Query\Select $query */
|
||||
$query = $view->build_info['query'];
|
||||
$tables = $query->getTables();
|
||||
|
||||
// Ensure that the join to node_field_data is not added by default.
|
||||
$this->assertEqual(['node_field_data', 'taxonomy_index'], array_keys($tables));
|
||||
// Ensure that the filter to the language column is not there by default.
|
||||
$condition = $query->conditions();
|
||||
// We only want to check the no. of conditions in the query.
|
||||
unset($condition['#conjunction']);
|
||||
$this->assertEqual(1, count($condition));
|
||||
|
||||
// Clear permissions for anonymous users to check access for default views.
|
||||
Role::load(RoleInterface::ANONYMOUS_ID)->revokePermission('access content')->save();
|
||||
|
||||
// Test the default views disclose no data by default.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('taxonomy/term/' . $term->id());
|
||||
$this->assertResponse(403);
|
||||
$this->drupalGet('taxonomy/term/' . $term->id() . '/feed');
|
||||
$this->assertResponse(403);
|
||||
}
|
||||
|
||||
}
|
146
core/modules/taxonomy/src/Tests/Views/TaxonomyTestBase.php
Normal file
146
core/modules/taxonomy/src/Tests/Views/TaxonomyTestBase.php
Normal file
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\Views\TaxonomyTestBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests\Views;
|
||||
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\entity_reference\Tests\EntityReferenceTestTrait;
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
|
||||
/**
|
||||
* Base class for all taxonomy tests.
|
||||
*/
|
||||
abstract class TaxonomyTestBase extends ViewTestBase {
|
||||
|
||||
use EntityReferenceTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('taxonomy', 'taxonomy_test_views');
|
||||
|
||||
/**
|
||||
* Stores the nodes used for the different tests.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface[]
|
||||
*/
|
||||
protected $nodes = array();
|
||||
|
||||
/**
|
||||
* The vocabulary used for creating terms.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyInterface
|
||||
*/
|
||||
protected $vocabulary;
|
||||
|
||||
/**
|
||||
* Stores the first term used in the different tests.
|
||||
*
|
||||
* @var \Drupal\taxonomy\TermInterface
|
||||
*/
|
||||
protected $term1;
|
||||
|
||||
/**
|
||||
* Stores the second term used in the different tests.
|
||||
*
|
||||
* @var \Drupal\taxonomy\TermInterface
|
||||
*/
|
||||
protected $term2;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->mockStandardInstall();
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), array('taxonomy_test_views'));
|
||||
|
||||
$this->term1 = $this->createTerm();
|
||||
$this->term2 = $this->createTerm();
|
||||
|
||||
$node = array();
|
||||
$node['type'] = 'article';
|
||||
$node['field_views_testing_tags'][]['target_id'] = $this->term1->id();
|
||||
$node['field_views_testing_tags'][]['target_id'] = $this->term2->id();
|
||||
$this->nodes[] = $this->drupalCreateNode($node);
|
||||
$this->nodes[] = $this->drupalCreateNode($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a workaround for the inability to use the standard profile.
|
||||
*
|
||||
* @see https://www.drupal.org/node/1708692
|
||||
*/
|
||||
protected function mockStandardInstall() {
|
||||
$this->drupalCreateContentType(array(
|
||||
'type' => 'article',
|
||||
));
|
||||
// Create the vocabulary for the tag field.
|
||||
$this->vocabulary = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => 'Views testing tags',
|
||||
'vid' => 'views_testing_tags',
|
||||
));
|
||||
$this->vocabulary->save();
|
||||
$field_name = 'field_' . $this->vocabulary->id();
|
||||
|
||||
$handler_settings = array(
|
||||
'target_bundles' => array(
|
||||
$this->vocabulary->id() => $this->vocabulary->id(),
|
||||
),
|
||||
'auto_create' => TRUE,
|
||||
);
|
||||
$this->createEntityReferenceField('node', 'article', $field_name, 'Tags', 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
|
||||
entity_get_form_display('node', 'article', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'entity_reference_autocomplete_tags',
|
||||
'weight' => -4,
|
||||
))
|
||||
->save();
|
||||
|
||||
entity_get_display('node', 'article', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'entity_reference_label',
|
||||
'weight' => 10,
|
||||
))
|
||||
->save();
|
||||
entity_get_display('node', 'article', 'teaser')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'entity_reference_label',
|
||||
'weight' => 10,
|
||||
))
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new term with random properties in vocabulary $vid.
|
||||
*
|
||||
* @param array $settings
|
||||
* (Optional) An associative array of settings to pass to `entity_create`.
|
||||
*
|
||||
* @return \Drupal\taxonomy\Entity\Term
|
||||
* The created taxonomy term.
|
||||
*/
|
||||
protected function createTerm(array $settings = []) {
|
||||
$filter_formats = filter_formats();
|
||||
$format = array_pop($filter_formats);
|
||||
$settings += [
|
||||
'name' => $this->randomMachineName(),
|
||||
'description' => $this->randomMachineName(),
|
||||
// Use the first available text format.
|
||||
'format' => $format->id(),
|
||||
'vid' => $this->vocabulary->id(),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
];
|
||||
$term = entity_create('taxonomy_term', $settings);
|
||||
$term->save();
|
||||
return $term;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\Views\TaxonomyViewsFieldAccessTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests\Views;
|
||||
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\views\Tests\Handler\FieldFieldAccessTestBase;
|
||||
|
||||
/**
|
||||
* Tests base field access in Views for the taxonomy entity.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class TaxonomyViewsFieldAccessTest extends FieldFieldAccessTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['taxonomy', 'text', 'entity_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check access for taxonomy fields.
|
||||
*/
|
||||
public function testTermFields() {
|
||||
$vocab = Vocabulary::create([
|
||||
'vid' => 'random',
|
||||
'name' => 'Randomness',
|
||||
]);
|
||||
$vocab->save();
|
||||
$term1 = Term::create([
|
||||
'name' => 'Semi random',
|
||||
'vid' => $vocab->id(),
|
||||
]);
|
||||
$term1->save();
|
||||
|
||||
$term2 = Term::create([
|
||||
'name' => 'Majorly random',
|
||||
'vid' => $vocab->id(),
|
||||
]);
|
||||
$term2->save();
|
||||
|
||||
$term3 = Term::create([
|
||||
'name' => 'Not really random',
|
||||
'vid' => $vocab->id(),
|
||||
]);
|
||||
$term3->save();
|
||||
|
||||
$this->assertFieldAccess('taxonomy_term', 'name', 'Majorly random');
|
||||
$this->assertFieldAccess('taxonomy_term', 'name', 'Semi random');
|
||||
$this->assertFieldAccess('taxonomy_term', 'name', 'Not really random');
|
||||
$this->assertFieldAccess('taxonomy_term', 'tid', $term1->id());
|
||||
$this->assertFieldAccess('taxonomy_term', 'tid', $term2->id());
|
||||
$this->assertFieldAccess('taxonomy_term', 'tid', $term3->id());
|
||||
$this->assertFieldAccess('taxonomy_term', 'uuid', $term1->uuid());
|
||||
$this->assertFieldAccess('taxonomy_term', 'uuid', $term2->uuid());
|
||||
$this->assertFieldAccess('taxonomy_term', 'uuid', $term3->uuid());
|
||||
}
|
||||
|
||||
}
|
53
core/modules/taxonomy/src/Tests/Views/TermNameFieldTest.php
Normal file
53
core/modules/taxonomy/src/Tests/Views/TermNameFieldTest.php
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\Views\TermNameFieldTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests\Views;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the term_name field handler.
|
||||
*
|
||||
* @group taxonomy
|
||||
*
|
||||
* @see \Drupal\taxonomy\Plugin\views\field\TermName
|
||||
*/
|
||||
class TermNameFieldTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $testViews = array('test_taxonomy_term_name');
|
||||
|
||||
/**
|
||||
* Tests term name field plugin functionality.
|
||||
*/
|
||||
public function testTermNameField() {
|
||||
$this->term1->name->value = $this->randomMachineName() . ' ' . $this->randomMachineName();
|
||||
$this->term1->save();
|
||||
|
||||
$user = $this->drupalCreateUser(['access content']);
|
||||
$this->drupalLogin($user);
|
||||
|
||||
$view = Views::getView('test_taxonomy_term_name');
|
||||
$view->initDisplay();
|
||||
$this->executeView($view);
|
||||
|
||||
$this->assertEqual($this->term1->getName(), $view->getStyle()->getField(0, 'name'));
|
||||
$this->assertEqual($this->term2->getName(), $view->getStyle()->getField(1, 'name'));
|
||||
|
||||
$view = Views::getView('test_taxonomy_term_name');
|
||||
$display =& $view->storage->getDisplay('default');
|
||||
$display['display_options']['fields']['name']['convert_spaces'] = TRUE;
|
||||
$view->storage->invalidateCaches();
|
||||
$this->executeView($view);
|
||||
|
||||
$this->assertEqual(str_replace(' ', '-', $this->term1->getName()), $view->getStyle()->getField(0, 'name'));
|
||||
$this->assertEqual($this->term2->getName(), $view->getStyle()->getField(1, 'name'));
|
||||
}
|
||||
|
||||
}
|
216
core/modules/taxonomy/src/Tests/VocabularyCrudTest.php
Normal file
216
core/modules/taxonomy/src/Tests/VocabularyCrudTest.php
Normal file
|
@ -0,0 +1,216 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\VocabularyCrudTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
|
||||
/**
|
||||
* Tests loading, saving and deleting vocabularies.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class VocabularyCrudTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('field_test', 'taxonomy_crud');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$admin_user = $this->drupalCreateUser(array('create article content', 'administer taxonomy'));
|
||||
$this->drupalLogin($admin_user);
|
||||
$this->vocabulary = $this->createVocabulary();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test deleting a taxonomy that contains terms.
|
||||
*/
|
||||
function testTaxonomyVocabularyDeleteWithTerms() {
|
||||
// Delete any existing vocabularies.
|
||||
foreach (Vocabulary::loadMultiple() as $vocabulary) {
|
||||
$vocabulary->delete();
|
||||
}
|
||||
$query = \Drupal::entityQuery('taxonomy_term')->count();
|
||||
|
||||
// Assert that there are no terms left.
|
||||
$this->assertEqual(0, $query->execute(), 'There are no terms remaining.');
|
||||
|
||||
$terms = array();
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$terms[$i] = $this->createTerm($vocabulary);
|
||||
}
|
||||
|
||||
// Set up hierarchy. term 2 is a child of 1 and 4 a child of 1 and 2.
|
||||
$terms[2]->parent = array($terms[1]->id());
|
||||
$terms[2]->save();
|
||||
$terms[4]->parent = array($terms[1]->id(), $terms[2]->id());
|
||||
$terms[4]->save();
|
||||
|
||||
// Assert that there are now 5 terms.
|
||||
$this->assertEqual(5, $query->execute(), 'There are 5 terms found.');
|
||||
|
||||
$vocabulary->delete();
|
||||
|
||||
// Assert that there are no terms left.
|
||||
$this->assertEqual(0, $query->execute(), 'All terms are deleted.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the vocabulary static reset works correctly.
|
||||
*/
|
||||
function testTaxonomyVocabularyLoadStaticReset() {
|
||||
$original_vocabulary = Vocabulary::load($this->vocabulary->id());
|
||||
$this->assertTrue(is_object($original_vocabulary), 'Vocabulary loaded successfully.');
|
||||
$this->assertEqual($this->vocabulary->label(), $original_vocabulary->label(), 'Vocabulary loaded successfully.');
|
||||
|
||||
// Change the name and description.
|
||||
$vocabulary = $original_vocabulary;
|
||||
$vocabulary->set('name', $this->randomMachineName());
|
||||
$vocabulary->set('description', $this->randomMachineName());
|
||||
$vocabulary->save();
|
||||
|
||||
// Load the vocabulary.
|
||||
$new_vocabulary = Vocabulary::load($original_vocabulary->id());
|
||||
$this->assertEqual($new_vocabulary->label(), $vocabulary->label(), 'The vocabulary was loaded.');
|
||||
|
||||
// Delete the vocabulary.
|
||||
$this->vocabulary->delete();
|
||||
$vocabularies = Vocabulary::loadMultiple();
|
||||
$this->assertTrue(!isset($vocabularies[$this->vocabulary->id()]), 'The vocabulary was deleted.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for loading multiple vocabularies.
|
||||
*/
|
||||
function testTaxonomyVocabularyLoadMultiple() {
|
||||
|
||||
// Delete any existing vocabularies.
|
||||
foreach (Vocabulary::loadMultiple() as $vocabulary) {
|
||||
$vocabulary->delete();
|
||||
}
|
||||
|
||||
// Create some vocabularies and assign weights.
|
||||
$vocabulary1 = $this->createVocabulary();
|
||||
$vocabulary1->set('weight', 0);
|
||||
$vocabulary1->save();
|
||||
$vocabulary2 = $this->createVocabulary();
|
||||
$vocabulary2->set('weight', 1);
|
||||
$vocabulary2->save();
|
||||
$vocabulary3 = $this->createVocabulary();
|
||||
$vocabulary3->set('weight', 2);
|
||||
$vocabulary3->save();
|
||||
|
||||
// Check if third party settings exist.
|
||||
$this->assertEqual('bar', $vocabulary1->getThirdPartySetting('taxonomy_crud', 'foo'), 'Third party settings were added to the vocabulary.');
|
||||
$this->assertEqual('bar', $vocabulary2->getThirdPartySetting('taxonomy_crud', 'foo'), 'Third party settings were added to the vocabulary.');
|
||||
$this->assertEqual('bar', $vocabulary3->getThirdPartySetting('taxonomy_crud', 'foo'), 'Third party settings were added to the vocabulary.');
|
||||
|
||||
// Fetch the names for all vocabularies, confirm that they are keyed by
|
||||
// machine name.
|
||||
$names = taxonomy_vocabulary_get_names();
|
||||
$this->assertEqual($names[$vocabulary1->id()], $vocabulary1->id(), 'Vocabulary 1 name found.');
|
||||
|
||||
// Fetch the vocabularies with entity_load_multiple(), specifying IDs.
|
||||
// Ensure they are returned in the same order as the original array.
|
||||
$vocabularies = Vocabulary::loadMultiple(array($vocabulary3->id(), $vocabulary2->id(), $vocabulary1->id()));
|
||||
$loaded_order = array_keys($vocabularies);
|
||||
$expected_order = array($vocabulary3->id(), $vocabulary2->id(), $vocabulary1->id());
|
||||
$this->assertIdentical($loaded_order, $expected_order);
|
||||
|
||||
// Test loading vocabularies by their properties.
|
||||
$controller = $this->container->get('entity.manager')->getStorage('taxonomy_vocabulary');
|
||||
// Fetch vocabulary 1 by name.
|
||||
$vocabulary = current($controller->loadByProperties(array('name' => $vocabulary1->label())));
|
||||
$this->assertEqual($vocabulary->id(), $vocabulary1->id(), 'Vocabulary loaded successfully by name.');
|
||||
|
||||
// Fetch vocabulary 2 by name and ID.
|
||||
$vocabulary = current($controller->loadByProperties(array(
|
||||
'name' => $vocabulary2->label(),
|
||||
'vid' => $vocabulary2->id(),
|
||||
)));
|
||||
$this->assertEqual($vocabulary->id(), $vocabulary2->id(), 'Vocabulary loaded successfully by name and ID.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that machine name changes are properly reflected.
|
||||
*/
|
||||
function testTaxonomyVocabularyChangeMachineName() {
|
||||
// Add a field to the vocabulary.
|
||||
entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_test',
|
||||
'entity_type' => 'taxonomy_term',
|
||||
'type' => 'test_field',
|
||||
))->save();
|
||||
entity_create('field_config', array(
|
||||
'field_name' => 'field_test',
|
||||
'entity_type' => 'taxonomy_term',
|
||||
'bundle' => $this->vocabulary->id(),
|
||||
))->save();
|
||||
|
||||
// Change the machine name.
|
||||
$old_name = $this->vocabulary->id();
|
||||
$new_name = Unicode::strtolower($this->randomMachineName());
|
||||
$this->vocabulary->set('vid', $new_name);
|
||||
$this->vocabulary->save();
|
||||
|
||||
// Check that entity bundles are properly updated.
|
||||
$info = entity_get_bundles('taxonomy_term');
|
||||
$this->assertFalse(isset($info[$old_name]), 'The old bundle name does not appear in entity_get_bundles().');
|
||||
$this->assertTrue(isset($info[$new_name]), 'The new bundle name appears in entity_get_bundles().');
|
||||
|
||||
// Check that the field is still attached to the vocabulary.
|
||||
$this->assertTrue(FieldConfig::loadByName('taxonomy_term', $new_name, 'field_test'), 'The bundle name was updated correctly.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test uninstall and reinstall of the taxonomy module.
|
||||
*/
|
||||
function testUninstallReinstall() {
|
||||
// Field storages and fields attached to taxonomy term bundles should be
|
||||
// removed when the module is uninstalled.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName() . '_field_name');
|
||||
$storage_definition = array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'taxonomy_term',
|
||||
'type' => 'text',
|
||||
'cardinality' => 4
|
||||
);
|
||||
entity_create('field_storage_config', $storage_definition)->save();
|
||||
$field_definition = array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'taxonomy_term',
|
||||
'bundle' => $this->vocabulary->id(),
|
||||
'label' => $this->randomMachineName() . '_label',
|
||||
);
|
||||
entity_create('field_config', $field_definition)->save();
|
||||
|
||||
// Remove the third party setting from the memory copy of the vocabulary.
|
||||
// We keep this invalid copy around while the taxonomy module is not even
|
||||
// installed for testing below.
|
||||
$this->vocabulary->unsetThirdPartySetting('taxonomy_crud', 'foo');
|
||||
|
||||
require_once \Drupal::root() . '/core/includes/install.inc';
|
||||
$this->container->get('module_installer')->uninstall(array('taxonomy'));
|
||||
$this->container->get('module_installer')->install(array('taxonomy'));
|
||||
|
||||
// Now create a vocabulary with the same name. All fields
|
||||
// connected to this vocabulary name should have been removed when the
|
||||
// module was uninstalled. Creating a new field with the same name and
|
||||
// an instance of this field on the same bundle name should be successful.
|
||||
$this->vocabulary->enforceIsNew();
|
||||
$this->vocabulary->save();
|
||||
entity_create('field_storage_config', $storage_definition)->save();
|
||||
entity_create('field_config', $field_definition)->save();
|
||||
}
|
||||
}
|
129
core/modules/taxonomy/src/Tests/VocabularyLanguageTest.php
Normal file
129
core/modules/taxonomy/src/Tests/VocabularyLanguageTest.php
Normal file
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\VocabularyLanguageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\language\Entity\ContentLanguageSettings;
|
||||
|
||||
/**
|
||||
* Tests the language functionality for vocabularies.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class VocabularyLanguageTest extends TaxonomyTestBase {
|
||||
|
||||
public static $modules = array('language');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create an administrative user.
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer taxonomy']));
|
||||
|
||||
// Add some custom languages.
|
||||
ConfigurableLanguage::create(array(
|
||||
'id' => 'aa',
|
||||
'label' => $this->randomMachineName(),
|
||||
))->save();
|
||||
|
||||
ConfigurableLanguage::create(array(
|
||||
'id' => 'bb',
|
||||
'label' => $this->randomMachineName(),
|
||||
))->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests language settings for vocabularies.
|
||||
*/
|
||||
function testVocabularyLanguage() {
|
||||
$this->drupalGet('admin/structure/taxonomy/add');
|
||||
|
||||
// Check that we have the language selector available.
|
||||
$this->assertField('edit-langcode', 'The language selector field was found on the page.');
|
||||
|
||||
// Create the vocabulary.
|
||||
$vid = Unicode::strtolower($this->randomMachineName());
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$edit['description'] = $this->randomMachineName();
|
||||
$edit['langcode'] = 'aa';
|
||||
$edit['vid'] = $vid;
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
// Check the language on the edit page.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $vid);
|
||||
$this->assertOptionSelected('edit-langcode', $edit['langcode'], 'The vocabulary language was correctly selected.');
|
||||
|
||||
// Change the language and save again.
|
||||
$edit['langcode'] = 'bb';
|
||||
unset($edit['vid']);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
// Check again the language on the edit page.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $vid);
|
||||
$this->assertOptionSelected('edit-langcode', $edit['langcode'], 'The vocabulary language was correctly selected.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests term language settings for vocabulary terms are saved and updated.
|
||||
*/
|
||||
function testVocabularyDefaultLanguageForTerms() {
|
||||
// Add a new vocabulary and check that the default language settings are for
|
||||
// the terms are saved.
|
||||
$edit = array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'vid' => Unicode::strtolower($this->randomMachineName()),
|
||||
'default_language[langcode]' => 'bb',
|
||||
'default_language[language_alterable]' => TRUE,
|
||||
);
|
||||
$vid = $edit['vid'];
|
||||
$this->drupalPostForm('admin/structure/taxonomy/add', $edit, t('Save'));
|
||||
|
||||
// Check that the vocabulary was actually created.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $edit['vid']);
|
||||
$this->assertResponse(200, 'The vocabulary has been created.');
|
||||
|
||||
// Check that the language settings were saved.
|
||||
$language_settings = ContentLanguageSettings::loadByEntityTypeBundle('taxonomy_term', $edit['vid']);
|
||||
$this->assertEqual($language_settings->getDefaultLangcode(), 'bb', 'The langcode was saved.');
|
||||
$this->assertTrue($language_settings->isLanguageAlterable(), 'The visibility setting was saved.');
|
||||
|
||||
// Check that the correct options are selected in the interface.
|
||||
$this->assertOptionSelected('edit-default-language-langcode', 'bb', 'The correct default language for the terms of this vocabulary is selected.');
|
||||
$this->assertFieldChecked('edit-default-language-language-alterable', 'Show language selection option is checked.');
|
||||
|
||||
// Edit the vocabulary and check that the new settings are updated.
|
||||
$edit = array(
|
||||
'default_language[langcode]' => 'aa',
|
||||
'default_language[language_alterable]' => FALSE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/taxonomy/manage/' . $vid, $edit, t('Save'));
|
||||
|
||||
// And check again the settings and also the interface.
|
||||
$language_settings = ContentLanguageSettings::loadByEntityTypeBundle('taxonomy_term', $vid);
|
||||
$this->assertEqual($language_settings->getDefaultLangcode(), 'aa', 'The langcode was saved.');
|
||||
$this->assertFalse($language_settings->isLanguageAlterable(), 'The visibility setting was saved.');
|
||||
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $vid);
|
||||
$this->assertOptionSelected('edit-default-language-langcode', 'aa', 'The correct default language for the terms of this vocabulary is selected.');
|
||||
$this->assertNoFieldChecked('edit-default-language-language-alterable', 'Show language selection option is not checked.');
|
||||
|
||||
// Check that language settings are changed after editing vocabulary.
|
||||
$edit = array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'default_language[langcode]' => 'authors_default',
|
||||
'default_language[language_alterable]' => FALSE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/taxonomy/manage/' . $vid, $edit, t('Save'));
|
||||
|
||||
// Check that we have the new settings.
|
||||
$new_settings = ContentLanguageSettings::loadByEntityTypeBundle('taxonomy_term', $vid);
|
||||
$this->assertEqual($new_settings->getDefaultLangcode(), 'authors_default', 'The langcode was saved.');
|
||||
$this->assertFalse($new_settings->isLanguageAlterable(), 'The new visibility setting was saved.');
|
||||
}
|
||||
}
|
126
core/modules/taxonomy/src/Tests/VocabularyPermissionsTest.php
Normal file
126
core/modules/taxonomy/src/Tests/VocabularyPermissionsTest.php
Normal file
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\VocabularyPermissionsTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
/**
|
||||
* Tests the taxonomy vocabulary permissions.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class VocabularyPermissionsTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* Create, edit and delete a taxonomy term via the user interface.
|
||||
*/
|
||||
function testVocabularyPermissionsTaxonomyTerm() {
|
||||
// Vocabulary used for creating, removing and editing terms.
|
||||
$vocabulary = $this->createVocabulary();
|
||||
|
||||
// Test as admin user.
|
||||
$user = $this->drupalCreateUser(array('administer taxonomy'));
|
||||
$this->drupalLogin($user);
|
||||
|
||||
// Visit the main taxonomy administration page.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id() . '/add');
|
||||
$this->assertResponse(200);
|
||||
$this->assertField('edit-name-0-value', 'Add taxonomy term form opened successfully.');
|
||||
|
||||
// Submit the term.
|
||||
$edit = array();
|
||||
$edit['name[0][value]'] = $this->randomMachineName();
|
||||
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('Created new term %name.', array('%name' => $edit['name[0][value]'])), 'Term created successfully.');
|
||||
|
||||
$terms = taxonomy_term_load_multiple_by_name($edit['name[0][value]']);
|
||||
$term = reset($terms);
|
||||
|
||||
// Edit the term.
|
||||
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($edit['name[0][value]'], 'Edit taxonomy term form opened successfully.');
|
||||
|
||||
$edit['name[0][value]'] = $this->randomMachineName();
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('Updated term %name.', array('%name' => $edit['name[0][value]'])), 'Term updated successfully.');
|
||||
|
||||
// Delete the vocabulary.
|
||||
$this->drupalGet('taxonomy/term/' . $term->id() . '/delete');
|
||||
$this->assertRaw(t('Are you sure you want to delete the @entity-type %label?', array('@entity-type' => 'taxonomy term', '%label' => $edit['name[0][value]'])), 'Delete taxonomy term form opened successfully.');
|
||||
|
||||
// Confirm deletion.
|
||||
$this->drupalPostForm(NULL, NULL, t('Delete'));
|
||||
$this->assertRaw(t('Deleted term %name.', array('%name' => $edit['name[0][value]'])), 'Term deleted.');
|
||||
|
||||
// Test as user with "edit" permissions.
|
||||
$user = $this->drupalCreateUser(array("edit terms in {$vocabulary->id()}"));
|
||||
$this->drupalLogin($user);
|
||||
|
||||
// Visit the main taxonomy administration page.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id() . '/add');
|
||||
$this->assertResponse(403, 'Add taxonomy term form open failed.');
|
||||
|
||||
// Create a test term.
|
||||
$term = $this->createTerm($vocabulary);
|
||||
|
||||
// Edit the term.
|
||||
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($term->getName(), 'Edit taxonomy term form opened successfully.');
|
||||
|
||||
$edit['name[0][value]'] = $this->randomMachineName();
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('Updated term %name.', array('%name' => $edit['name[0][value]'])), 'Term updated successfully.');
|
||||
|
||||
// Delete the vocabulary.
|
||||
$this->drupalGet('taxonomy/term/' . $term->id() . '/delete');
|
||||
$this->assertResponse(403, 'Delete taxonomy term form open failed.');
|
||||
|
||||
// Test as user with "delete" permissions.
|
||||
$user = $this->drupalCreateUser(array("delete terms in {$vocabulary->id()}"));
|
||||
$this->drupalLogin($user);
|
||||
|
||||
// Visit the main taxonomy administration page.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id() . '/add');
|
||||
$this->assertResponse(403, 'Add taxonomy term form open failed.');
|
||||
|
||||
// Create a test term.
|
||||
$term = $this->createTerm($vocabulary);
|
||||
|
||||
// Edit the term.
|
||||
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
|
||||
$this->assertResponse(403, 'Edit taxonomy term form open failed.');
|
||||
|
||||
// Delete the vocabulary.
|
||||
$this->drupalGet('taxonomy/term/' . $term->id() . '/delete');
|
||||
$this->assertRaw(t('Are you sure you want to delete the @entity-type %label?', array('@entity-type' => 'taxonomy term', '%label' => $term->getName())), 'Delete taxonomy term form opened successfully.');
|
||||
|
||||
// Confirm deletion.
|
||||
$this->drupalPostForm(NULL, NULL, t('Delete'));
|
||||
$this->assertRaw(t('Deleted term %name.', array('%name' => $term->getName())), 'Term deleted.');
|
||||
|
||||
// Test as user without proper permissions.
|
||||
$user = $this->drupalCreateUser();
|
||||
$this->drupalLogin($user);
|
||||
|
||||
// Visit the main taxonomy administration page.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id() . '/add');
|
||||
$this->assertResponse(403, 'Add taxonomy term form open failed.');
|
||||
|
||||
// Create a test term.
|
||||
$term = $this->createTerm($vocabulary);
|
||||
|
||||
// Edit the term.
|
||||
$this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
|
||||
$this->assertResponse(403, 'Edit taxonomy term form open failed.');
|
||||
|
||||
// Delete the vocabulary.
|
||||
$this->drupalGet('taxonomy/term/' . $term->id() . '/delete');
|
||||
$this->assertResponse(403, 'Delete taxonomy term form open failed.');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\VocabularyTranslationTest.
|
||||
*/
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
|
||||
/**
|
||||
* Tests content translation for vocabularies.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class VocabularyTranslationTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('content_translation', 'language');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create an administrative user.
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'administer taxonomy',
|
||||
'administer content translation',
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests language settings for vocabularies.
|
||||
*/
|
||||
function testVocabularyLanguage() {
|
||||
$this->drupalGet('admin/structure/taxonomy/add');
|
||||
|
||||
// Check that the field to enable content translation is available.
|
||||
$this->assertField('edit-default-language-content-translation', 'The content translation checkbox is present on the page.');
|
||||
|
||||
// Create the vocabulary.
|
||||
$vid = Unicode::strtolower($this->randomMachineName());
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$edit['description'] = $this->randomMachineName();
|
||||
$edit['langcode'] = 'en';
|
||||
$edit['vid'] = $vid;
|
||||
$edit['default_language[content_translation]'] = TRUE;
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
// Check if content translation is enabled on the edit page.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $vid);
|
||||
$this->assertFieldChecked('edit-default-language-content-translation', 'The content translation was correctly selected.');
|
||||
}
|
||||
|
||||
}
|
159
core/modules/taxonomy/src/Tests/VocabularyUiTest.php
Normal file
159
core/modules/taxonomy/src/Tests/VocabularyUiTest.php
Normal file
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\Tests\VocabularyUiTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy\Tests;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
|
||||
/**
|
||||
* Tests the taxonomy vocabulary interface.
|
||||
*
|
||||
* @group taxonomy
|
||||
*/
|
||||
class VocabularyUiTest extends TaxonomyTestBase {
|
||||
|
||||
/**
|
||||
* The vocabulary used for creating terms.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyInterface
|
||||
*/
|
||||
protected $vocabulary;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer taxonomy']));
|
||||
$this->vocabulary = $this->createVocabulary();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create, edit and delete a vocabulary via the user interface.
|
||||
*/
|
||||
function testVocabularyInterface() {
|
||||
// Visit the main taxonomy administration page.
|
||||
$this->drupalGet('admin/structure/taxonomy');
|
||||
|
||||
// Create a new vocabulary.
|
||||
$this->clickLink(t('Add vocabulary'));
|
||||
$edit = array();
|
||||
$vid = Unicode::strtolower($this->randomMachineName());
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$edit['description'] = $this->randomMachineName();
|
||||
$edit['vid'] = $vid;
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('Created new vocabulary %name.', array('%name' => $edit['name'])), 'Vocabulary created successfully.');
|
||||
|
||||
// Edit the vocabulary.
|
||||
$this->drupalGet('admin/structure/taxonomy');
|
||||
$this->assertText($edit['name'], 'Vocabulary found in the vocabulary overview listing.');
|
||||
$this->assertLinkByHref(Url::fromRoute('entity.taxonomy_term.add_form', ['taxonomy_vocabulary' => $edit['vid']])->toString());
|
||||
$this->clickLink(t('Edit vocabulary'));
|
||||
$edit = array();
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->drupalGet('admin/structure/taxonomy');
|
||||
$this->assertText($edit['name'], 'Vocabulary found in the vocabulary overview listing.');
|
||||
|
||||
// Try to submit a vocabulary with a duplicate machine name.
|
||||
$edit['vid'] = $vid;
|
||||
$this->drupalPostForm('admin/structure/taxonomy/add', $edit, t('Save'));
|
||||
$this->assertText(t('The machine-readable name is already in use. It must be unique.'));
|
||||
|
||||
// Try to submit an invalid machine name.
|
||||
$edit['vid'] = '!&^%';
|
||||
$this->drupalPostForm('admin/structure/taxonomy/add', $edit, t('Save'));
|
||||
$this->assertText(t('The machine-readable name must contain only lowercase letters, numbers, and underscores.'));
|
||||
|
||||
// Ensure that vocabulary titles are escaped properly.
|
||||
$edit = array();
|
||||
$edit['name'] = 'Don\'t Panic';
|
||||
$edit['description'] = $this->randomMachineName();
|
||||
$edit['vid'] = 'don_t_panic';
|
||||
$this->drupalPostForm('admin/structure/taxonomy/add', $edit, t('Save'));
|
||||
|
||||
$site_name = $this->config('system.site')->get('name');
|
||||
$this->assertTitle(t('Don\'t Panic | @site-name', array('@site-name' => $site_name)), 'The page title contains the escaped character.');
|
||||
$this->assertNoTitle(t('Don't Panic | @site-name', array('@site-name' => $site_name)), 'The page title does not contain an encoded character.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Changing weights on the vocabulary overview with two or more vocabularies.
|
||||
*/
|
||||
function testTaxonomyAdminChangingWeights() {
|
||||
// Create some vocabularies.
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$this->createVocabulary();
|
||||
}
|
||||
// Get all vocabularies and change their weights.
|
||||
$vocabularies = Vocabulary::loadMultiple();
|
||||
$edit = array();
|
||||
foreach ($vocabularies as $key => $vocabulary) {
|
||||
$weight = -$vocabulary->get('weight');
|
||||
$vocabularies[$key]->set('weight', $weight);
|
||||
$edit['vocabularies[' . $key . '][weight]'] = $weight;
|
||||
}
|
||||
// Saving the new weights via the interface.
|
||||
$this->drupalPostForm('admin/structure/taxonomy', $edit, t('Save'));
|
||||
|
||||
// Load the vocabularies from the database.
|
||||
$this->container->get('entity.manager')->getStorage('taxonomy_vocabulary')->resetCache();
|
||||
$new_vocabularies = Vocabulary::loadMultiple();
|
||||
|
||||
// Check that the weights are saved in the database correctly.
|
||||
foreach ($vocabularies as $key => $vocabulary) {
|
||||
$this->assertEqual($new_vocabularies[$key]->get('weight'), $vocabularies[$key]->get('weight'), 'The vocabulary weight was changed.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the vocabulary overview with no vocabularies.
|
||||
*/
|
||||
function testTaxonomyAdminNoVocabularies() {
|
||||
// Delete all vocabularies.
|
||||
$vocabularies = Vocabulary::loadMultiple();
|
||||
foreach ($vocabularies as $key => $vocabulary) {
|
||||
$vocabulary->delete();
|
||||
}
|
||||
// Confirm that no vocabularies are found in the database.
|
||||
$this->assertFalse(Vocabulary::loadMultiple(), 'No vocabularies found.');
|
||||
$this->drupalGet('admin/structure/taxonomy');
|
||||
// Check the default message for no vocabularies.
|
||||
$this->assertText(t('No vocabularies available.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deleting a vocabulary.
|
||||
*/
|
||||
function testTaxonomyAdminDeletingVocabulary() {
|
||||
// Create a vocabulary.
|
||||
$vid = Unicode::strtolower($this->randomMachineName());
|
||||
$edit = array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'vid' => $vid,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/taxonomy/add', $edit, t('Save'));
|
||||
$this->assertText(t('Created new vocabulary'), 'New vocabulary was created.');
|
||||
|
||||
// Check the created vocabulary.
|
||||
$this->container->get('entity.manager')->getStorage('taxonomy_vocabulary')->resetCache();
|
||||
$vocabulary = Vocabulary::load($vid);
|
||||
$this->assertTrue($vocabulary, 'Vocabulary found.');
|
||||
|
||||
// Delete the vocabulary.
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id());
|
||||
$this->clickLink(t('Delete'));
|
||||
$this->assertRaw(t('Are you sure you want to delete the vocabulary %name?', array('%name' => $vocabulary->label())), '[confirm deletion] Asks for confirmation.');
|
||||
$this->assertText(t('Deleting a vocabulary will delete all the terms in it. This action cannot be undone.'), '[confirm deletion] Inform that all terms will be deleted.');
|
||||
|
||||
// Confirm deletion.
|
||||
$this->drupalPostForm(NULL, NULL, t('Delete'));
|
||||
$this->assertRaw(t('Deleted vocabulary %name.', array('%name' => $vocabulary->label())), 'Vocabulary deleted.');
|
||||
$this->container->get('entity.manager')->getStorage('taxonomy_vocabulary')->resetCache();
|
||||
$this->assertFalse(Vocabulary::load($vid), 'Vocabulary not found.');
|
||||
}
|
||||
}
|
161
core/modules/taxonomy/src/VocabularyForm.php
Normal file
161
core/modules/taxonomy/src/VocabularyForm.php
Normal file
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\VocabularyForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\language\Entity\ContentLanguageSettings;
|
||||
use Drupal\taxonomy\VocabularyStorageInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Base form for vocabulary edit forms.
|
||||
*/
|
||||
class VocabularyForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* The vocabulary storage.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyStorageInterface.
|
||||
*/
|
||||
protected $vocabularyStorage;
|
||||
|
||||
/**
|
||||
* Constructs a new vocabulary form.
|
||||
*
|
||||
* @param \Drupal\taxonomy\VocabularyStorageInterface $vocabulary_storage
|
||||
* The vocabulary storage.
|
||||
*/
|
||||
public function __construct(VocabularyStorageInterface $vocabulary_storage) {
|
||||
$this->vocabularyStorage = $vocabulary_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager')->getStorage('taxonomy_vocabulary')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$vocabulary = $this->entity;
|
||||
if ($vocabulary->isNew()) {
|
||||
$form['#title'] = $this->t('Add vocabulary');
|
||||
}
|
||||
else {
|
||||
$form['#title'] = $this->t('Edit vocabulary');
|
||||
}
|
||||
|
||||
$form['name'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Name'),
|
||||
'#default_value' => $vocabulary->label(),
|
||||
'#maxlength' => 255,
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['vid'] = array(
|
||||
'#type' => 'machine_name',
|
||||
'#default_value' => $vocabulary->id(),
|
||||
'#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
|
||||
'#machine_name' => array(
|
||||
'exists' => array($this, 'exists'),
|
||||
'source' => array('name'),
|
||||
),
|
||||
);
|
||||
$form['description'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Description'),
|
||||
'#default_value' => $vocabulary->getDescription(),
|
||||
);
|
||||
|
||||
// $form['langcode'] is not wrapped in an
|
||||
// if ($this->moduleHandler->moduleExists('language')) check because the
|
||||
// language_select form element works also without the language module being
|
||||
// installed. https://www.drupal.org/node/1749954 documents the new element.
|
||||
$form['langcode'] = array(
|
||||
'#type' => 'language_select',
|
||||
'#title' => $this->t('Vocabulary language'),
|
||||
'#languages' => LanguageInterface::STATE_ALL,
|
||||
'#default_value' => $vocabulary->language()->getId(),
|
||||
);
|
||||
if ($this->moduleHandler->moduleExists('language')) {
|
||||
$form['default_terms_language'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('Terms language'),
|
||||
'#open' => TRUE,
|
||||
);
|
||||
$form['default_terms_language']['default_language'] = array(
|
||||
'#type' => 'language_configuration',
|
||||
'#entity_information' => array(
|
||||
'entity_type' => 'taxonomy_term',
|
||||
'bundle' => $vocabulary->id(),
|
||||
),
|
||||
'#default_value' => ContentLanguageSettings::loadByEntityTypeBundle('taxonomy_term', $vocabulary->id()),
|
||||
);
|
||||
}
|
||||
// Set the hierarchy to "multiple parents" by default. This simplifies the
|
||||
// vocabulary form and standardizes the term form.
|
||||
$form['hierarchy'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => '0',
|
||||
);
|
||||
|
||||
return parent::form($form, $form_state, $vocabulary);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$vocabulary = $this->entity;
|
||||
|
||||
// Prevent leading and trailing spaces in vocabulary names.
|
||||
$vocabulary->set('name', trim($vocabulary->label()));
|
||||
|
||||
$status = $vocabulary->save();
|
||||
$edit_link = $this->entity->link($this->t('Edit'));
|
||||
switch ($status) {
|
||||
case SAVED_NEW:
|
||||
drupal_set_message($this->t('Created new vocabulary %name.', array('%name' => $vocabulary->label())));
|
||||
$this->logger('taxonomy')->notice('Created new vocabulary %name.', array('%name' => $vocabulary->label(), 'link' => $edit_link));
|
||||
$form_state->setRedirectUrl($vocabulary->urlInfo('overview-form'));
|
||||
break;
|
||||
|
||||
case SAVED_UPDATED:
|
||||
drupal_set_message($this->t('Updated vocabulary %name.', array('%name' => $vocabulary->label())));
|
||||
$this->logger('taxonomy')->notice('Updated vocabulary %name.', array('%name' => $vocabulary->label(), 'link' => $edit_link));
|
||||
$form_state->setRedirectUrl($vocabulary->urlInfo('collection'));
|
||||
break;
|
||||
}
|
||||
|
||||
$form_state->setValue('vid', $vocabulary->id());
|
||||
$form_state->set('vid', $vocabulary->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the vocabulary already exists.
|
||||
*
|
||||
* @param string $id
|
||||
* The vocabulary ID
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the vocabulary exists, FALSE otherwise.
|
||||
*/
|
||||
public function exists($id) {
|
||||
$action = $this->vocabularyStorage->load($id);
|
||||
return !empty($action);
|
||||
}
|
||||
|
||||
}
|
46
core/modules/taxonomy/src/VocabularyInterface.php
Normal file
46
core/modules/taxonomy/src/VocabularyInterface.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\VocabularyInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a taxonomy vocabulary entity.
|
||||
*/
|
||||
interface VocabularyInterface extends ConfigEntityInterface {
|
||||
|
||||
/**
|
||||
* Returns the vocabulary hierarchy.
|
||||
*
|
||||
* @return integer
|
||||
* The vocabulary hierarchy.
|
||||
*/
|
||||
public function getHierarchy();
|
||||
|
||||
/**
|
||||
* Sets the vocabulary hierarchy.
|
||||
*
|
||||
* @param integer $hierarchy
|
||||
* The hierarchy type of vocabulary.
|
||||
* Possible values:
|
||||
* - TAXONOMY_HIERARCHY_DISABLED: No parents.
|
||||
* - TAXONOMY_HIERARCHY_SINGLE: Single parent.
|
||||
* - TAXONOMY_HIERARCHY_MULTIPLE: Multiple parents.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setHierarchy($hierarchy);
|
||||
|
||||
/**
|
||||
* Returns the vocabulary description.
|
||||
*
|
||||
* @return string
|
||||
* The vocabulary description.
|
||||
*/
|
||||
public function getDescription();
|
||||
}
|
110
core/modules/taxonomy/src/VocabularyListBuilder.php
Normal file
110
core/modules/taxonomy/src/VocabularyListBuilder.php
Normal file
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\VocabularyListBuilder.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\Config\Entity\DraggableListBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Defines a class to build a listing of taxonomy vocabulary entities.
|
||||
*
|
||||
* @see \Drupal\taxonomy\Entity\Vocabulary
|
||||
*/
|
||||
class VocabularyListBuilder extends DraggableListBuilder {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $entitiesKey = 'vocabularies';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'taxonomy_overview_vocabularies';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultOperations(EntityInterface $entity) {
|
||||
$operations = parent::getDefaultOperations($entity);
|
||||
|
||||
if (isset($operations['edit'])) {
|
||||
$operations['edit']['title'] = t('Edit vocabulary');
|
||||
}
|
||||
|
||||
$operations['list'] = array(
|
||||
'title' => t('List terms'),
|
||||
'weight' => 0,
|
||||
'url' => $entity->urlInfo('overview-form'),
|
||||
);
|
||||
$operations['add'] = array(
|
||||
'title' => t('Add terms'),
|
||||
'weight' => 10,
|
||||
'url' => Url::fromRoute('entity.taxonomy_term.add_form', ['taxonomy_vocabulary' => $entity->id()]),
|
||||
);
|
||||
unset($operations['delete']);
|
||||
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header['label'] = t('Vocabulary name');
|
||||
return $header + parent::buildHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
$row['label'] = $this->getLabel($entity);
|
||||
return $row + parent::buildRow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render() {
|
||||
$entities = $this->load();
|
||||
// If there are not multiple vocabularies, disable dragging by unsetting the
|
||||
// weight key.
|
||||
if (count($entities) <= 1) {
|
||||
unset($this->weightKey);
|
||||
}
|
||||
$build = parent::render();
|
||||
$build['table']['#empty'] = t('No vocabularies available. <a href="@link">Add vocabulary</a>.', array('@link' => \Drupal::url('entity.taxonomy_vocabulary.add_form')));
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::buildForm($form, $form_state);
|
||||
$form['vocabularies']['#attributes'] = array('id' => 'taxonomy');
|
||||
$form['actions']['submit']['#value'] = t('Save');
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitForm($form, $form_state);
|
||||
|
||||
drupal_set_message(t('The configuration options have been saved.'));
|
||||
}
|
||||
|
||||
}
|
33
core/modules/taxonomy/src/VocabularyStorage.php
Normal file
33
core/modules/taxonomy/src/VocabularyStorage.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\VocabularyStorage.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityStorage;
|
||||
|
||||
/**
|
||||
* Defines a controller class for taxonomy vocabularies.
|
||||
*/
|
||||
class VocabularyStorage extends ConfigEntityStorage implements VocabularyStorageInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resetCache(array $ids = NULL) {
|
||||
drupal_static_reset('taxonomy_vocabulary_get_names');
|
||||
parent::resetCache($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getToplevelTids($vids) {
|
||||
return db_query('SELECT t.tid FROM {taxonomy_term_data} t INNER JOIN {taxonomy_term_hierarchy} th ON th.tid = t.tid WHERE t.vid IN ( :vids[] ) AND th.parent = 0', array(':vids[]' => $vids))->fetchCol();
|
||||
}
|
||||
|
||||
}
|
28
core/modules/taxonomy/src/VocabularyStorageInterface.php
Normal file
28
core/modules/taxonomy/src/VocabularyStorageInterface.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\VocabularyStorageInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
|
||||
|
||||
/**
|
||||
* Defines an interface for vocabulary entity storage classes.
|
||||
*/
|
||||
interface VocabularyStorageInterface extends ConfigEntityStorageInterface {
|
||||
|
||||
/**
|
||||
* Gets top-level term IDs of vocabularies.
|
||||
*
|
||||
* @param array $vids
|
||||
* Array of vocabulary IDs.
|
||||
*
|
||||
* @return array
|
||||
* Array of top-level term IDs.
|
||||
*/
|
||||
public function getToplevelTids($vids);
|
||||
|
||||
}
|
11
core/modules/taxonomy/taxonomy.info.yml
Normal file
11
core/modules/taxonomy/taxonomy.info.yml
Normal file
|
@ -0,0 +1,11 @@
|
|||
name: Taxonomy
|
||||
type: module
|
||||
description: 'Enables the categorization of content.'
|
||||
package: Core
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- entity_reference
|
||||
- node
|
||||
- text
|
||||
configure: entity.taxonomy_vocabulary.collection
|
53
core/modules/taxonomy/taxonomy.js
Normal file
53
core/modules/taxonomy/taxonomy.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* @file
|
||||
* Taxonomy behaviors.
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Move a block in the blocks table from one region to another via select list.
|
||||
*
|
||||
* This behavior is dependent on the tableDrag behavior, since it uses the
|
||||
* objects initialized in that behavior to update the row.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.termDrag = {
|
||||
attach: function (context, settings) {
|
||||
var backStep = settings.taxonomy.backStep;
|
||||
var forwardStep = settings.taxonomy.forwardStep;
|
||||
// Get the blocks tableDrag object.
|
||||
var tableDrag = Drupal.tableDrag.taxonomy;
|
||||
var $table = $('#taxonomy');
|
||||
var rows = $table.find('tr').length;
|
||||
|
||||
// When a row is swapped, keep previous and next page classes set.
|
||||
tableDrag.row.prototype.onSwap = function (swappedRow) {
|
||||
$table.find('tr.taxonomy-term-preview').removeClass('taxonomy-term-preview');
|
||||
$table.find('tr.taxonomy-term-divider-top').removeClass('taxonomy-term-divider-top');
|
||||
$table.find('tr.taxonomy-term-divider-bottom').removeClass('taxonomy-term-divider-bottom');
|
||||
|
||||
var tableBody = $table[0].tBodies[0];
|
||||
if (backStep) {
|
||||
for (var n = 0; n < backStep; n++) {
|
||||
$(tableBody.rows[n]).addClass('taxonomy-term-preview');
|
||||
}
|
||||
$(tableBody.rows[backStep - 1]).addClass('taxonomy-term-divider-top');
|
||||
$(tableBody.rows[backStep]).addClass('taxonomy-term-divider-bottom');
|
||||
}
|
||||
|
||||
if (forwardStep) {
|
||||
for (var k = rows - forwardStep - 1; k < rows - 1; k++) {
|
||||
$(tableBody.rows[k]).addClass('taxonomy-term-preview');
|
||||
}
|
||||
$(tableBody.rows[rows - forwardStep - 2]).addClass('taxonomy-term-divider-top');
|
||||
$(tableBody.rows[rows - forwardStep - 1]).addClass('taxonomy-term-divider-bottom');
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
12
core/modules/taxonomy/taxonomy.libraries.yml
Normal file
12
core/modules/taxonomy/taxonomy.libraries.yml
Normal file
|
@ -0,0 +1,12 @@
|
|||
drupal.taxonomy:
|
||||
version: VERSION
|
||||
js:
|
||||
taxonomy.js: {}
|
||||
css:
|
||||
component:
|
||||
css/taxonomy.theme.css: {}
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/drupal
|
||||
- core/drupalSettings
|
||||
- core/drupal.tabledrag
|
11
core/modules/taxonomy/taxonomy.links.action.yml
Normal file
11
core/modules/taxonomy/taxonomy.links.action.yml
Normal file
|
@ -0,0 +1,11 @@
|
|||
entity.taxonomy_vocabulary.add_form:
|
||||
route_name: entity.taxonomy_vocabulary.add_form
|
||||
title: 'Add vocabulary'
|
||||
appears_on:
|
||||
- entity.taxonomy_vocabulary.collection
|
||||
|
||||
entity.taxonomy_term.add_form:
|
||||
route_name: entity.taxonomy_term.add_form
|
||||
title: 'Add term'
|
||||
appears_on:
|
||||
- entity.taxonomy_vocabulary.overview_form
|
17
core/modules/taxonomy/taxonomy.links.contextual.yml
Normal file
17
core/modules/taxonomy/taxonomy.links.contextual.yml
Normal file
|
@ -0,0 +1,17 @@
|
|||
entity.taxonomy_term.edit_form:
|
||||
title: Edit
|
||||
group: taxonomy_term
|
||||
route_name: entity.taxonomy_term.edit_form
|
||||
weight: 10
|
||||
|
||||
entity.taxonomy_term.delete_form:
|
||||
title: Delete
|
||||
group: taxonomy_term
|
||||
route_name: entity.taxonomy_term.delete_form
|
||||
weight: 20
|
||||
|
||||
entity.taxonomy_vocabulary.delete_form:
|
||||
title: Delete
|
||||
group: taxonomy_vocabulary
|
||||
route_name: entity.taxonomy_vocabulary.delete_form
|
||||
weight: 20
|
5
core/modules/taxonomy/taxonomy.links.menu.yml
Normal file
5
core/modules/taxonomy/taxonomy.links.menu.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
entity.taxonomy_vocabulary.collection:
|
||||
title: Taxonomy
|
||||
parent: system.admin_structure
|
||||
description: 'Manage tagging, categorization, and classification of your content.'
|
||||
route_name: entity.taxonomy_vocabulary.collection
|
19
core/modules/taxonomy/taxonomy.links.task.yml
Normal file
19
core/modules/taxonomy/taxonomy.links.task.yml
Normal file
|
@ -0,0 +1,19 @@
|
|||
entity.taxonomy_term.canonical:
|
||||
title: 'View'
|
||||
route_name: entity.taxonomy_term.canonical
|
||||
base_route: entity.taxonomy_term.canonical
|
||||
|
||||
entity.taxonomy_term.edit_form:
|
||||
title: 'Edit'
|
||||
route_name: entity.taxonomy_term.edit_form
|
||||
base_route: entity.taxonomy_term.canonical
|
||||
|
||||
entity.taxonomy_vocabulary.overview_form:
|
||||
title: 'List'
|
||||
route_name: entity.taxonomy_vocabulary.overview_form
|
||||
base_route: entity.taxonomy_vocabulary.overview_form
|
||||
|
||||
entity.taxonomy_vocabulary.edit_form:
|
||||
title: 'Edit'
|
||||
route_name: entity.taxonomy_vocabulary.edit_form
|
||||
base_route: entity.taxonomy_vocabulary.overview_form
|
569
core/modules/taxonomy/taxonomy.module
Normal file
569
core/modules/taxonomy/taxonomy.module
Normal file
|
@ -0,0 +1,569 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Enables the organization of content into categories.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\Tags;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Drupal\taxonomy\TermInterface;
|
||||
use Drupal\taxonomy\VocabularyInterface;
|
||||
|
||||
/**
|
||||
* Denotes that no term in the vocabulary has a parent.
|
||||
*/
|
||||
const TAXONOMY_HIERARCHY_DISABLED = 0;
|
||||
|
||||
/**
|
||||
* Denotes that one or more terms in the vocabulary has a single parent.
|
||||
*/
|
||||
const TAXONOMY_HIERARCHY_SINGLE = 1;
|
||||
|
||||
/**
|
||||
* Denotes that one or more terms in the vocabulary have multiple parents.
|
||||
*/
|
||||
const TAXONOMY_HIERARCHY_MULTIPLE = 2;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function taxonomy_help($route_name, RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'help.page.taxonomy':
|
||||
$field_ui_url = \Drupal::moduleHandler()->moduleExists('field_ui') ? \Drupal::url('help.page', array('name' => 'field_ui')) : '#';
|
||||
$output = '';
|
||||
$output .= '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Taxonomy module allows users who have permission to create and edit content to categorize (tag) content of that type. Users who have the <em>Administer vocabularies and terms</em> <a href="!permissions" title="Taxonomy module permissions">permission</a> can add <em>vocabularies</em> that contain a set of related <em>terms</em>. The terms in a vocabulary can either be pre-set by an administrator or built gradually as content is added and edited. Terms may be organized hierarchically if desired.', array('!permissions' => \Drupal::url('user.admin_permissions', array(), array('fragment' => 'module-taxonomy')))) . '</p>';
|
||||
$output .= '<p>' . t('For more information, see the <a href="!taxonomy">online documentation for the Taxonomy module</a>.', array('!taxonomy' => 'https://www.drupal.org/documentation/modules/taxonomy/')) . '</p>';
|
||||
$output .= '<h3>' . t('Uses') . '</h3>';
|
||||
$output .= '<dl>';
|
||||
$output .= '<dt>' . t('Managing vocabularies') . '</dt>';
|
||||
$output .= '<dd>' . t('Users who have the <em>Administer vocabularies and terms</em> permission can add and edit vocabularies from the <a href="!taxonomy_admin">Taxonomy administration page</a>. Vocabularies can be deleted from their <em>Edit vocabulary</em> page. Users with the <em>Taxonomy term: Administer fields</em> permission may add additional fields for terms in that vocabulary using the <a href="!field_ui">Field UI module</a>.' , array('!taxonomy_admin' => \Drupal::url('entity.taxonomy_vocabulary.collection'), '!field_ui' => $field_ui_url)) . '</dd>';
|
||||
$output .= '<dt>' . t('Managing terms') . '</dt>';
|
||||
$output .= '<dd>' . t('Users who have the <em>Administer vocabularies and terms</em> permission or the <em>Edit terms</em> permission for a particular vocabulary can add, edit, and organize the terms in a vocabulary from a vocabulary\'s term listing page, which can be accessed by going to the <a href="!taxonomy_admin">Taxonomy administration page</a> and clicking <em>List terms</em> in the <em>Operations</em> column. Users must have the <em>Administer vocabularies and terms</em> permission or the <em>Delete terms</em> permission for a particular vocabulary to delete terms.' , array('!taxonomy_admin' => \Drupal::url('entity.taxonomy_vocabulary.collection'))) . ' </dd>';
|
||||
$output .= '<dt>' . t('Classifying entity content') . '</dt>';
|
||||
$output .= '<dd>' . t('A user with the <em>Administer fields</em> permission for a certain entity type may add <em>Taxonomy term</em> reference fields to the entity type, which will allow entities to be classified using taxonomy terms. See the <a href="!entity_reference">Entity Reference help</a> for more information about reference fields. See the <a href="!field">Field module help</a> and the <a href="!field_ui">Field UI help</a> pages for general information on fields and how to create and manage them.' , array('!field_ui' => $field_ui_url, '!field' => \Drupal::url('help.page', array('name' => 'field')), '!entity_reference' => \Drupal::url('help.page', array('name' => 'entity_reference')))) . '</dd>';
|
||||
$output .= '<dt>' . t('Adding new terms during content creation') . '</dt>';
|
||||
$output .= '<dd>' . t('Allowing users to add new terms gradually builds a vocabulary as content is added and edited. Users can add new terms if either of the two <em>Autocomplete</em> widgets is chosen for the Taxonomy term reference field in the <em>Manage form display</em> page for the field. You will also need to enable the <em>Create referenced entities if they don\'t already exist</em> option, and restrict the field to one vocabulary.') . '</dd>';
|
||||
$output .= '<dt>' . t('Configuring displays and form displays') . '</dt>';
|
||||
$output .= '<dd>' . t('See the <a href="!entity_reference">Entity Reference help</a> page for the field widgets and formatters that can be configured for any reference field on the <em>Manage display</em> and <em>Manage form display</em> pages. Taxonomy additionally provides an <em>RSS category</em> formatter that displays nothing when the entity item is displayed as HTML, but displays an RSS category instead of a list when the entity item is displayed in an RSS feed.', array('!entity_reference' => \Drupal::url('help.page', array('name' => 'entity_reference')))) . '</li>';
|
||||
$output .= '</ul>';
|
||||
$output .= '</dd>';
|
||||
$output .= '</dl>';
|
||||
return $output;
|
||||
|
||||
case 'entity.taxonomy_vocabulary.collection':
|
||||
$output = '<p>' . t('Taxonomy is for categorizing content. Terms are grouped into vocabularies. For example, a vocabulary called "Fruit" would contain the terms "Apple" and "Banana".') . '</p>';
|
||||
return $output;
|
||||
|
||||
case 'entity.taxonomy_vocabulary.overview_form':
|
||||
$vocabulary = $route_match->getParameter('taxonomy_vocabulary');
|
||||
switch ($vocabulary->getHierarchy()) {
|
||||
case TAXONOMY_HIERARCHY_DISABLED:
|
||||
return '<p>' . t('You can reorganize the terms in %capital_name using their drag-and-drop handles, and group terms under a parent term by sliding them under and to the right of the parent.', array('%capital_name' => Unicode::ucfirst($vocabulary->label()), '%name' => $vocabulary->label())) . '</p>';
|
||||
case TAXONOMY_HIERARCHY_SINGLE:
|
||||
return '<p>' . t('%capital_name contains terms grouped under parent terms. You can reorganize the terms in %capital_name using their drag-and-drop handles.', array('%capital_name' => Unicode::ucfirst($vocabulary->label()), '%name' => $vocabulary->label())) . '</p>';
|
||||
case TAXONOMY_HIERARCHY_MULTIPLE:
|
||||
return '<p>' . t('%capital_name contains terms with multiple parents. Drag and drop of terms with multiple parents is not supported, but you can re-enable drag-and-drop support by editing each term to include only a single parent.', array('%capital_name' => Unicode::ucfirst($vocabulary->label()))) . '</p>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity URI callback.
|
||||
*/
|
||||
function taxonomy_term_uri($term) {
|
||||
return new Url('entity.taxonomy_term.canonical', array(
|
||||
'taxonomy_term' => $term->id(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_page_attachments_alter().
|
||||
*/
|
||||
function taxonomy_page_attachments_alter(array &$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']['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']['html_head_link'][] = array(
|
||||
array(
|
||||
'rel' => 'shortlink',
|
||||
'href' => $term->url($rel, array('alias' => TRUE)),
|
||||
),
|
||||
TRUE,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function taxonomy_theme() {
|
||||
return array(
|
||||
'taxonomy_term' => array(
|
||||
'render element' => 'elements',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and updates the hierarchy flag of a vocabulary.
|
||||
*
|
||||
* Checks the current parents of all terms in a vocabulary and updates the
|
||||
* vocabulary's hierarchy setting to the lowest possible level. If no term
|
||||
* has parent terms then the vocabulary will be given a hierarchy of
|
||||
* TAXONOMY_HIERARCHY_DISABLED. If any term has a single parent then the
|
||||
* vocabulary will be given a hierarchy of TAXONOMY_HIERARCHY_SINGLE. If any
|
||||
* term has multiple parents then the vocabulary will be given a hierarchy of
|
||||
* TAXONOMY_HIERARCHY_MULTIPLE.
|
||||
*
|
||||
* @param \Drupal\taxonomy\VocabularyInterface $vocabulary
|
||||
* A taxonomy vocabulary entity.
|
||||
* @param $changed_term
|
||||
* An array of the term structure that was updated.
|
||||
*
|
||||
* @return
|
||||
* An integer that represents the level of the vocabulary's hierarchy.
|
||||
*/
|
||||
function taxonomy_check_vocabulary_hierarchy(VocabularyInterface $vocabulary, $changed_term) {
|
||||
$tree = \Drupal::entityManager()->getStorage('taxonomy_term')->loadTree($vocabulary->id());
|
||||
$hierarchy = TAXONOMY_HIERARCHY_DISABLED;
|
||||
foreach ($tree as $term) {
|
||||
// Update the changed term with the new parent value before comparison.
|
||||
if ($term->tid == $changed_term['tid']) {
|
||||
$term = (object) $changed_term;
|
||||
$term->parents = $term->parent;
|
||||
}
|
||||
// Check this term's parent count.
|
||||
if (count($term->parents) > 1) {
|
||||
$hierarchy = TAXONOMY_HIERARCHY_MULTIPLE;
|
||||
break;
|
||||
}
|
||||
elseif (count($term->parents) == 1 && !isset($term->parents[0])) {
|
||||
$hierarchy = TAXONOMY_HIERARCHY_SINGLE;
|
||||
}
|
||||
}
|
||||
if ($hierarchy != $vocabulary->getHierarchy()) {
|
||||
$vocabulary->setHierarchy($hierarchy);
|
||||
$vocabulary->save();
|
||||
}
|
||||
|
||||
return $hierarchy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an array which displays a term detail page.
|
||||
*
|
||||
* @param \Drupal\taxonomy\Entity\Term $term
|
||||
* A taxonomy term object.
|
||||
* @param string $view_mode
|
||||
* View mode; e.g., 'full', 'teaser', etc.
|
||||
* @param string $langcode
|
||||
* (optional) A language code to use for rendering. Defaults to the global
|
||||
* content language of the current request.
|
||||
*
|
||||
* @return array
|
||||
* A $page element suitable for use by drupal_render().
|
||||
*/
|
||||
function taxonomy_term_view(Term $term, $view_mode = 'full', $langcode = NULL) {
|
||||
return entity_view($term, $view_mode, $langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a drupal_render() style array from an array of loaded terms.
|
||||
*
|
||||
* @param array $terms
|
||||
* An array of taxonomy terms as returned by Term::loadMultiple().
|
||||
* @param string $view_mode
|
||||
* View mode; e.g., 'full', 'teaser', etc.
|
||||
* @param string $langcode
|
||||
* (optional) A language code to use for rendering. Defaults to the global
|
||||
* content language of the current request.
|
||||
*
|
||||
* @return array
|
||||
* An array in the format expected by drupal_render().
|
||||
*/
|
||||
function taxonomy_term_view_multiple(array $terms, $view_mode = 'full', $langcode = NULL) {
|
||||
return entity_view_multiple($terms, $view_mode, $langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme_suggestions_HOOK().
|
||||
*/
|
||||
function taxonomy_theme_suggestions_taxonomy_term(array $variables) {
|
||||
$suggestions = array();
|
||||
|
||||
/** @var \Drupal\taxonomy\TermInterface $term */
|
||||
$term = $variables['elements']['#taxonomy_term'];
|
||||
|
||||
$suggestions[] = 'taxonomy_term__' . $term->bundle();
|
||||
$suggestions[] = 'taxonomy_term__' . $term->id();
|
||||
|
||||
return $suggestions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares variables for taxonomy term templates.
|
||||
*
|
||||
* Default template: taxonomy-term.html.twig.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - elements: An associative array containing the taxonomy term and any
|
||||
* fields attached to the term. Properties used:
|
||||
* - #taxonomy_term: A \Drupal\taxonomy\TermInterface object.
|
||||
* - #view_mode: The current view mode for this taxonomy term, e.g.
|
||||
* 'full' or 'teaser'.
|
||||
* - attributes: HTML attributes for the containing element.
|
||||
*/
|
||||
function template_preprocess_taxonomy_term(&$variables) {
|
||||
$variables['view_mode'] = $variables['elements']['#view_mode'];
|
||||
$variables['term'] = $variables['elements']['#taxonomy_term'];
|
||||
/** @var \Drupal\taxonomy\TermInterface $term */
|
||||
$term = $variables['term'];
|
||||
|
||||
$variables['url'] = $term->url();
|
||||
// We use name here because that is what appears in the UI.
|
||||
$variables['name'] = $variables['elements']['name'];
|
||||
unset($variables['elements']['name']);
|
||||
$variables['page'] = $variables['view_mode'] == 'full' && taxonomy_term_is_page($term);
|
||||
|
||||
// Helpful $content variable for templates.
|
||||
$variables['content'] = array();
|
||||
foreach (Element::children($variables['elements']) as $key) {
|
||||
$variables['content'][$key] = $variables['elements'][$key];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the current page is the page of the passed-in term.
|
||||
*
|
||||
* @param \Drupal\taxonomy\Entity\Term $term
|
||||
* A taxonomy term entity.
|
||||
*/
|
||||
function taxonomy_term_is_page(Term $term) {
|
||||
if (\Drupal::routeMatch()->getRouteName() == 'entity.taxonomy_term.canonical' && $page_term_id = \Drupal::routeMatch()->getRawParameter('taxonomy_term')) {
|
||||
return $page_term_id == $term->id();
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all static cache variables for terms.
|
||||
*/
|
||||
function taxonomy_terms_static_reset() {
|
||||
\Drupal::entityManager()->getStorage('taxonomy_term')->resetCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all static cache variables for vocabularies.
|
||||
*
|
||||
* @param $ids
|
||||
* An array of ids to reset in the entity cache.
|
||||
*/
|
||||
function taxonomy_vocabulary_static_reset(array $ids = NULL) {
|
||||
\Drupal::entityManager()->getStorage('taxonomy_vocabulary')->resetCache($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get names for all taxonomy vocabularies.
|
||||
*
|
||||
* @return array
|
||||
* A list of existing vocabulary IDs.
|
||||
*/
|
||||
function taxonomy_vocabulary_get_names() {
|
||||
$names = &drupal_static(__FUNCTION__);
|
||||
|
||||
if (!isset($names)) {
|
||||
$names = array();
|
||||
$config_names = \Drupal::configFactory()->listAll('taxonomy.vocabulary.');
|
||||
foreach ($config_names as $config_name) {
|
||||
$id = substr($config_name, strlen('taxonomy.vocabulary.'));
|
||||
$names[$id] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to map a string to an existing term, as for glossary use.
|
||||
*
|
||||
* Provides a case-insensitive and trimmed mapping, to maximize the
|
||||
* likelihood of a successful match.
|
||||
*
|
||||
* @param $name
|
||||
* Name of the term to search for.
|
||||
* @param $vocabulary
|
||||
* (optional) Vocabulary machine name to limit the search. Defaults to NULL.
|
||||
*
|
||||
* @return
|
||||
* An array of matching term objects.
|
||||
*/
|
||||
function taxonomy_term_load_multiple_by_name($name, $vocabulary = NULL) {
|
||||
$values = array('name' => trim($name));
|
||||
if (isset($vocabulary)) {
|
||||
$vocabularies = taxonomy_vocabulary_get_names();
|
||||
if (isset($vocabularies[$vocabulary])){
|
||||
$values['vid'] = $vocabulary;
|
||||
}
|
||||
else {
|
||||
// Return an empty array when filtering by a non-existing vocabulary.
|
||||
return array();
|
||||
}
|
||||
}
|
||||
return entity_load_multiple_by_properties('taxonomy_term', $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load multiple taxonomy terms based on certain conditions.
|
||||
*
|
||||
* This function should be used whenever you need to load more than one term
|
||||
* from the database. Terms are loaded into memory and will not require
|
||||
* database access if loaded again during the same page request.
|
||||
*
|
||||
* @see entity_load_multiple()
|
||||
* @see \Drupal\Core\Entity\Query\EntityQueryInterface
|
||||
*
|
||||
* @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
|
||||
* Use \Drupal\taxonomy\Entity\Term::loadMultiple().
|
||||
*
|
||||
* @param array $tids
|
||||
* (optional) An array of entity IDs. If omitted, all entities are loaded.
|
||||
*
|
||||
* @return array
|
||||
* An array of taxonomy term entities, indexed by tid. When no results are
|
||||
* found, an empty array is returned.
|
||||
*/
|
||||
function taxonomy_term_load_multiple(array $tids = NULL) {
|
||||
return Term::loadMultiple($tids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads multiple taxonomy vocabularies based on certain conditions.
|
||||
*
|
||||
* This function should be used whenever you need to load more than one
|
||||
* vocabulary from the database. Terms are loaded into memory and will not
|
||||
* require database access if loaded again during the same page request.
|
||||
*
|
||||
* @see entity_load_multiple()
|
||||
*
|
||||
* @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
|
||||
* Use \Drupal\taxonomy\Entity\Vocabulary::loadMultiple().
|
||||
*
|
||||
* @param array $vids
|
||||
* (optional) An array of entity IDs. If omitted, all entities are loaded.
|
||||
*
|
||||
* @return array
|
||||
* An array of vocabulary objects, indexed by vid.
|
||||
*/
|
||||
function taxonomy_vocabulary_load_multiple(array $vids = NULL) {
|
||||
return Vocabulary::loadMultiple($vids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the taxonomy vocabulary entity matching a vocabulary ID.
|
||||
*
|
||||
* @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
|
||||
* Use \Drupal\taxonomy\Entity\Vocabulary::load().
|
||||
*
|
||||
* @param int $vid
|
||||
* The vocabulary's ID.
|
||||
*
|
||||
* @return \Drupal\taxonomy\Entity\Vocabulary|null
|
||||
* The taxonomy vocabulary entity, if exists, NULL otherwise. Results are
|
||||
* statically cached.
|
||||
*/
|
||||
function taxonomy_vocabulary_load($vid) {
|
||||
return Vocabulary::load($vid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the taxonomy term entity matching a term ID.
|
||||
*
|
||||
* @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
|
||||
* Use \Drupal\taxonomy\Entity\Term::load().
|
||||
*
|
||||
* @param $tid
|
||||
* A term's ID
|
||||
*
|
||||
* @return \Drupal\taxonomy\Entity\Term|null
|
||||
* A taxonomy term entity, or NULL if the term was not found. Results are
|
||||
* statically cached.
|
||||
*/
|
||||
function taxonomy_term_load($tid) {
|
||||
if (!is_numeric($tid)) {
|
||||
return NULL;
|
||||
}
|
||||
return Term::load($tid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implodes a list of tags of a certain vocabulary into a string.
|
||||
*
|
||||
* @see \Drupal\Component\Utility\Tags::explode()
|
||||
*/
|
||||
function taxonomy_implode_tags($tags, $vid = NULL) {
|
||||
$typed_tags = array();
|
||||
foreach ($tags as $tag) {
|
||||
// Extract terms belonging to the vocabulary in question.
|
||||
if (!isset($vid) || $tag->bundle() == $vid) {
|
||||
// Make sure we have a completed loaded taxonomy term.
|
||||
if ($tag instanceof EntityInterface && $label = $tag->label()) {
|
||||
// Commas and quotes in tag names are special cases, so encode 'em.
|
||||
$typed_tags[] = Tags::encode($label);
|
||||
}
|
||||
}
|
||||
}
|
||||
return implode(', ', $typed_tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Title callback for term pages.
|
||||
*
|
||||
* @param \Drupal\taxonomy\Entity\Term $term
|
||||
* A taxonomy term entity.
|
||||
*
|
||||
* @return
|
||||
* The term name to be used as the page title.
|
||||
*/
|
||||
function taxonomy_term_title(Term $term) {
|
||||
return $term->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @defgroup taxonomy_index Taxonomy indexing
|
||||
* @{
|
||||
* Functions to maintain taxonomy indexing.
|
||||
*
|
||||
* Taxonomy uses default field storage to store canonical relationships
|
||||
* between terms and fieldable entities. However its most common use case
|
||||
* requires listing all content associated with a term or group of terms
|
||||
* sorted by creation date. To avoid slow queries due to joining across
|
||||
* multiple node and field tables with various conditions and order by criteria,
|
||||
* we maintain a denormalized table with all relationships between terms,
|
||||
* published nodes and common sort criteria such as status, sticky and created.
|
||||
* When using other field storage engines or alternative methods of
|
||||
* denormalizing this data you should set the
|
||||
* taxonomy.settings:maintain_index_table to '0' to avoid unnecessary writes in
|
||||
* SQL.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_insert() for node entities.
|
||||
*/
|
||||
function taxonomy_node_insert(EntityInterface $node) {
|
||||
// Add taxonomy index entries for the node.
|
||||
taxonomy_build_node_index($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and inserts taxonomy index entries for a given node.
|
||||
*
|
||||
* The index lists all terms that are related to a given node entity, and is
|
||||
* therefore maintained at the entity level.
|
||||
*
|
||||
* @param \Drupal\node\Entity\Node $node
|
||||
* The node entity.
|
||||
*/
|
||||
function taxonomy_build_node_index($node) {
|
||||
// We maintain a denormalized table of term/node relationships, containing
|
||||
// only data for current, published nodes.
|
||||
if (!\Drupal::config('taxonomy.settings')->get('maintain_index_table') || !(\Drupal::entityManager()->getStorage('node') instanceof SqlContentEntityStorage)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$status = $node->isPublished();
|
||||
$sticky = (int) $node->isSticky();
|
||||
// We only maintain the taxonomy index for published nodes.
|
||||
if ($status && $node->isDefaultRevision()) {
|
||||
// Collect a unique list of all the term IDs from all node fields.
|
||||
$tid_all = array();
|
||||
foreach ($node->getFieldDefinitions() as $field) {
|
||||
$field_name = $field->getName();
|
||||
if (is_subclass_of($field->getItemDefinition()->getClass(), '\Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem') && $field->getSetting('target_type') == 'taxonomy_term') {
|
||||
foreach ($node->getTranslationLanguages() as $language) {
|
||||
foreach ($node->getTranslation($language->getId())->$field_name as $item) {
|
||||
if (!$item->isEmpty()) {
|
||||
$tid_all[$item->target_id] = $item->target_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Insert index entries for all the node's terms.
|
||||
if (!empty($tid_all)) {
|
||||
foreach ($tid_all as $tid) {
|
||||
db_merge('taxonomy_index')
|
||||
->key(array('nid' => $node->id(), 'tid' => $tid, 'status' => $node->isPublished()))
|
||||
->fields(array('sticky' => $sticky, 'created' => $node->getCreatedTime()))
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_update() for node entities.
|
||||
*/
|
||||
function taxonomy_node_update(EntityInterface $node) {
|
||||
// Always rebuild the node's taxonomy index entries on node save.
|
||||
taxonomy_delete_node_index($node);
|
||||
taxonomy_build_node_index($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_predelete() for node entities.
|
||||
*/
|
||||
function taxonomy_node_predelete(EntityInterface $node) {
|
||||
// Clean up the {taxonomy_index} table when nodes are deleted.
|
||||
taxonomy_delete_node_index($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes taxonomy index entries for a given node.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $node
|
||||
* The node entity.
|
||||
*/
|
||||
function taxonomy_delete_node_index(EntityInterface $node) {
|
||||
if (\Drupal::config('taxonomy.settings')->get('maintain_index_table')) {
|
||||
db_delete('taxonomy_index')->condition('nid', $node->id())->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_delete() for taxonomy_term entities.
|
||||
*/
|
||||
function taxonomy_taxonomy_term_delete(Term $term) {
|
||||
if (\Drupal::config('taxonomy.settings')->get('maintain_index_table')) {
|
||||
// Clean up the {taxonomy_index} table when terms are deleted.
|
||||
db_delete('taxonomy_index')->condition('tid', $term->id())->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "defgroup taxonomy_index".
|
||||
*/
|
5
core/modules/taxonomy/taxonomy.permissions.yml
Normal file
5
core/modules/taxonomy/taxonomy.permissions.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
administer taxonomy:
|
||||
title: 'Administer vocabularies and terms'
|
||||
|
||||
permission_callbacks:
|
||||
- Drupal\taxonomy\TaxonomyPermissions::permissions
|
84
core/modules/taxonomy/taxonomy.routing.yml
Normal file
84
core/modules/taxonomy/taxonomy.routing.yml
Normal file
|
@ -0,0 +1,84 @@
|
|||
entity.taxonomy_vocabulary.collection:
|
||||
path: '/admin/structure/taxonomy'
|
||||
defaults:
|
||||
_entity_list: 'taxonomy_vocabulary'
|
||||
_title: 'Taxonomy'
|
||||
requirements:
|
||||
_permission: 'administer taxonomy'
|
||||
|
||||
entity.taxonomy_term.add_form:
|
||||
path: '/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/add'
|
||||
defaults:
|
||||
_controller: '\Drupal\taxonomy\Controller\TaxonomyController::addForm'
|
||||
_title: 'Add term'
|
||||
requirements:
|
||||
_entity_create_access: 'taxonomy_term:{taxonomy_vocabulary}'
|
||||
|
||||
entity.taxonomy_term.edit_form:
|
||||
path: '/taxonomy/term/{taxonomy_term}/edit'
|
||||
defaults:
|
||||
_entity_form: 'taxonomy_term.default'
|
||||
_title: 'Edit term'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
_entity_access: 'taxonomy_term.update'
|
||||
|
||||
entity.taxonomy_term.delete_form:
|
||||
path: '/taxonomy/term/{taxonomy_term}/delete'
|
||||
defaults:
|
||||
_entity_form: 'taxonomy_term.delete'
|
||||
_title: 'Delete term'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
_entity_access: 'taxonomy_term.delete'
|
||||
|
||||
entity.taxonomy_vocabulary.add_form:
|
||||
path: '/admin/structure/taxonomy/add'
|
||||
defaults:
|
||||
_entity_form: 'taxonomy_vocabulary'
|
||||
_title: 'Add vocabulary'
|
||||
requirements:
|
||||
_entity_create_access: 'taxonomy_vocabulary'
|
||||
|
||||
entity.taxonomy_vocabulary.edit_form:
|
||||
path: '/admin/structure/taxonomy/manage/{taxonomy_vocabulary}'
|
||||
defaults:
|
||||
_entity_form: 'taxonomy_vocabulary.default'
|
||||
_title_callback: '\Drupal\taxonomy\Controller\TaxonomyController::vocabularyTitle'
|
||||
requirements:
|
||||
_entity_access: 'taxonomy_vocabulary.update'
|
||||
|
||||
entity.taxonomy_vocabulary.delete_form:
|
||||
path: '/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/delete'
|
||||
defaults:
|
||||
_entity_form: 'taxonomy_vocabulary.delete'
|
||||
_title: 'Delete vocabulary'
|
||||
requirements:
|
||||
_entity_access: 'taxonomy_vocabulary.delete'
|
||||
|
||||
entity.taxonomy_vocabulary.reset_form:
|
||||
path: '/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/reset'
|
||||
defaults:
|
||||
_entity_form: 'taxonomy_vocabulary.reset'
|
||||
_title: 'Reset'
|
||||
requirements:
|
||||
_permission: 'administer taxonomy'
|
||||
|
||||
entity.taxonomy_vocabulary.overview_form:
|
||||
path: '/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/overview'
|
||||
defaults:
|
||||
_form: 'Drupal\taxonomy\Form\OverviewTerms'
|
||||
_title_callback: 'Drupal\taxonomy\Controller\TaxonomyController::vocabularyTitle'
|
||||
requirements:
|
||||
_entity_access: 'taxonomy_vocabulary.view'
|
||||
|
||||
entity.taxonomy_term.canonical:
|
||||
path: '/taxonomy/term/{taxonomy_term}'
|
||||
defaults:
|
||||
_entity_view: 'taxonomy_term.full'
|
||||
_title: 'Taxonomy term'
|
||||
_title_callback: '\Drupal\taxonomy\Controller\TaxonomyController::termTitle'
|
||||
requirements:
|
||||
_entity_access: 'taxonomy_term.view'
|
6
core/modules/taxonomy/taxonomy.services.yml
Normal file
6
core/modules/taxonomy/taxonomy.services.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
services:
|
||||
taxonomy_term.breadcrumb:
|
||||
class: Drupal\taxonomy\TermBreadcrumbBuilder
|
||||
arguments: ['@entity.manager']
|
||||
tags:
|
||||
- { name: breadcrumb_builder, priority: 1002 }
|
188
core/modules/taxonomy/taxonomy.tokens.inc
Normal file
188
core/modules/taxonomy/taxonomy.tokens.inc
Normal file
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Builds placeholder replacement tokens for taxonomy terms and vocabularies.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
|
||||
/**
|
||||
* Implements hook_token_info().
|
||||
*/
|
||||
function taxonomy_token_info() {
|
||||
$types['term'] = array(
|
||||
'name' => t("Taxonomy terms"),
|
||||
'description' => t("Tokens related to taxonomy terms."),
|
||||
'needs-data' => 'term',
|
||||
);
|
||||
$types['vocabulary'] = array(
|
||||
'name' => t("Vocabularies"),
|
||||
'description' => t("Tokens related to taxonomy vocabularies."),
|
||||
'needs-data' => 'vocabulary',
|
||||
);
|
||||
|
||||
// Taxonomy term related variables.
|
||||
$term['tid'] = array(
|
||||
'name' => t("Term ID"),
|
||||
'description' => t("The unique ID of the taxonomy term."),
|
||||
);
|
||||
$term['name'] = array(
|
||||
'name' => t("Name"),
|
||||
'description' => t("The name of the taxonomy term."),
|
||||
);
|
||||
$term['description'] = array(
|
||||
'name' => t("Description"),
|
||||
'description' => t("The optional description of the taxonomy term."),
|
||||
);
|
||||
$term['node-count'] = array(
|
||||
'name' => t("Node count"),
|
||||
'description' => t("The number of nodes tagged with the taxonomy term."),
|
||||
);
|
||||
$term['url'] = array(
|
||||
'name' => t("URL"),
|
||||
'description' => t("The URL of the taxonomy term."),
|
||||
);
|
||||
|
||||
// Taxonomy vocabulary related variables.
|
||||
$vocabulary['vid'] = array(
|
||||
'name' => t("Vocabulary ID"),
|
||||
'description' => t("The unique ID of the taxonomy vocabulary."),
|
||||
);
|
||||
$vocabulary['name'] = array(
|
||||
'name' => t("Name"),
|
||||
'description' => t("The name of the taxonomy vocabulary."),
|
||||
);
|
||||
$vocabulary['description'] = array(
|
||||
'name' => t("Description"),
|
||||
'description' => t("The optional description of the taxonomy vocabulary."),
|
||||
);
|
||||
$vocabulary['node-count'] = array(
|
||||
'name' => t("Node count"),
|
||||
'description' => t("The number of nodes tagged with terms belonging to the taxonomy vocabulary."),
|
||||
);
|
||||
$vocabulary['term-count'] = array(
|
||||
'name' => t("Term count"),
|
||||
'description' => t("The number of terms belonging to the taxonomy vocabulary."),
|
||||
);
|
||||
|
||||
// Chained tokens for taxonomies
|
||||
$term['vocabulary'] = array(
|
||||
'name' => t("Vocabulary"),
|
||||
'description' => t("The vocabulary the taxonomy term belongs to."),
|
||||
'type' => 'vocabulary',
|
||||
);
|
||||
$term['parent'] = array(
|
||||
'name' => t("Parent term"),
|
||||
'description' => t("The parent term of the taxonomy term, if one exists."),
|
||||
'type' => 'term',
|
||||
);
|
||||
|
||||
return array(
|
||||
'types' => $types,
|
||||
'tokens' => array(
|
||||
'term' => $term,
|
||||
'vocabulary' => $vocabulary,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tokens().
|
||||
*/
|
||||
function taxonomy_tokens($type, $tokens, array $data = array(), array $options = array()) {
|
||||
$token_service = \Drupal::token();
|
||||
|
||||
$replacements = array();
|
||||
$sanitize = !empty($options['sanitize']);
|
||||
$taxonomy_storage = \Drupal::entityManager()->getStorage('taxonomy_term');
|
||||
if ($type == 'term' && !empty($data['term'])) {
|
||||
$term = $data['term'];
|
||||
|
||||
foreach ($tokens as $name => $original) {
|
||||
switch ($name) {
|
||||
case 'tid':
|
||||
$replacements[$original] = $term->id();
|
||||
break;
|
||||
|
||||
case 'name':
|
||||
$replacements[$original] = $sanitize ? SafeMarkup::checkPlain($term->getName()) : $term->getName();
|
||||
break;
|
||||
|
||||
case 'description':
|
||||
$replacements[$original] = $sanitize ? $term->description->processed : $term->getDescription();
|
||||
break;
|
||||
|
||||
case 'url':
|
||||
$replacements[$original] = $term->url('canonical', array('absolute' => TRUE));
|
||||
break;
|
||||
|
||||
case 'node-count':
|
||||
$query = db_select('taxonomy_index');
|
||||
$query->condition('tid', $term->id());
|
||||
$query->addTag('term_node_count');
|
||||
$count = $query->countQuery()->execute()->fetchField();
|
||||
$replacements[$original] = $count;
|
||||
break;
|
||||
|
||||
case 'vocabulary':
|
||||
$vocabulary = Vocabulary::load($term->bundle());
|
||||
$replacements[$original] = SafeMarkup::checkPlain($vocabulary->label());
|
||||
break;
|
||||
|
||||
case 'parent':
|
||||
if ($parents = $taxonomy_storage->loadParents($term->id())) {
|
||||
$parent = array_pop($parents);
|
||||
$replacements[$original] = SafeMarkup::checkPlain($parent->getName());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($vocabulary_tokens = $token_service->findWithPrefix($tokens, 'vocabulary')) {
|
||||
$vocabulary = Vocabulary::load($term->bundle());
|
||||
$replacements += $token_service->generate('vocabulary', $vocabulary_tokens, array('vocabulary' => $vocabulary), $options);
|
||||
}
|
||||
|
||||
if (($vocabulary_tokens = $token_service->findWithPrefix($tokens, 'parent')) && $parents = $taxonomy_storage->loadParents($term->id())) {
|
||||
$parent = array_pop($parents);
|
||||
$replacements += $token_service->generate('term', $vocabulary_tokens, array('term' => $parent), $options);
|
||||
}
|
||||
}
|
||||
|
||||
elseif ($type == 'vocabulary' && !empty($data['vocabulary'])) {
|
||||
$vocabulary = $data['vocabulary'];
|
||||
|
||||
foreach ($tokens as $name => $original) {
|
||||
switch ($name) {
|
||||
case 'vid':
|
||||
$replacements[$original] = $vocabulary->id();
|
||||
break;
|
||||
|
||||
case 'name':
|
||||
$replacements[$original] = $sanitize ? SafeMarkup::checkPlain($vocabulary->label()) : $vocabulary->label();
|
||||
break;
|
||||
|
||||
case 'description':
|
||||
$replacements[$original] = $sanitize ? Xss::filter($vocabulary->getDescription()) : $vocabulary->getDescription();
|
||||
break;
|
||||
|
||||
case 'term-count':
|
||||
$replacements[$original] = \Drupal::entityQuery('taxonomy_term')
|
||||
->condition('vid', $vocabulary->id())
|
||||
->addTag('vocabulary_term_count')
|
||||
->count()
|
||||
->execute();
|
||||
break;
|
||||
|
||||
case 'node-count':
|
||||
$replacements[$original] = $taxonomy_storage->nodeCount($vocabulary->id());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $replacements;
|
||||
}
|
85
core/modules/taxonomy/taxonomy.views.inc
Normal file
85
core/modules/taxonomy/taxonomy.views.inc
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides views data for taxonomy.module.
|
||||
*/
|
||||
|
||||
use Drupal\field\FieldStorageConfigInterface;
|
||||
|
||||
/**
|
||||
* Implements hook_views_data_alter().
|
||||
*/
|
||||
function taxonomy_views_data_alter(&$data) {
|
||||
$data['node_field_data']['term_node_tid'] = array(
|
||||
'title' => t('Taxonomy terms on node'),
|
||||
'help' => t('Relate nodes to taxonomy terms, specifying which vocabulary or vocabularies to use. This relationship will cause duplicated records if there are multiple terms.'),
|
||||
'relationship' => array(
|
||||
'id' => 'node_term_data',
|
||||
'label' => t('term'),
|
||||
'base' => 'taxonomy_term_field_data',
|
||||
),
|
||||
'field' => array(
|
||||
'title' => t('All taxonomy terms'),
|
||||
'help' => t('Display all taxonomy terms associated with a node from specified vocabularies.'),
|
||||
'id' => 'taxonomy_index_tid',
|
||||
'no group by' => TRUE,
|
||||
'click sortable' => FALSE,
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_data']['term_node_tid_depth'] = array(
|
||||
'help' => t('Display content if it has the selected taxonomy terms, or children of the selected terms. Due to additional complexity, this has fewer options than the versions without depth.'),
|
||||
'real field' => 'nid',
|
||||
'argument' => array(
|
||||
'title' => t('Has taxonomy term ID (with depth)'),
|
||||
'id' => 'taxonomy_index_tid_depth',
|
||||
'accept depth modifier' => TRUE,
|
||||
),
|
||||
'filter' => array(
|
||||
'title' => t('Has taxonomy terms (with depth)'),
|
||||
'id' => 'taxonomy_index_tid_depth',
|
||||
),
|
||||
);
|
||||
|
||||
$data['node_field_data']['term_node_tid_depth_modifier'] = array(
|
||||
'title' => t('Has taxonomy term ID depth modifier'),
|
||||
'help' => t('Allows the "depth" for Taxonomy: Term ID (with depth) to be modified via an additional contextual filter value.'),
|
||||
'argument' => array(
|
||||
'id' => 'taxonomy_index_tid_depth_modifier',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_views_data_alter().
|
||||
*
|
||||
* Views integration for entity reference fields which reference taxonomy terms.
|
||||
* Adds a term relationship to the default field data.
|
||||
*
|
||||
* @see views_field_default_views_data()
|
||||
*/
|
||||
function taxonomy_field_views_data_alter(array &$data, FieldStorageConfigInterface $field_storage) {
|
||||
if ($field_storage->getType() == 'entity_reference' && $field_storage->getSetting('target_type') == 'taxonomy_term') {
|
||||
foreach ($data as $table_name => $table_data) {
|
||||
foreach ($table_data as $field_name => $field_data) {
|
||||
if (isset($field_data['filter']) && $field_name != 'delta') {
|
||||
$data[$table_name][$field_name]['filter']['id'] = 'taxonomy_index_tid';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_views_plugins_argument_validator_alter().
|
||||
*
|
||||
* Extend the generic entity argument validator.
|
||||
*
|
||||
* @see \Drupal\views\Plugin\views\argument_validator\Entity
|
||||
*/
|
||||
function taxonomy_views_plugins_argument_validator_alter(array &$plugins) {
|
||||
$plugins['entity:taxonomy_term']['title'] = t('Taxonomy term ID');
|
||||
$plugins['entity:taxonomy_term']['class'] = 'Drupal\taxonomy\Plugin\views\argument_validator\Term';
|
||||
$plugins['entity:taxonomy_term']['provider'] = 'taxonomy';
|
||||
}
|
35
core/modules/taxonomy/templates/taxonomy-term.html.twig
Normal file
35
core/modules/taxonomy/templates/taxonomy-term.html.twig
Normal file
|
@ -0,0 +1,35 @@
|
|||
{#
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation to display a taxonomy term.
|
||||
*
|
||||
* Available variables:
|
||||
* - url: URL of the current term.
|
||||
* - name: Name of the current term.
|
||||
* - content: Items for the content of the term (fields and description).
|
||||
* Use 'content' to print them all, or print a subset such as
|
||||
* 'content.description'. Use the following code to exclude the
|
||||
* printing of a given child element:
|
||||
* @code
|
||||
* {{ content|without('description') }}
|
||||
* @endcode
|
||||
* - attributes: HTML attributes for the wrapper.
|
||||
* - page: Flag for the full page state.
|
||||
* - term: The taxonomy term entity, including:
|
||||
* - id: The ID of the taxonomy term.
|
||||
* - bundle: Machine name of the current vocabulary.
|
||||
* - view_mode: View mode, e.g. 'full', 'teaser', etc.
|
||||
*
|
||||
* @see template_preprocess_taxonomy_term()
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
<div{{ attributes}}>
|
||||
{{ title_prefix }}
|
||||
{% if not page %}
|
||||
<h2><a href="{{ url }}">{{ name }}</a></h2>
|
||||
{% endif %}
|
||||
{{ title_suffix }}
|
||||
{{ content }}
|
||||
</div>
|
|
@ -0,0 +1,7 @@
|
|||
taxonomy.vocabulary.*.third_party.taxonomy_crud:
|
||||
type: mapping
|
||||
label: 'Schema for taxonomy_crud module additions to vocabulary entity'
|
||||
mapping:
|
||||
foo:
|
||||
type: string
|
||||
label: 'Label for foo'
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue