Issue #1574560 by andypost, Rajendar Reddy: Update jQuery Form Plugin to the latest...
[project/drupal.git] / core / modules / taxonomy / taxonomy.module
1 <?php
2
3 /**
4 * @file
5 * Enables the organization of content into categories.
6 */
7
8 use Drupal\Component\Utility\Tags;
9 use Drupal\Core\Entity\ContentEntityDatabaseStorage;
10 use Drupal\Core\Entity\EntityInterface;
11 use Drupal\Core\Render\Element;
12 use Drupal\Core\Url;
13 use Drupal\file\FileInterface;
14 use Drupal\node\Entity\Node;
15 use Drupal\taxonomy\Entity\Term;
16 use Drupal\taxonomy\Entity\Vocabulary;
17 use Drupal\taxonomy\VocabularyInterface;
18 use Drupal\Component\Utility\String;
19
20 /**
21 * Denotes that no term in the vocabulary has a parent.
22 */
23 const TAXONOMY_HIERARCHY_DISABLED = 0;
24
25 /**
26 * Denotes that one or more terms in the vocabulary has a single parent.
27 */
28 const TAXONOMY_HIERARCHY_SINGLE = 1;
29
30 /**
31 * Denotes that one or more terms in the vocabulary have multiple parents.
32 */
33 const TAXONOMY_HIERARCHY_MULTIPLE = 2;
34
35 /**
36 * Users can create new terms in a free-tagging vocabulary when
37 * submitting a taxonomy_autocomplete_widget. We store a term object
38 * whose tid is 'autocreate' as a field data item during widget
39 * validation and then actually create the term if/when that field
40 * data item makes it to taxonomy_field_insert/update().
41 */
42
43 /**
44 * Implements hook_help().
45 */
46 function taxonomy_help($path, $arg) {
47 switch ($path) {
48 case 'admin/help#taxonomy':
49 $output = '';
50 $output .= '<h3>' . t('About') . '</h3>';
51 $output .= '<p>' . t('The Taxonomy module allows you to classify the content of your website. To classify content, you define <em>vocabularies</em> that contain related <em>terms</em>, and then assign the vocabularies to content types. For more information, see the online handbook entry for the <a href="@taxonomy">Taxonomy module</a>.', array('@taxonomy' => 'http://drupal.org/documentation/modules/taxonomy')) . '</p>';
52 $output .= '<h3>' . t('Uses') . '</h3>';
53 $output .= '<dl>';
54 $output .= '<dt>' . t('Creating vocabularies') . '</dt>';
55 $output .= '<dd>' . t('Users with sufficient <a href="@perm">permissions</a> can create <em>vocabularies</em> and <em>terms</em> through the <a href="@taxo">Taxonomy page</a>. The page listing the terms provides a drag-and-drop interface for controlling the order of the terms and sub-terms within a vocabulary, in a hierarchical fashion. A <em>controlled vocabulary</em> classifying music by genre with terms and sub-terms could look as follows:', array('@taxo' => url('admin/structure/taxonomy'), '@perm' => url('admin/people/permissions', array('fragment'=>'module-taxonomy'))));
56 $output .= '<ul><li>' . t('<em>vocabulary</em>: Music') . '</li>';
57 $output .= '<ul><li>' . t('<em>term</em>: Jazz') . '</li>';
58 $output .= '<ul><li>' . t('<em>sub-term</em>: Swing') . '</li>';
59 $output .= '<li>' . t('<em>sub-term</em>: Fusion') . '</li></ul></ul>';
60 $output .= '<ul><li>' . t('<em>term</em>: Rock') . '</li>';
61 $output .= '<ul><li>' . t('<em>sub-term</em>: Country rock') . '</li>';
62 $output .= '<li>' . t('<em>sub-term</em>: Hard rock') . '</li></ul></ul></ul>';
63 $output .= t('You can assign a sub-term to multiple parent terms. For example, <em>fusion</em> can be assigned to both <em>rock</em> and <em>jazz</em>.') . '</dd>';
64 $output .= '<dd>' . t('Terms in a <em>free-tagging vocabulary</em> can be built gradually as you create or edit content. This is often done used for blogs or photo management applications.') . '</dd>';
65 $output .= '<dt>' . t('Assigning vocabularies to content types') . '</dt>';
66 $output .= '<dd>' . t('Before you can use a new vocabulary to classify your content, a new Taxonomy term field must be added to a <a href="@ctedit">content type</a> on its <em>manage fields</em> page. When adding a taxonomy field, you choose a <em>widget</em> to use to enter the taxonomy information on the content editing page: a select list, checkboxes, radio buttons, or an auto-complete field (to build a free-tagging vocabulary). After choosing the field type and widget, on the subsequent <em>field settings</em> page you can choose the desired vocabulary, whether one or multiple terms can be chosen from the vocabulary, and other settings. The same vocabulary can be added to multiple content types, by using the "Re-use existing field" section on the manage fields page.', array('@ctedit' => url('admin/structure/types'))) . '</dd>';
67 $output .= '<dt>' . t('Classifying content') . '</dt>';
68 $output .= '<dd>' . t('After the vocabulary is assigned to the content type, you can start classifying content. The field with terms will appear on the content editing screen when you edit or <a href="@addnode">add new content</a>.', array('@addnode' => url('node/add'))) . '</dd>';
69 $output .= '<dt>' . t('Viewing listings and RSS feeds by term') . '</dt>';
70 $output .= '<dd>' . t("Each taxonomy term automatically provides a page listing content that has its classification, and a corresponding RSS feed. For example, if the taxonomy term <em>country rock</em> has the ID 123 (you can see this by looking at the URL when hovering on the linked term, which you can click to navigate to the listing page), then you will find this list at the path <em>taxonomy/term/123</em>. The RSS feed will use the path <em>taxonomy/term/123/feed</em> (the RSS icon for this term's listing will automatically display in your browser's address bar when viewing the listing page).") . '</dd>';
71 $output .= '<dt>' . t('Extending Taxonomy module') . '</dt>';
72 $output .= '<dd>' . t('There are <a href="@taxcontrib">many contributed modules</a> that extend the behavior of the Taxonomy module for both display and organization of terms.', array('@taxcontrib' => 'http://drupal.org/project/modules?filters=tid:71&solrsort=sis_project_release_usage%20desc'));
73 $output .= '</dl>';
74 return $output;
75 case 'admin/structure/taxonomy':
76 $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>';
77 return $output;
78 case 'admin/structure/taxonomy/manage/%':
79 $vocabulary = entity_load('taxonomy_vocabulary', $arg[4]);
80 switch ($vocabulary->hierarchy) {
81 case TAXONOMY_HIERARCHY_DISABLED:
82 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' => drupal_ucfirst($vocabulary->name), '%name' => $vocabulary->name)) . '</p>';
83 case TAXONOMY_HIERARCHY_SINGLE:
84 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' => drupal_ucfirst($vocabulary->name), '%name' => $vocabulary->name)) . '</p>';
85 case TAXONOMY_HIERARCHY_MULTIPLE:
86 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' => drupal_ucfirst($vocabulary->name))) . '</p>';
87 }
88 }
89 }
90
91 /**
92 * Implements hook_permission().
93 */
94 function taxonomy_permission() {
95 $permissions = array(
96 'administer taxonomy' => array(
97 'title' => t('Administer vocabularies and terms'),
98 ),
99 );
100 foreach (entity_load_multiple('taxonomy_vocabulary') as $vocabulary) {
101 $permissions += array(
102 'edit terms in ' . $vocabulary->id() => array(
103 'title' => t('Edit terms in %vocabulary', array('%vocabulary' => $vocabulary->name)),
104 ),
105 );
106 $permissions += array(
107 'delete terms in ' . $vocabulary->id() => array(
108 'title' => t('Delete terms from %vocabulary', array('%vocabulary' => $vocabulary->name)),
109 ),
110 );
111 }
112 return $permissions;
113 }
114
115 /**
116 * Implements hook_entity_bundle_info().
117 */
118 function taxonomy_entity_bundle_info() {
119 $bundles = array();
120 foreach (taxonomy_vocabulary_get_names() as $id) {
121 $config = \Drupal::config('taxonomy.vocabulary.' . $id);
122 $bundles['taxonomy_term'][$id]['label'] = $config->get('name');
123 }
124 return $bundles;
125 }
126
127 /**
128 * Entity URI callback.
129 */
130 function taxonomy_term_uri($term) {
131 return new Url('taxonomy.term_page', array(
132 'taxonomy_term' => $term->id(),
133 ));
134 }
135
136 /**
137 * Return nodes attached to a term across all field instances.
138 *
139 * This function requires taxonomy module to be maintaining its own tables,
140 * and will return an empty array if it is not. If using other field storage
141 * methods alternatives methods for listing terms will need to be used.
142 *
143 * @param $tid
144 * The term ID.
145 * @param $pager
146 * Boolean to indicate whether a pager should be used.
147 * @param $limit
148 * Integer. The maximum number of nodes to find.
149 * Set to FALSE for no limit.
150 * @param $order
151 * An array of fields and directions.
152 *
153 * @return
154 * An array of nids matching the query.
155 */
156 function taxonomy_select_nodes($tid, $pager = TRUE, $limit = FALSE, $order = array('t.sticky' => 'DESC', 't.created' => 'DESC')) {
157 if (!\Drupal::config('taxonomy.settings')->get('maintain_index_table')) {
158 return array();
159 }
160 $query = db_select('taxonomy_index', 't');
161 $query->addTag('node_access');
162 $query->addMetaData('base_table', 'taxonomy_index');
163 $query->condition('tid', $tid);
164 if ($pager) {
165 $count_query = clone $query;
166 $count_query->addExpression('COUNT(t.nid)');
167
168 $query = $query->extend('Drupal\Core\Database\Query\PagerSelectExtender');
169 if ($limit !== FALSE) {
170 $query = $query->limit($limit);
171 }
172 $query->setCountQuery($count_query);
173 }
174 else {
175 if ($limit !== FALSE) {
176 $query->range(0, $limit);
177 }
178 }
179 $query->addField('t', 'nid');
180 $query->addField('t', 'tid');
181 foreach ($order as $field => $direction) {
182 $query->orderBy($field, $direction);
183 // ORDER BY fields need to be loaded too, assume they are in the form
184 // table_alias.name
185 list($table_alias, $name) = explode('.', $field);
186 $query->addField($table_alias, $name);
187 }
188 return $query->execute()->fetchCol();
189 }
190
191 /**
192 * Implements hook_theme().
193 */
194 function taxonomy_theme() {
195 return array(
196 'taxonomy_term' => array(
197 'render element' => 'elements',
198 'template' => 'taxonomy-term',
199 ),
200 );
201 }
202
203 /**
204 * Checks and updates the hierarchy flag of a vocabulary.
205 *
206 * Checks the current parents of all terms in a vocabulary and updates the
207 * vocabulary's hierarchy setting to the lowest possible level. If no term
208 * has parent terms then the vocabulary will be given a hierarchy of
209 * TAXONOMY_HIERARCHY_DISABLED. If any term has a single parent then the
210 * vocabulary will be given a hierarchy of TAXONOMY_HIERARCHY_SINGLE. If any
211 * term has multiple parents then the vocabulary will be given a hierarchy of
212 * TAXONOMY_HIERARCHY_MULTIPLE.
213 *
214 * @param \Drupal\taxonomy\VocabularyInterface $vocabulary
215 * A taxonomy vocabulary entity.
216 * @param $changed_term
217 * An array of the term structure that was updated.
218 *
219 * @return
220 * An integer that represents the level of the vocabulary's hierarchy.
221 */
222 function taxonomy_check_vocabulary_hierarchy(VocabularyInterface $vocabulary, $changed_term) {
223 $tree = taxonomy_get_tree($vocabulary->id());
224 $hierarchy = TAXONOMY_HIERARCHY_DISABLED;
225 foreach ($tree as $term) {
226 // Update the changed term with the new parent value before comparison.
227 if ($term->tid == $changed_term['tid']) {
228 $term = (object) $changed_term;
229 $term->parents = $term->parent;
230 }
231 // Check this term's parent count.
232 if (count($term->parents) > 1) {
233 $hierarchy = TAXONOMY_HIERARCHY_MULTIPLE;
234 break;
235 }
236 elseif (count($term->parents) == 1 && !isset($term->parents[0])) {
237 $hierarchy = TAXONOMY_HIERARCHY_SINGLE;
238 }
239 }
240 if ($hierarchy != $vocabulary->hierarchy) {
241 $vocabulary->hierarchy = $hierarchy;
242 $vocabulary->save();
243 }
244
245 return $hierarchy;
246 }
247
248 /**
249 * Generates an array which displays a term detail page.
250 *
251 * @param \Drupal\taxonomy\Entity\Term $term
252 * A taxonomy term object.
253 * @param string $view_mode
254 * View mode, e.g. 'full', 'teaser'...
255 * @param string $langcode
256 * (optional) A language code to use for rendering. Defaults to the global
257 * content language of the current request.
258 *
259 * @return array
260 * A $page element suitable for use by drupal_render().
261 */
262 function taxonomy_term_view(Term $term, $view_mode = 'full', $langcode = NULL) {
263 return entity_view($term, $view_mode, $langcode);
264 }
265
266 /**
267 * Constructs a drupal_render() style array from an array of loaded terms.
268 *
269 * @param array $terms
270 * An array of taxonomy terms as returned by entity_load_multiple('taxonomy_term').
271 * @param string $view_mode
272 * View mode, e.g. 'full', 'teaser'...
273 * @param string $langcode
274 * (optional) A language code to use for rendering. Defaults to the global
275 * content language of the current request.
276 *
277 * @return array
278 * An array in the format expected by drupal_render().
279 */
280 function taxonomy_term_view_multiple(array $terms, $view_mode = 'full', $langcode = NULL) {
281 return entity_view_multiple($terms, $view_mode, $langcode);
282 }
283
284 /**
285 * Implements hook_theme_suggestions_HOOK().
286 */
287 function taxonomy_theme_suggestions_taxonomy_term(array $variables) {
288 $suggestions = array();
289
290 /** @var \Drupal\taxonomy\TermInterface $term */
291 $term = $variables['elements']['#taxonomy_term'];
292
293 $suggestions[] = 'taxonomy_term__' . $term->bundle();
294 $suggestions[] = 'taxonomy_term__' . $term->id();
295
296 return $suggestions;
297 }
298
299 /**
300 * Prepares variables for taxonomy term templates.
301 *
302 * Default template: taxonomy-term.html.twig.
303 *
304 * @param array $variables
305 * An associative array containing:
306 * - elements: An associative array containing the taxonomy term and any
307 * fields attached to the term. Properties used:
308 * - #taxonomy_term: A \Drupal\taxonomy\TermInterface object.
309 * - #view_mode: The current view mode for this taxonomy term, e.g.
310 * 'full' or 'teaser'.
311 * - attributes: HTML attributes for the containing element.
312 */
313 function template_preprocess_taxonomy_term(&$variables) {
314 $variables['view_mode'] = $variables['elements']['#view_mode'];
315 $variables['term'] = $variables['elements']['#taxonomy_term'];
316 /** @var \Drupal\taxonomy\TermInterface $term */
317 $term = $variables['term'];
318
319 $variables['url'] = $term->url();
320 // We use name here because that is what appears in the UI.
321 $variables['name'] = $variables['elements']['name'];
322 unset($variables['elements']['name']);
323 $variables['page'] = $variables['view_mode'] == 'full' && taxonomy_term_is_page($term);
324
325 // Helpful $content variable for templates.
326 $variables['content'] = array();
327 foreach (Element::children($variables['elements']) as $key) {
328 $variables['content'][$key] = $variables['elements'][$key];
329 }
330
331 // Gather classes, and clean up name so there are no underscores.
332 $variables['attributes']['class'][] = 'taxonomy-term';
333 $vocabulary_name_css = str_replace('_', '-', $term->bundle());
334 $variables['attributes']['class'][] = 'vocabulary-' . $vocabulary_name_css;
335 }
336
337 /**
338 * Returns whether the current page is the page of the passed-in term.
339 *
340 * @param \Drupal\taxonomy\Entity\Term $term
341 * A taxonomy term entity.
342 */
343 function taxonomy_term_is_page(Term $term) {
344 $request = \Drupal::request();
345 if ($request->attributes->has('taxonomy_term')) {
346 $page_term = $request->attributes->get('taxonomy_term');
347 return $page_term->id() == $term->id();
348 }
349 return FALSE;
350 }
351
352 /**
353 * Clear all static cache variables for terms.
354 */
355 function taxonomy_terms_static_reset() {
356 \Drupal::entityManager()->getStorage('taxonomy_term')->resetCache();
357 }
358
359 /**
360 * Clear all static cache variables for vocabularies.
361 *
362 * @param $ids
363 * An array of ids to reset in entity controller cache.
364 */
365 function taxonomy_vocabulary_static_reset(array $ids = NULL) {
366 \Drupal::entityManager()->getStorage('taxonomy_vocabulary')->resetCache($ids);
367 }
368
369 /**
370 * Get names for all taxonomy vocabularies.
371 *
372 * @return array
373 * A list of existing vocabulary IDs.
374 */
375 function taxonomy_vocabulary_get_names() {
376 $names = &drupal_static(__FUNCTION__);
377
378 if (!isset($names)) {
379 $names = array();
380 $config_names = \Drupal::configFactory()->listAll('taxonomy.vocabulary.');
381 foreach ($config_names as $config_name) {
382 $id = substr($config_name, strlen('taxonomy.vocabulary.'));
383 $names[$id] = $id;
384 }
385 }
386
387 return $names;
388 }
389
390 /**
391 * Finds all parents of a given term ID.
392 *
393 * @param $tid
394 * A taxonomy term ID.
395 *
396 * @return
397 * An array of term objects which are the parents of the term $tid, or an
398 * empty array if parents are not found.
399 */
400 function taxonomy_term_load_parents($tid) {
401 $parents = &drupal_static(__FUNCTION__, array());
402
403 if ($tid && !isset($parents[$tid])) {
404 $tids = \Drupal::entityManager()->getStorage('taxonomy_term')->loadParents($tid);
405 $parents[$tid] = entity_load_multiple('taxonomy_term', $tids);
406 }
407
408 return isset($parents[$tid]) ? $parents[$tid] : array();
409 }
410
411 /**
412 * Find all ancestors of a given term ID.
413 */
414 function taxonomy_term_load_parents_all($tid) {
415 $cache = &drupal_static(__FUNCTION__, array());
416
417 if (isset($cache[$tid])) {
418 return $cache[$tid];
419 }
420
421 $parents = array();
422 if ($term = entity_load('taxonomy_term', $tid)) {
423 $parents[] = $term;
424 $n = 0;
425 while ($parent = taxonomy_term_load_parents($parents[$n]->id())) {
426 $parents = array_merge($parents, $parent);
427 $n++;
428 }
429 }
430
431 $cache[$tid] = $parents;
432
433 return $parents;
434 }
435
436 /**
437 * Finds all children of a term ID.
438 *
439 * @param $tid
440 * A taxonomy term ID.
441 *
442 * @return
443 * An array of term objects that are the children of the term $tid, or an
444 * empty array when no children exist.
445 */
446 function taxonomy_term_load_children($tid) {
447 $children = &drupal_static(__FUNCTION__, array());
448
449 if ($tid && !isset($children[$tid])) {
450 $tids = \Drupal::entityManager()->getStorage('taxonomy_term')->loadChildren($tid);
451 $children[$tid] = entity_load_multiple('taxonomy_term', $tids);
452 }
453
454 return isset($children[$tid]) ? $children[$tid] : array();
455 }
456
457 /**
458 * Create a hierarchical representation of a vocabulary.
459 *
460 * @param $vid
461 * The vocabulary ID to generate the tree for.
462 * @param $parent
463 * The term ID under which to generate the tree. If 0, generate the tree
464 * for the entire vocabulary.
465 * @param $max_depth
466 * The number of levels of the tree to return. Leave NULL to return all levels.
467 * @param $load_entities
468 * If TRUE, a full entity load will occur on the term objects. Otherwise they
469 * are partial objects queried directly from the {taxonomy_term_data} table to
470 * save execution time and memory consumption when listing large numbers of
471 * terms. Defaults to FALSE.
472 *
473 * @return
474 * An array of all term objects in the tree. Each term object is extended
475 * to have "depth" and "parents" attributes in addition to its normal ones.
476 * Results are statically cached. Term objects will be partial or complete
477 * depending on the $load_entities parameter.
478 */
479 function taxonomy_get_tree($vid, $parent = 0, $max_depth = NULL, $load_entities = FALSE) {
480 $children = &drupal_static(__FUNCTION__, array());
481 $parents = &drupal_static(__FUNCTION__ . ':parents', array());
482 $terms = &drupal_static(__FUNCTION__ . ':terms', array());
483
484 // We cache trees, so it's not CPU-intensive to call taxonomy_get_tree() on a
485 // term and its children, too.
486 if (!isset($children[$vid])) {
487 $children[$vid] = array();
488 $parents[$vid] = array();
489 $terms[$vid] = array();
490
491 $result = \Drupal::entityManager()->getStorage('taxonomy_term')->loadTree($vid);
492
493 foreach ($result as $term) {
494 $children[$vid][$term->parent][] = $term->tid;
495 $parents[$vid][$term->tid][] = $term->parent;
496 $terms[$vid][$term->tid] = $term;
497 }
498 }
499
500 // Load full entities, if necessary. The entity controller statically
501 // caches the results.
502 if ($load_entities) {
503 $term_entities = entity_load_multiple('taxonomy_term', array_keys($terms[$vid]));
504 }
505
506 $max_depth = (!isset($max_depth)) ? count($children[$vid]) : $max_depth;
507 $tree = array();
508
509 // Keeps track of the parents we have to process, the last entry is used
510 // for the next processing step.
511 $process_parents = array();
512 $process_parents[] = $parent;
513
514 // Loops over the parent terms and adds its children to the tree array.
515 // Uses a loop instead of a recursion, because it's more efficient.
516 while (count($process_parents)) {
517 $parent = array_pop($process_parents);
518 // The number of parents determines the current depth.
519 $depth = count($process_parents);
520 if ($max_depth > $depth && !empty($children[$vid][$parent])) {
521 $has_children = FALSE;
522 $child = current($children[$vid][$parent]);
523 do {
524 if (empty($child)) {
525 break;
526 }
527 $term = $load_entities ? $term_entities[$child] : $terms[$vid][$child];
528 if (isset($parents[$vid][$load_entities ? $term->id() : $term->tid])) {
529 // Clone the term so that the depth attribute remains correct
530 // in the event of multiple parents.
531 $term = clone $term;
532 }
533 $term->depth = $depth;
534 unset($term->parent);
535 $tid = $load_entities ? $term->id() : $term->tid;
536 $term->parents = $parents[$vid][$tid];
537 $tree[] = $term;
538 if (!empty($children[$vid][$tid])) {
539 $has_children = TRUE;
540
541 // We have to continue with this parent later.
542 $process_parents[] = $parent;
543 // Use the current term as parent for the next iteration.
544 $process_parents[] = $tid;
545
546 // Reset pointers for child lists because we step in there more often
547 // with multi parents.
548 reset($children[$vid][$tid]);
549 // Move pointer so that we get the correct term the next time.
550 next($children[$vid][$parent]);
551 break;
552 }
553 } while ($child = next($children[$vid][$parent]));
554
555 if (!$has_children) {
556 // We processed all terms in this hierarchy-level, reset pointer
557 // so that this function works the next time it gets called.
558 reset($children[$vid][$parent]);
559 }
560 }
561 }
562
563 return $tree;
564 }
565
566 /**
567 * Try to map a string to an existing term, as for glossary use.
568 *
569 * Provides a case-insensitive and trimmed mapping, to maximize the
570 * likelihood of a successful match.
571 *
572 * @param $name
573 * Name of the term to search for.
574 * @param $vocabulary
575 * (optional) Vocabulary machine name to limit the search. Defaults to NULL.
576 *
577 * @return
578 * An array of matching term objects.
579 */
580 function taxonomy_term_load_multiple_by_name($name, $vocabulary = NULL) {
581 $values = array('name' => trim($name));
582 if (isset($vocabulary)) {
583 $vocabularies = taxonomy_vocabulary_get_names();
584 if (isset($vocabularies[$vocabulary])){
585 $values['vid'] = $vocabulary;
586 }
587 else {
588 // Return an empty array when filtering by a non-existing vocabulary.
589 return array();
590 }
591 }
592 return entity_load_multiple_by_properties('taxonomy_term', $values);
593 }
594
595 /**
596 * Load multiple taxonomy terms based on certain conditions.
597 *
598 * This function should be used whenever you need to load more than one term
599 * from the database. Terms are loaded into memory and will not require
600 * database access if loaded again during the same page request.
601 *
602 * @see entity_load_multiple()
603 * @see \Drupal\Core\Entity\Query\EntityQueryInterface
604 *
605 * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
606 * Use entity_load_multiple('taxonomy_term', $tids).
607 *
608 * @param array $tids
609 * (optional) An array of entity IDs. If omitted, all entities are loaded.
610 *
611 * @return array
612 * An array of taxonomy term entities, indexed by tid. When no results are
613 * found, an empty array is returned.
614 */
615 function taxonomy_term_load_multiple(array $tids = NULL) {
616 return entity_load_multiple('taxonomy_term', $tids);
617 }
618
619 /**
620 * Loads multiple taxonomy vocabularies based on certain conditions.
621 *
622 * This function should be used whenever you need to load more than one
623 * vocabulary from the database. Terms are loaded into memory and will not
624 * require database access if loaded again during the same page request.
625 *
626 * @see entity_load_multiple()
627 *
628 * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
629 * Use entity_load_multiple('taxonomy_vocabulary', $vids).
630 *
631 * @param array $vids
632 * (optional) An array of entity IDs. If omitted, all entities are loaded.
633 *
634 * @return array
635 * An array of vocabulary objects, indexed by vid.
636 */
637 function taxonomy_vocabulary_load_multiple(array $vids = NULL) {
638 return entity_load_multiple('taxonomy_vocabulary', $vids);
639 }
640
641 /**
642 * Return the taxonomy vocabulary entity matching a vocabulary ID.
643 *
644 * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
645 * Use entity_load('taxonomy_vocabulary', $vid).
646 *
647 * @param int $vid
648 * The vocabulary's ID.
649 *
650 * @return \Drupal\taxonomy\Entity\Vocabulary|null
651 * The taxonomy vocabulary entity, if exists, NULL otherwise. Results are
652 * statically cached.
653 */
654 function taxonomy_vocabulary_load($vid) {
655 return entity_load('taxonomy_vocabulary', $vid);
656 }
657
658 /**
659 * Return the taxonomy term entity matching a term ID.
660 *
661 * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
662 * Use entity_load('taxonomy_term', $tid).
663 *
664 * @param $tid
665 * A term's ID
666 *
667 * @return \Drupal\taxonomy\Entity\Term|null
668 * A taxonomy term entity, or NULL if the term was not found. Results are
669 * statically cached.
670 */
671 function taxonomy_term_load($tid) {
672 if (!is_numeric($tid)) {
673 return NULL;
674 }
675 return entity_load('taxonomy_term', $tid);
676 }
677
678 /**
679 * Implements hook_file_download_access().
680 */
681 function taxonomy_file_download_access($field, EntityInterface $entity, FileInterface $file) {
682 if ($entity->getEntityTypeId() == 'taxonomy_term') {
683 return $entity->access('view');
684 }
685 }
686
687 /**
688 * Implodes a list of tags of a certain vocabulary into a string.
689 *
690 * @see \Drupal\Component\Utility\Tags::explode()
691 */
692 function taxonomy_implode_tags($tags, $vid = NULL) {
693 $typed_tags = array();
694 foreach ($tags as $tag) {
695 // Extract terms belonging to the vocabulary in question.
696 if (!isset($vid) || $tag->bundle() == $vid) {
697 // Make sure we have a completed loaded taxonomy term.
698 if ($tag instanceof EntityInterface && $label = $tag->label()) {
699 // Commas and quotes in tag names are special cases, so encode 'em.
700 $typed_tags[] = Tags::encode($label);
701 }
702 }
703 }
704 return implode(', ', $typed_tags);
705 }
706
707 /**
708 * Implements hook_field_widget_info_alter().
709 */
710 function taxonomy_field_widget_info_alter(&$info) {
711 $info['options_select']['field_types'][] = 'taxonomy_term_reference';
712 $info['options_buttons']['field_types'][] = 'taxonomy_term_reference';
713 }
714
715 /**
716 * Title callback for term pages.
717 *
718 * @param \Drupal\taxonomy\Entity\Term $term
719 * A taxonomy term entity.
720 *
721 * @return
722 * The term name to be used as the page title.
723 */
724 function taxonomy_term_title(Term $term) {
725 return $term->getName();
726 }
727
728 /**
729 * Form element validate handler for taxonomy term autocomplete element.
730 */
731 function taxonomy_autocomplete_validate($element, &$form_state) {
732 // Split the values into an array.
733 // @see \Drupal\taxonomy\Plugin\Field\FieldWidget\TaxonomyAutocompleteWidget:massageFormValues()
734 $typed_terms = array();
735 if ($tags = $element['#value']) {
736 $typed_terms = Tags::explode($tags);
737 }
738 form_set_value($element, $typed_terms, $form_state);
739 }
740
741 /**
742 * @defgroup taxonomy_index Taxonomy indexing
743 * @{
744 * Functions to maintain taxonomy indexing.
745 *
746 * Taxonomy uses default field storage to store canonical relationships
747 * between terms and fieldable entities. However its most common use case
748 * requires listing all content associated with a term or group of terms
749 * sorted by creation date. To avoid slow queries due to joining across
750 * multiple node and field tables with various conditions and order by criteria,
751 * we maintain a denormalized table with all relationships between terms,
752 * published nodes and common sort criteria such as sticky and created.
753 * This is used as a lookup table by taxonomy_select_nodes(). When using other
754 * field storage engines or alternative methods of denormalizing this data
755 * you should set the taxonomy.settings:maintain_index_table to '0' to avoid
756 * unnecessary writes in SQL.
757 */
758
759 /**
760 * Implements hook_node_insert().
761 */
762 function taxonomy_node_insert(EntityInterface $node) {
763 // Add taxonomy index entries for the node.
764 taxonomy_build_node_index($node);
765 }
766
767 /**
768 * Builds and inserts taxonomy index entries for a given node.
769 *
770 * The index lists all terms that are related to a given node entity, and is
771 * therefore maintained at the entity level.
772 *
773 * @param \Drupal\node\Entity\Node $node
774 * The node entity.
775 */
776 function taxonomy_build_node_index($node) {
777 // We maintain a denormalized table of term/node relationships, containing
778 // only data for current, published nodes.
779 if (!\Drupal::config('taxonomy.settings')->get('maintain_index_table') || !(\Drupal::entityManager()->getStorage('node') instanceof ContentEntityDatabaseStorage)) {
780 return;
781 }
782
783 $status = $node->isPublished();
784 $sticky = (int) $node->isSticky();
785 // We only maintain the taxonomy index for published nodes.
786 if ($status && $node->isDefaultRevision()) {
787 // Collect a unique list of all the term IDs from all node fields.
788 $tid_all = array();
789 foreach ($node->getFieldDefinitions() as $field) {
790 $field_name = $field->getName();
791 if ($field->getType() == 'taxonomy_term_reference') {
792 foreach ($node->getTranslationLanguages() as $language) {
793 foreach ($node->getTranslation($language->id)->$field_name as $item) {
794 if (!$item->isEmpty()) {
795 $tid_all[$item->target_id] = $item->target_id;
796 }
797 }
798 }
799 }
800 }
801 // Insert index entries for all the node's terms.
802 if (!empty($tid_all)) {
803 foreach ($tid_all as $tid) {
804 db_merge('taxonomy_index')
805 ->key(array('nid' => $node->id(), 'tid' => $tid))
806 ->fields(array('sticky' => $sticky, 'created' => $node->getCreatedTime()))
807 ->execute();
808 }
809 }
810 }
811 }
812
813 /**
814 * Implements hook_node_update().
815 */
816 function taxonomy_node_update(EntityInterface $node) {
817 // Always rebuild the node's taxonomy index entries on node save.
818 taxonomy_delete_node_index($node);
819 taxonomy_build_node_index($node);
820 }
821
822 /**
823 * Implements hook_node_predelete().
824 */
825 function taxonomy_node_predelete(EntityInterface $node) {
826 // Clean up the {taxonomy_index} table when nodes are deleted.
827 taxonomy_delete_node_index($node);
828 }
829
830 /**
831 * Deletes taxonomy index entries for a given node.
832 *
833 * @param \Drupal\Core\Entity\EntityInterface $node
834 * The node entity.
835 */
836 function taxonomy_delete_node_index(EntityInterface $node) {
837 if (\Drupal::config('taxonomy.settings')->get('maintain_index_table')) {
838 db_delete('taxonomy_index')->condition('nid', $node->id())->execute();
839 }
840 }
841
842 /**
843 * Implements hook_taxonomy_term_delete().
844 */
845 function taxonomy_taxonomy_term_delete(Term $term) {
846 if (\Drupal::config('taxonomy.settings')->get('maintain_index_table')) {
847 // Clean up the {taxonomy_index} table when terms are deleted.
848 db_delete('taxonomy_index')->condition('tid', $term->id())->execute();
849 }
850 }
851
852 /**
853 * @} End of "defgroup taxonomy_index".
854 */