First commit

This commit is contained in:
Theodotos Andreou 2018-01-14 13:10:16 +00:00
commit c6e2478c40
13918 changed files with 2303184 additions and 0 deletions

View file

@ -0,0 +1,54 @@
<?php
/**
* @file
* Default theme implementation to display a term.
*
* Available variables:
* - $name: (deprecated) The unsanitized name of the term. Use $term_name
* instead.
* - $content: An array of items for the content of the term (fields and
* description). Use render($content) to print them all, or print a subset
* such as render($content['field_example']). Use
* hide($content['field_example']) to temporarily suppress the printing of a
* given element.
* - $term_url: Direct URL of the current term.
* - $term_name: Name of the current term.
* - $classes: String of classes that can be used to style contextually through
* CSS. It can be manipulated through the variable $classes_array from
* preprocess functions. The default values can be one or more of the following:
* - taxonomy-term: The current template type, i.e., "theming hook".
* - vocabulary-[vocabulary-name]: The vocabulary to which the term belongs to.
* For example, if the term is a "Tag" it would result in "vocabulary-tag".
*
* Other variables:
* - $term: Full term object. Contains data that may not be safe.
* - $view_mode: View mode, e.g. 'full', 'teaser'...
* - $page: Flag for the full page state.
* - $classes_array: Array of html class attribute values. It is flattened
* into a string within the variable $classes.
* - $zebra: Outputs either "even" or "odd". Useful for zebra striping in
* teaser listings.
* - $id: Position of the term. Increments each time it's output.
* - $is_front: Flags true when presented in the front page.
* - $logged_in: Flags true when the current user is a logged-in member.
* - $is_admin: Flags true when the current user is an administrator.
*
* @see template_preprocess()
* @see template_preprocess_taxonomy_term()
* @see template_process()
*
* @ingroup themeable
*/
?>
<div id="taxonomy-term-<?php print $term->tid; ?>" class="<?php print $classes; ?>">
<?php if (!$page): ?>
<h2><a href="<?php print $term_url; ?>"><?php print $term_name; ?></a></h2>
<?php endif; ?>
<div class="content">
<?php print render($content); ?>
</div>
</div>

View file

@ -0,0 +1,984 @@
<?php
/**
* @file
* Administrative page callbacks for the taxonomy module.
*/
/**
* Form builder to list and manage vocabularies.
*
* @ingroup forms
* @see taxonomy_overview_vocabularies_submit()
* @see theme_taxonomy_overview_vocabularies()
*/
function taxonomy_overview_vocabularies($form) {
$vocabularies = taxonomy_get_vocabularies();
$form['#tree'] = TRUE;
foreach ($vocabularies as $vocabulary) {
$form[$vocabulary->vid]['#vocabulary'] = $vocabulary;
$form[$vocabulary->vid]['name'] = array('#markup' => check_plain($vocabulary->name));
$form[$vocabulary->vid]['weight'] = array(
'#type' => 'weight',
'#title' => t('Weight for @title', array('@title' => $vocabulary->name)),
'#title_display' => 'invisible',
'#delta' => 10,
'#default_value' => $vocabulary->weight,
);
$form[$vocabulary->vid]['edit'] = array('#type' => 'link', '#title' => t('edit vocabulary'), '#href' => "admin/structure/taxonomy/$vocabulary->machine_name/edit");
$form[$vocabulary->vid]['list'] = array('#type' => 'link', '#title' => t('list terms'), '#href' => "admin/structure/taxonomy/$vocabulary->machine_name");
$form[$vocabulary->vid]['add'] = array('#type' => 'link', '#title' => t('add terms'), '#href' => "admin/structure/taxonomy/$vocabulary->machine_name/add");
}
// Only make this form include a submit button and weight if more than one
// vocabulary exists.
if (count($vocabularies) > 1) {
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save'));
}
elseif (isset($vocabulary)) {
unset($form[$vocabulary->vid]['weight']);
}
return $form;
}
/**
* Submit handler for vocabularies overview. Updates changed vocabulary weights.
*
* @see taxonomy_overview_vocabularies()
*/
function taxonomy_overview_vocabularies_submit($form, &$form_state) {
foreach ($form_state['values'] as $vid => $vocabulary) {
if (is_numeric($vid) && $form[$vid]['#vocabulary']->weight != $form_state['values'][$vid]['weight']) {
$form[$vid]['#vocabulary']->weight = $form_state['values'][$vid]['weight'];
taxonomy_vocabulary_save($form[$vid]['#vocabulary']);
}
}
drupal_set_message(t('The configuration options have been saved.'));
}
/**
* Returns HTML for the vocabulary overview form as a sortable list of vocabularies.
*
* @param $variables
* An associative array containing:
* - form: A render element representing the form.
*
* @see taxonomy_overview_vocabularies()
* @ingroup themeable
*/
function theme_taxonomy_overview_vocabularies($variables) {
$form = $variables['form'];
$rows = array();
foreach (element_children($form) as $key) {
if (isset($form[$key]['name'])) {
$vocabulary = &$form[$key];
$row = array();
$row[] = drupal_render($vocabulary['name']);
if (isset($vocabulary['weight'])) {
$vocabulary['weight']['#attributes']['class'] = array('vocabulary-weight');
$row[] = drupal_render($vocabulary['weight']);
}
$row[] = drupal_render($vocabulary['edit']);
$row[] = drupal_render($vocabulary['list']);
$row[] = drupal_render($vocabulary['add']);
$rows[] = array('data' => $row, 'class' => array('draggable'));
}
}
$header = array(t('Vocabulary name'));
if (isset($form['actions'])) {
$header[] = t('Weight');
drupal_add_tabledrag('taxonomy', 'order', 'sibling', 'vocabulary-weight');
}
$header[] = array('data' => t('Operations'), 'colspan' => '3');
return theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No vocabularies available. <a href="@link">Add vocabulary</a>.', array('@link' => url('admin/structure/taxonomy/add'))), 'attributes' => array('id' => 'taxonomy'))) . drupal_render_children($form);
}
/**
* Form builder for the vocabulary editing form.
*
* @ingroup forms
* @see taxonomy_form_vocabulary_submit()
* @see taxonomy_form_vocabulary_validate()
*/
function taxonomy_form_vocabulary($form, &$form_state, $edit = array()) {
// During initial form build, add the entity to the form state for use
// during form building and processing. During a rebuild, use what is in the
// form state.
if (!isset($form_state['vocabulary'])) {
$vocabulary = is_object($edit) ? $edit : (object) $edit;
$defaults = array(
'name' => '',
'machine_name' => '',
'description' => '',
'hierarchy' => 0,
'weight' => 0,
);
foreach ($defaults as $key => $value) {
if (!isset($vocabulary->$key)) {
$vocabulary->$key = $value;
}
}
$form_state['vocabulary'] = $vocabulary;
}
else {
$vocabulary = $form_state['vocabulary'];
}
// @todo Legacy support. Modules are encouraged to access the entity using
// $form_state. Remove in Drupal 8.
$form['#vocabulary'] = $form_state['vocabulary'];
// Check whether we need a deletion confirmation form.
if (isset($form_state['confirm_delete']) && isset($form_state['values']['vid'])) {
return taxonomy_vocabulary_confirm_delete($form, $form_state, $form_state['values']['vid']);
}
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Name'),
'#default_value' => $vocabulary->name,
'#maxlength' => 255,
'#required' => TRUE,
);
$form['machine_name'] = array(
'#type' => 'machine_name',
'#default_value' => $vocabulary->machine_name,
'#maxlength' => 255,
'#machine_name' => array(
'exists' => 'taxonomy_vocabulary_machine_name_load',
),
);
$form['old_machine_name'] = array(
'#type' => 'value',
'#value' => $vocabulary->machine_name,
);
$form['description'] = array(
'#type' => 'textfield',
'#title' => t('Description'),
'#default_value' => $vocabulary->description,
);
// 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',
);
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save'));
if (isset($vocabulary->vid)) {
$form['actions']['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
$form['vid'] = array('#type' => 'value', '#value' => $vocabulary->vid);
$form['module'] = array('#type' => 'value', '#value' => $vocabulary->module);
}
$form['#validate'][] = 'taxonomy_form_vocabulary_validate';
return $form;
}
/**
* Form validation handler for taxonomy_form_vocabulary().
*
* Makes sure that the machine name of the vocabulary is not in the
* disallowed list (names that conflict with menu items, such as 'list'
* and 'add').
*
* @see taxonomy_form_vocabulary()
* @see taxonomy_form_vocabulary_submit()
*/
function taxonomy_form_vocabulary_validate($form, &$form_state) {
// During the deletion there is no 'machine_name' key
if (isset($form_state['values']['machine_name'])) {
// Do not allow machine names to conflict with taxonomy path arguments.
$machine_name = $form_state['values']['machine_name'];
$disallowed = array('add', 'list');
if (in_array($machine_name, $disallowed)) {
form_set_error('machine_name', t('The machine-readable name cannot be "add" or "list".'));
}
}
}
/**
* Form submission handler for taxonomy_form_vocabulary().
*
* @see taxonomy_form_vocabulary()
* @see taxonomy_form_vocabulary_validate()
*/
function taxonomy_form_vocabulary_submit($form, &$form_state) {
if ($form_state['triggering_element']['#value'] == t('Delete')) {
// Rebuild the form to confirm vocabulary deletion.
$form_state['rebuild'] = TRUE;
$form_state['confirm_delete'] = TRUE;
return;
}
$vocabulary = $form_state['vocabulary'];
entity_form_submit_build_entity('taxonomy_vocabulary', $vocabulary, $form, $form_state);
switch (taxonomy_vocabulary_save($vocabulary)) {
case SAVED_NEW:
drupal_set_message(t('Created new vocabulary %name.', array('%name' => $vocabulary->name)));
watchdog('taxonomy', 'Created new vocabulary %name.', array('%name' => $vocabulary->name), WATCHDOG_NOTICE, l(t('edit'), 'admin/structure/taxonomy/' . $vocabulary->machine_name . '/edit'));
break;
case SAVED_UPDATED:
drupal_set_message(t('Updated vocabulary %name.', array('%name' => $vocabulary->name)));
watchdog('taxonomy', 'Updated vocabulary %name.', array('%name' => $vocabulary->name), WATCHDOG_NOTICE, l(t('edit'), 'admin/structure/taxonomy/' . $vocabulary->machine_name . '/edit'));
break;
}
$form_state['values']['vid'] = $vocabulary->vid;
$form_state['vid'] = $vocabulary->vid;
$form_state['redirect'] = 'admin/structure/taxonomy';
}
/**
* Form builder for the taxonomy terms overview.
*
* 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.
*
* @ingroup forms
* @see taxonomy_overview_terms_submit()
* @see theme_taxonomy_overview_terms()
*/
function taxonomy_overview_terms($form, &$form_state, $vocabulary) {
global $pager_page_array, $pager_total, $pager_total_items;
// Check for confirmation forms.
if (isset($form_state['confirm_reset_alphabetical'])) {
return taxonomy_vocabulary_confirm_reset_alphabetical($form, $form_state, $vocabulary->vid);
}
$form['#vocabulary'] = $vocabulary;
$form['#tree'] = TRUE;
$form['#parent_fields'] = FALSE;
$page = isset($_GET['page']) ? $_GET['page'] : 0;
$page_increment = variable_get('taxonomy_terms_per_page_admin', 100); // Number of terms per page.
$page_entries = 0; // Elements shown on this page.
$before_entries = 0; // Elements at the root level before this page.
$after_entries = 0; // Elements at the root level after this page.
$root_entries = 0; // Elements at the root level on this page.
// 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 = taxonomy_get_tree($vocabulary->vid);
$term = current($tree);
do {
// In case this tree is completely empty.
if (empty($term)) {
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.
if (isset($term->depth) && ($term->depth > 0) && !isset($back_step)) {
$back_step = 0;
while ($pterm = prev($tree)) {
$before_entries--;
$back_step++;
if ($pterm->depth == 0) {
prev($tree);
continue 2; // Jump back to the start of the root level parent.
}
}
}
$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->tid] = isset($term_deltas[$term->tid]) ? $term_deltas[$term->tid] + 1 : 0;
$key = 'tid:' . $term->tid . ':' . $term_deltas[$term->tid];
// Keep track of the first term displayed on this page.
if ($page_entries == 1) {
$form['#first_tid'] = $term->tid;
}
// 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 ($term = next($tree));
// 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.
if (!empty($form_state['input'])) {
$order = array_flip(array_keys($form_state['input'])); // Get the $_POST order.
$current_page = array_merge($order, $current_page); // Update our form with the new order.
foreach ($current_page as $key => $term) {
// Verify this is a term for the current page and set at the current depth.
if (is_array($form_state['input'][$key]) && is_numeric($form_state['input'][$key]['tid'])) {
$current_page[$key]->depth = $form_state['input'][$key]['depth'];
}
else {
unset($current_page[$key]);
}
}
}
// Build the actual form.
foreach ($current_page as $key => $term) {
// Save the term for the current page so we don't have to load it a second time.
$form[$key]['#term'] = (array) $term;
if (isset($term->parents)) {
$form[$key]['#term']['parent'] = $term->parent = $term->parents[0];
unset($form[$key]['#term']['parents'], $term->parents);
}
$form[$key]['view'] = array('#type' => 'link', '#title' => $term->name, '#href' => "taxonomy/term/$term->tid");
if ($vocabulary->hierarchy < 2 && count($tree) > 1) {
$form['#parent_fields'] = TRUE;
$form[$key]['tid'] = array(
'#type' => 'hidden',
'#value' => $term->tid
);
$form[$key]['parent'] = array(
'#type' => 'hidden',
// Yes, default_value on a hidden. It needs to be changeable by the javascript.
'#default_value' => $term->parent,
);
$form[$key]['depth'] = array(
'#type' => 'hidden',
// Same as above, the depth is modified by javascript, so it's a default_value.
'#default_value' => $term->depth,
);
$form[$key]['weight'] = array(
'#type' => 'weight',
'#delta' => $delta,
'#title_display' => 'invisible',
'#title' => t('Weight for added term'),
'#default_value' => $term->weight,
);
}
$form[$key]['edit'] = array('#type' => 'link', '#title' => t('edit'), '#href' => 'taxonomy/term/' . $term->tid . '/edit', '#options' => array('query' => drupal_get_destination()));
}
$form['#total_entries'] = $total_entries;
$form['#page_increment'] = $page_increment;
$form['#page_entries'] = $page_entries;
$form['#back_step'] = $back_step;
$form['#forward_step'] = $forward_step;
$form['#empty_text'] = t('No terms available. <a href="@link">Add term</a>.', array('@link' => url('admin/structure/taxonomy/' . $vocabulary->machine_name . '/add')));
if ($vocabulary->hierarchy < 2 && count($tree) > 1) {
$form['actions'] = array('#type' => 'actions', '#tree' => FALSE);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save')
);
$form['actions']['reset_alphabetical'] = array(
'#type' => 'submit',
'#value' => t('Reset to alphabetical')
);
$form_state['redirect'] = array($_GET['q'], (isset($_GET['page']) ? array('query' => array('page' => $_GET['page'])) : array()));
}
return $form;
}
/**
* Submit handler for terms overview form.
*
* 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.
*
* @see taxonomy_overview_terms()
*/
function taxonomy_overview_terms_submit($form, &$form_state) {
if ($form_state['triggering_element']['#value'] == t('Reset to alphabetical')) {
// Execute the reset action.
if ($form_state['values']['reset_alphabetical'] === TRUE) {
return taxonomy_vocabulary_confirm_reset_alphabetical_submit($form, $form_state);
}
// Rebuild the form to confirm the reset action.
$form_state['rebuild'] = TRUE;
$form_state['confirm_reset_alphabetical'] = TRUE;
return;
}
// Sort term order based on weight.
uasort($form_state['values'], 'drupal_sort_weight');
$vocabulary = $form['#vocabulary'];
$hierarchy = 0; // Update the current hierarchy type as we go.
$changed_terms = array();
$tree = taxonomy_get_tree($vocabulary->vid);
if (empty($tree)) {
return;
}
// Build a list of all terms that need to be updated on previous pages.
$weight = 0;
$term = (array) $tree[0];
while ($term['tid'] != $form['#first_tid']) {
if ($term['parents'][0] == 0 && $term['weight'] != $weight) {
$term['parent'] = $term['parents'][0];
$term['weight'] = $weight;
$changed_terms[$term['tid']] = $term;
}
$weight++;
$hierarchy = $term['parents'][0] != 0 ? 1 : $hierarchy;
$term = (array) $tree[$weight];
}
// Renumber the current page weights and assign any new parents.
$level_weights = array();
foreach ($form_state['values'] as $tid => $values) {
if (isset($form[$tid]['#term'])) {
$term = $form[$tid]['#term'];
// Give terms at the root level a weight in sequence with terms on previous pages.
if ($values['parent'] == 0 && $term['weight'] != $weight) {
$term['weight'] = $weight;
$changed_terms[$term['tid']] = $term;
}
// Terms not at the root level can safely start from 0 because they're all on this page.
elseif ($values['parent'] > 0) {
$level_weights[$values['parent']] = isset($level_weights[$values['parent']]) ? $level_weights[$values['parent']] + 1 : 0;
if ($level_weights[$values['parent']] != $term['weight']) {
$term['weight'] = $level_weights[$values['parent']];
$changed_terms[$term['tid']] = $term;
}
}
// Update any changed parents.
if ($values['parent'] != $term['parent']) {
$term['parent'] = $values['parent'];
$changed_terms[$term['tid']] = $term;
}
$hierarchy = $term['parent'] != 0 ? 1 : $hierarchy;
$weight++;
}
}
// Build a list of all terms that need to be updated on following pages.
for ($weight; $weight < count($tree); $weight++) {
$term = (array) $tree[$weight];
if ($term['parents'][0] == 0 && $term['weight'] != $weight) {
$term['parent'] = $term['parents'][0];
$term['weight'] = $weight;
$changed_terms[$term['tid']] = $term;
}
$hierarchy = $term['parents'][0] != 0 ? 1 : $hierarchy;
}
// Save all updated terms.
foreach ($changed_terms as $changed) {
$term = (object) $changed;
// Update term_hierachy and term_data directly since we don't have a
// fully populated term object to save.
db_update('taxonomy_term_hierarchy')
->fields(array('parent' => $term->parent))
->condition('tid', $term->tid, '=')
->execute();
db_update('taxonomy_term_data')
->fields(array('weight' => $term->weight))
->condition('tid', $term->tid, '=')
->execute();
}
// Update the vocabulary hierarchy to flat or single hierarchy.
if ($vocabulary->hierarchy != $hierarchy) {
$vocabulary->hierarchy = $hierarchy;
taxonomy_vocabulary_save($vocabulary);
}
drupal_set_message(t('The configuration options have been saved.'));
}
/**
* Returns HTML for a terms overview form as a sortable list of terms.
*
* @param $variables
* An associative array containing:
* - form: A render element representing the form.
*
* @see taxonomy_overview_terms()
* @ingroup themeable
*/
function theme_taxonomy_overview_terms($variables) {
$form = $variables['form'];
$page_increment = $form['#page_increment'];
$page_entries = $form['#page_entries'];
$back_step = $form['#back_step'];
$forward_step = $form['#forward_step'];
// Add drag and drop if parent fields are present in the form.
if ($form['#parent_fields']) {
drupal_add_tabledrag('taxonomy', 'match', 'parent', 'term-parent', 'term-parent', 'term-id', FALSE);
drupal_add_tabledrag('taxonomy', 'depth', 'group', 'term-depth', NULL, NULL, FALSE);
drupal_add_js(drupal_get_path('module', 'taxonomy') . '/taxonomy.js');
drupal_add_js(array('taxonomy' => array('backStep' => $back_step, 'forwardStep' => $forward_step)), 'setting');
drupal_add_css(drupal_get_path('module', 'taxonomy') . '/taxonomy.css');
}
drupal_add_tabledrag('taxonomy', 'order', 'sibling', 'term-weight');
$errors = form_get_errors() != FALSE ? form_get_errors() : array();
$rows = array();
foreach (element_children($form) as $key) {
if (isset($form[$key]['#term'])) {
$term = &$form[$key];
$row = array();
$row[] = (isset($term['#term']['depth']) && $term['#term']['depth'] > 0 ? theme('indentation', array('size' => $term['#term']['depth'])) : ''). drupal_render($term['view']);
if ($form['#parent_fields']) {
$term['tid']['#attributes']['class'] = array('term-id');
$term['parent']['#attributes']['class'] = array('term-parent');
$term['depth']['#attributes']['class'] = array('term-depth');
$row[0] .= drupal_render($term['parent']) . drupal_render($term['tid']) . drupal_render($term['depth']);
}
$term['weight']['#attributes']['class'] = array('term-weight');
$row[] = drupal_render($term['weight']);
$row[] = drupal_render($term['edit']);
$row = array('data' => $row);
$rows[$key] = $row;
}
}
// Add necessary classes to rows.
$row_position = 0;
foreach ($rows as $key => $row) {
$rows[$key]['class'] = array();
if (isset($form['#parent_fields'])) {
$rows[$key]['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) {
$rows[$key]['class'][] = 'taxonomy-term-preview';
}
if ($row_position !== 0 && $row_position !== count($rows) - 1) {
if ($row_position == $back_step - 1 || $row_position == $page_entries - $forward_step - 1) {
$rows[$key]['class'][] = 'taxonomy-term-divider-top';
}
elseif ($row_position == $back_step || $row_position == $page_entries - $forward_step) {
$rows[$key]['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) {
$rows[$key]['class'][] = 'error';
}
}
$row_position++;
}
if (empty($rows)) {
$rows[] = array(array('data' => $form['#empty_text'], 'colspan' => '3'));
}
$header = array(t('Name'), t('Weight'), t('Operations'));
$output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'taxonomy')));
$output .= drupal_render_children($form);
$output .= theme('pager');
return $output;
}
/**
* Form function for the term edit form.
*
* @ingroup forms
* @see taxonomy_form_term_submit()
*/
function taxonomy_form_term($form, &$form_state, $edit = array(), $vocabulary = NULL) {
// During initial form build, add the term entity to the form state for use
// during form building and processing. During a rebuild, use what is in the
// form state.
if (!isset($form_state['term'])) {
$term = is_object($edit) ? $edit : (object) $edit;
if (!isset($vocabulary) && isset($term->vid)) {
$vocabulary = taxonomy_vocabulary_load($term->vid);
}
$defaults = array(
'name' => '',
'description' => '',
'format' => NULL,
'vocabulary_machine_name' => isset($vocabulary) ? $vocabulary->machine_name : NULL,
'tid' => NULL,
'weight' => 0,
);
foreach ($defaults as $key => $value) {
if (!isset($term->$key)) {
$term->$key = $value;
}
}
$form_state['term'] = $term;
}
else {
$term = $form_state['term'];
if (!isset($vocabulary) && isset($term->vid)) {
$vocabulary = taxonomy_vocabulary_load($term->vid);
}
}
$parent = array_keys(taxonomy_get_parents($term->tid));
$form['#term'] = (array) $term;
$form['#term']['parent'] = $parent;
$form['#vocabulary'] = $vocabulary;
// Check for confirmation forms.
if (isset($form_state['confirm_delete'])) {
return array_merge($form, taxonomy_term_confirm_delete($form, $form_state, $term->tid));
}
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Name'),
'#default_value' => $term->name,
'#maxlength' => 255,
'#required' => TRUE,
'#weight' => -5,
);
$form['description'] = array(
'#type' => 'text_format',
'#title' => t('Description'),
'#default_value' => $term->description,
'#format' => $term->format,
'#weight' => 0,
);
$form['vocabulary_machine_name'] = array(
'#type' => 'value',
'#value' => isset($term->vocabulary_machine_name) ? $term->vocabulary_machine_name : $vocabulary->name,
);
$langcode = entity_language('taxonomy_term', $term);
field_attach_form('taxonomy_term', $term, $form, $form_state, $langcode);
$form['relations'] = array(
'#type' => 'fieldset',
'#title' => t('Relations'),
'#collapsible' => TRUE,
'#collapsed' => $vocabulary->hierarchy < 2,
'#weight' => 10,
);
// taxonomy_get_tree and taxonomy_get_parents may contain large numbers of
// items so we check for taxonomy_override_selector before loading the
// full vocabulary. Contrib modules can then intercept before
// hook_form_alter to provide scalable alternatives.
if (!variable_get('taxonomy_override_selector', FALSE)) {
$parent = array_keys(taxonomy_get_parents($term->tid));
$children = taxonomy_get_tree($vocabulary->vid, $term->tid);
// A term can't be the child of itself, nor of its children.
foreach ($children as $child) {
$exclude[] = $child->tid;
}
$exclude[] = $term->tid;
$tree = taxonomy_get_tree($vocabulary->vid);
$options = array('<' . 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' => t('Parent terms'),
'#options' => $options,
'#default_value' => $parent,
'#multiple' => TRUE,
);
}
$form['relations']['weight'] = array(
'#type' => 'textfield',
'#title' => t('Weight'),
'#size' => 6,
'#default_value' => $term->weight,
'#description' => t('Terms are displayed in ascending order by weight.'),
'#required' => TRUE,
);
$form['vid'] = array(
'#type' => 'value',
'#value' => $vocabulary->vid,
);
$form['tid'] = array(
'#type' => 'value',
'#value' => $term->tid,
);
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
'#weight' => 5,
);
if ($term->tid) {
$form['actions']['delete'] = array(
'#type' => 'submit',
'#value' => t('Delete'),
'#access' => user_access("delete terms in $vocabulary->vid") || user_access('administer taxonomy'),
'#weight' => 10,
);
}
else {
$form_state['redirect'] = $_GET['q'];
}
return $form;
}
/**
* Validation handler for the term form.
*
* @see taxonomy_form_term()
*/
function taxonomy_form_term_validate($form, &$form_state) {
entity_form_field_validate('taxonomy_term', $form, $form_state);
// Ensure numeric values.
if (isset($form_state['values']['weight']) && !is_numeric($form_state['values']['weight'])) {
form_set_error('weight', t('Weight value must be numeric.'));
}
}
/**
* Submit handler to insert or update a term.
*
* @see taxonomy_form_term()
*/
function taxonomy_form_term_submit($form, &$form_state) {
if ($form_state['triggering_element']['#value'] == t('Delete')) {
// Execute the term deletion.
if ($form_state['values']['delete'] === TRUE) {
return taxonomy_term_confirm_delete_submit($form, $form_state);
}
// Rebuild the form to confirm term deletion.
$form_state['rebuild'] = TRUE;
$form_state['confirm_delete'] = TRUE;
return;
}
$term = taxonomy_form_term_submit_build_taxonomy_term($form, $form_state);
$status = taxonomy_term_save($term);
switch ($status) {
case SAVED_NEW:
drupal_set_message(t('Created new term %term.', array('%term' => $term->name)));
watchdog('taxonomy', 'Created new term %term.', array('%term' => $term->name), WATCHDOG_NOTICE, l(t('edit'), 'taxonomy/term/' . $term->tid . '/edit'));
break;
case SAVED_UPDATED:
drupal_set_message(t('Updated term %term.', array('%term' => $term->name)));
watchdog('taxonomy', 'Updated term %term.', array('%term' => $term->name), WATCHDOG_NOTICE, l(t('edit'), 'taxonomy/term/' . $term->tid . '/edit'));
// Clear the page and block caches to avoid stale data.
cache_clear_all();
break;
}
$current_parent_count = count($form_state['values']['parent']);
$previous_parent_count = count($form['#term']['parent']);
// Root doesn't count if it's the only parent.
if ($current_parent_count == 1 && isset($form_state['values']['parent'][0])) {
$current_parent_count = 0;
$form_state['values']['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.
if ($current_parent_count < $previous_parent_count && $current_parent_count < 2) {
taxonomy_check_vocabulary_hierarchy($form['#vocabulary'], $form_state['values']);
}
// 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 && $form['#vocabulary']->hierarchy < 2) {
$form['#vocabulary']->hierarchy = $current_parent_count == 1 ? 1 : 2;
taxonomy_vocabulary_save($form['#vocabulary']);
}
$form_state['values']['tid'] = $term->tid;
$form_state['tid'] = $term->tid;
}
/**
* Updates the form state's term entity by processing this submission's values.
*/
function taxonomy_form_term_submit_build_taxonomy_term($form, &$form_state) {
$term = $form_state['term'];
entity_form_submit_build_entity('taxonomy_term', $term, $form, $form_state);
// Convert text_format field into values expected by taxonomy_term_save().
$description = $form_state['values']['description'];
$term->description = $description['value'];
$term->format = $description['format'];
return $term;
}
/**
* Form builder for the term delete form.
*
* @ingroup forms
* @see taxonomy_term_confirm_delete_submit()
*/
function taxonomy_term_confirm_delete($form, &$form_state, $tid) {
$term = taxonomy_term_load($tid);
// Always provide entity id in the same form key as in the entity edit form.
$form['tid'] = array('#type' => 'value', '#value' => $tid);
$form['#term'] = $term;
$form['type'] = array('#type' => 'value', '#value' => 'term');
$form['name'] = array('#type' => 'value', '#value' => $term->name);
$form['vocabulary_machine_name'] = array('#type' => 'value', '#value' => $term->vocabulary_machine_name);
$form['delete'] = array('#type' => 'value', '#value' => TRUE);
return confirm_form($form,
t('Are you sure you want to delete the term %title?',
array('%title' => $term->name)),
'admin/structure/taxonomy',
t('Deleting a term will delete all its children if there are any. This action cannot be undone.'),
t('Delete'),
t('Cancel'));
}
/**
* Submit handler to delete a term after confirmation.
*
* @see taxonomy_term_confirm_delete()
*/
function taxonomy_term_confirm_delete_submit($form, &$form_state) {
taxonomy_term_delete($form_state['values']['tid']);
taxonomy_check_vocabulary_hierarchy($form['#vocabulary'], $form_state['values']);
drupal_set_message(t('Deleted term %name.', array('%name' => $form_state['values']['name'])));
watchdog('taxonomy', 'Deleted term %name.', array('%name' => $form_state['values']['name']), WATCHDOG_NOTICE);
$form_state['redirect'] = 'admin/structure/taxonomy';
cache_clear_all();
return;
}
/**
* Form builder for the vocabulary delete confirmation form.
*
* @ingroup forms
* @see taxonomy_vocabulary_confirm_delete_submit()
*/
function taxonomy_vocabulary_confirm_delete($form, &$form_state, $vid) {
$vocabulary = taxonomy_vocabulary_load($vid);
// Always provide entity id in the same form key as in the entity edit form.
$form['vid'] = array('#type' => 'value', '#value' => $vid);
$form['#vocabulary'] = $vocabulary;
$form['#id'] = 'taxonomy_vocabulary_confirm_delete';
$form['type'] = array('#type' => 'value', '#value' => 'vocabulary');
$form['name'] = array('#type' => 'value', '#value' => $vocabulary->name);
$form['#submit'] = array('taxonomy_vocabulary_confirm_delete_submit');
return confirm_form($form,
t('Are you sure you want to delete the vocabulary %title?',
array('%title' => $vocabulary->name)),
'admin/structure/taxonomy',
t('Deleting a vocabulary will delete all the terms in it. This action cannot be undone.'),
t('Delete'),
t('Cancel'));
}
/**
* Submit handler to delete a vocabulary after confirmation.
*
* @see taxonomy_vocabulary_confirm_delete()
*/
function taxonomy_vocabulary_confirm_delete_submit($form, &$form_state) {
$status = taxonomy_vocabulary_delete($form_state['values']['vid']);
drupal_set_message(t('Deleted vocabulary %name.', array('%name' => $form_state['values']['name'])));
watchdog('taxonomy', 'Deleted vocabulary %name.', array('%name' => $form_state['values']['name']), WATCHDOG_NOTICE);
$form_state['redirect'] = 'admin/structure/taxonomy';
cache_clear_all();
return;
}
/**
* Form builder to confirm resetting a vocabulary to alphabetical order.
*
* @ingroup forms
* @see taxonomy_vocabulary_confirm_reset_alphabetical_submit()
*/
function taxonomy_vocabulary_confirm_reset_alphabetical($form, &$form_state, $vid) {
$vocabulary = taxonomy_vocabulary_load($vid);
$form['type'] = array('#type' => 'value', '#value' => 'vocabulary');
$form['vid'] = array('#type' => 'value', '#value' => $vid);
$form['machine_name'] = array('#type' => 'value', '#value' => $vocabulary->machine_name);
$form['name'] = array('#type' => 'value', '#value' => $vocabulary->name);
$form['reset_alphabetical'] = array('#type' => 'value', '#value' => TRUE);
return confirm_form($form,
t('Are you sure you want to reset the vocabulary %title to alphabetical order?',
array('%title' => $vocabulary->name)),
'admin/structure/taxonomy/' . $vocabulary->machine_name,
t('Resetting a vocabulary will discard all custom ordering and sort items alphabetically.'),
t('Reset to alphabetical'),
t('Cancel'));
}
/**
* Submit handler to reset a vocabulary to alphabetical order after confirmation.
*
* @see taxonomy_vocabulary_confirm_reset_alphabetical()
*/
function taxonomy_vocabulary_confirm_reset_alphabetical_submit($form, &$form_state) {
db_update('taxonomy_term_data')
->fields(array('weight' => 0))
->condition('vid', $form_state['values']['vid'])
->execute();
drupal_set_message(t('Reset vocabulary %name to alphabetical order.', array('%name' => $form_state['values']['name'])));
watchdog('taxonomy', 'Reset vocabulary %name to alphabetical order.', array('%name' => $form_state['values']['name']), WATCHDOG_NOTICE);
$form_state['redirect'] = 'admin/structure/taxonomy/' . $form_state['values']['machine_name'];
}

View file

@ -0,0 +1,231 @@
<?php
/**
* @file
* Hooks provided by the Taxonomy module.
*/
/**
* @addtogroup hooks
* @{
*/
/**
* Act on taxonomy vocabularies when loaded.
*
* Modules implementing this hook can act on the vocabulary objects before they
* are returned by taxonomy_vocabulary_load_multiple().
*
* @param $vocabulary
* An array of taxonomy vocabulary objects.
*/
function hook_taxonomy_vocabulary_load($vocabularies) {
$result = db_select('mytable', 'm')
->fields('m', array('vid', 'foo'))
->condition('m.vid', array_keys($vocabularies), 'IN')
->execute();
foreach ($result as $record) {
$vocabularies[$record->vid]->foo = $record->foo;
}
}
/**
* Act on taxonomy vocabularies before they are saved.
*
* Modules implementing this hook can act on the vocabulary object before it is
* inserted or updated.
*
* @param $vocabulary
* A taxonomy vocabulary object.
*/
function hook_taxonomy_vocabulary_presave($vocabulary) {
$vocabulary->foo = 'bar';
}
/**
* Act on taxonomy vocabularies when inserted.
*
* Modules implementing this hook can act on the vocabulary object when saved
* to the database.
*
* @param $vocabulary
* A taxonomy vocabulary object.
*/
function hook_taxonomy_vocabulary_insert($vocabulary) {
if ($vocabulary->machine_name == 'my_vocabulary') {
$vocabulary->weight = 100;
}
}
/**
* Act on taxonomy vocabularies when updated.
*
* Modules implementing this hook can act on the vocabulary object when updated.
*
* @param $vocabulary
* A taxonomy vocabulary object.
*/
function hook_taxonomy_vocabulary_update($vocabulary) {
db_update('mytable')
->fields(array('foo' => $vocabulary->foo))
->condition('vid', $vocabulary->vid)
->execute();
}
/**
* Respond to the deletion of taxonomy vocabularies.
*
* Modules implementing this hook can respond to the deletion of taxonomy
* vocabularies from the database.
*
* @param $vocabulary
* A taxonomy vocabulary object.
*/
function hook_taxonomy_vocabulary_delete($vocabulary) {
db_delete('mytable')
->condition('vid', $vocabulary->vid)
->execute();
}
/**
* Act on taxonomy terms when loaded.
*
* Modules implementing this hook can act on the term objects returned by
* taxonomy_term_load_multiple().
*
* For performance reasons, information to be added to term objects should be
* loaded in a single query for all terms where possible.
*
* Since terms are stored and retrieved from cache during a page request, avoid
* altering properties provided by the {taxonomy_term_data} table, since this
* may affect the way results are loaded from cache in subsequent calls.
*
* @param $terms
* An array of term objects, indexed by tid.
*/
function hook_taxonomy_term_load($terms) {
$result = db_select('mytable', 'm')
->fields('m', array('tid', 'foo'))
->condition('m.tid', array_keys($terms), 'IN')
->execute();
foreach ($result as $record) {
$terms[$record->tid]->foo = $record->foo;
}
}
/**
* Act on taxonomy terms before they are saved.
*
* Modules implementing this hook can act on the term object before it is
* inserted or updated.
*
* @param $term
* A term object.
*/
function hook_taxonomy_term_presave($term) {
$term->foo = 'bar';
}
/**
* Act on taxonomy terms when inserted.
*
* Modules implementing this hook can act on the term object when saved to
* the database.
*
* @param $term
* A taxonomy term object.
*/
function hook_taxonomy_term_insert($term) {
db_insert('mytable')
->fields(array(
'tid' => $term->tid,
'foo' => $term->foo,
))
->execute();
}
/**
* Act on taxonomy terms when updated.
*
* Modules implementing this hook can act on the term object when updated.
*
* @param $term
* A taxonomy term object.
*/
function hook_taxonomy_term_update($term) {
db_update('mytable')
->fields(array('foo' => $term->foo))
->condition('tid', $term->tid)
->execute();
}
/**
* Respond to the deletion of taxonomy terms.
*
* Modules implementing this hook can respond to the deletion of taxonomy
* terms from the database.
*
* @param $term
* A taxonomy term object.
*/
function hook_taxonomy_term_delete($term) {
db_delete('mytable')
->condition('tid', $term->tid)
->execute();
}
/**
* Act on a taxonomy term that is being assembled before rendering.
*
* The module may add elements to $term->content prior to rendering. The
* structure of $term->content is a renderable array as expected by
* drupal_render().
*
* @param $term
* The term that is being assembled for rendering.
* @param $view_mode
* The $view_mode parameter from taxonomy_term_view().
* @param $langcode
* The language code used for rendering.
*
* @see hook_entity_view()
*/
function hook_taxonomy_term_view($term, $view_mode, $langcode) {
$term->content['my_additional_field'] = array(
'#markup' => $additional_field,
'#weight' => 10,
'#theme' => 'mymodule_my_additional_field',
);
}
/**
* Alter the results of taxonomy_term_view().
*
* This hook is called after the content has been assembled in a structured
* array and may be used for doing processing which requires that the complete
* taxonomy term content structure has been built.
*
* If the module wishes to act on the rendered HTML of the term rather than the
* structured content array, it may use this hook to add a #post_render
* callback. Alternatively, it could also implement
* hook_preprocess_taxonomy_term(). See drupal_render() and theme()
* documentation respectively for details.
*
* @param $build
* A renderable array representing the term.
*
* @see hook_entity_view_alter()
*/
function hook_taxonomy_term_view_alter(&$build) {
if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) {
// Change its weight.
$build['an_additional_field']['#weight'] = -10;
}
// Add a #post_render callback to act on the rendered HTML of the term.
$build['#post_render'][] = 'my_module_node_post_render';
}
/**
* @} End of "addtogroup hooks".
*/

View file

@ -0,0 +1,13 @@
tr.taxonomy-term-preview {
background-color: #EEE;
}
tr.taxonomy-term-divider-top {
border-bottom: none;
}
tr.taxonomy-term-divider-bottom {
border-top: 1px dotted #CCC;
}
.taxonomy-term-description {
margin: 5px 0 20px;
}

View file

@ -0,0 +1,15 @@
name = Taxonomy
description = Enables the categorization of content.
package = Core
version = VERSION
core = 7.x
dependencies[] = options
files[] = taxonomy.module
files[] = taxonomy.test
configure = admin/structure/taxonomy
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,939 @@
<?php
/**
* @file
* Install, update and uninstall functions for the taxonomy module.
*/
/**
* Implements hook_uninstall().
*/
function taxonomy_uninstall() {
// Remove variables.
variable_del('taxonomy_override_selector');
variable_del('taxonomy_terms_per_page_admin');
// Remove taxonomy_term bundles.
$vocabularies = db_query("SELECT machine_name FROM {taxonomy_vocabulary}")->fetchCol();
foreach ($vocabularies as $vocabulary) {
field_attach_delete_bundle('taxonomy_term', $vocabulary);
}
}
/**
* Implements hook_schema().
*/
function taxonomy_schema() {
$schema['taxonomy_term_data'] = array(
'description' => 'Stores term information.',
'fields' => array(
'tid' => array(
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'Primary Key: Unique term ID.',
),
'vid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'The {taxonomy_vocabulary}.vid of the vocabulary to which the term is assigned.',
),
'name' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => 'The term name.',
'translatable' => TRUE,
),
'description' => array(
'type' => 'text',
'not null' => FALSE,
'size' => 'big',
'description' => 'A description of the term.',
'translatable' => TRUE,
),
'format' => array(
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
'description' => 'The {filter_format}.format of the description.',
),
'weight' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'The weight of this term in relation to other terms.',
),
),
'primary key' => array('tid'),
'foreign keys' => array(
'vocabulary' => array(
'table' => 'taxonomy_vocabulary',
'columns' => array('vid' => 'vid'),
),
),
'indexes' => array(
'taxonomy_tree' => array('vid', 'weight', 'name'),
'vid_name' => array('vid', 'name'),
'name' => array('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_vocabulary'] = array(
'description' => 'Stores vocabulary information.',
'fields' => array(
'vid' => array(
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'Primary Key: Unique vocabulary ID.',
),
'name' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => 'Name of the vocabulary.',
'translatable' => TRUE,
),
'machine_name' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => 'The vocabulary machine name.',
),
'description' => array(
'type' => 'text',
'not null' => FALSE,
'size' => 'big',
'description' => 'Description of the vocabulary.',
'translatable' => TRUE,
),
'hierarchy' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
'description' => 'The type of hierarchy allowed within the vocabulary. (0 = disabled, 1 = single, 2 = multiple)',
),
'module' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => 'The module which created the vocabulary.',
),
'weight' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'The weight of this vocabulary in relation to other vocabularies.',
),
),
'primary key' => array('vid'),
'indexes' => array(
'list' => array('weight', 'name'),
),
'unique keys' => array(
'machine_name' => array('machine_name'),
),
);
$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,
),
'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,
),
),
'indexes' => array(
'term_node' => array('tid', 'sticky', 'created'),
'nid' => array('nid'),
),
'foreign keys' => array(
'tracked_node' => array(
'table' => 'node',
'columns' => array('nid' => 'nid'),
),
'term' => array(
'table' => 'taxonomy_term_data',
'columns' => array('tid' => 'tid'),
),
),
);
return $schema;
}
/**
* Implements hook_field_schema().
*/
function taxonomy_field_schema($field) {
return array(
'columns' => array(
'tid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => FALSE,
),
),
'indexes' => array(
'tid' => array('tid'),
),
'foreign keys' => array(
'tid' => array(
'table' => 'taxonomy_term_data',
'columns' => array('tid' => 'tid'),
),
),
);
}
/**
* Implements hook_update_dependencies().
*/
function taxonomy_update_dependencies() {
// taxonomy_update_7004() migrates taxonomy term data to fields and therefore
// must run after all Field modules have been enabled, which happens in
// system_update_7027().
$dependencies['taxonomy'][7004] = array(
'system' => 7027,
);
return $dependencies;
}
/**
* Utility function: get the list of vocabularies directly from the database.
*
* This function is valid for a database schema version 7002.
*
* @ingroup update_api
*/
function _update_7002_taxonomy_get_vocabularies() {
return db_query('SELECT v.* FROM {taxonomy_vocabulary} v ORDER BY v.weight, v.name')->fetchAllAssoc('vid', PDO::FETCH_OBJ);
}
/**
* Rename taxonomy tables.
*/
function taxonomy_update_7001() {
db_rename_table('term_data', 'taxonomy_term_data');
db_rename_table('term_hierarchy', 'taxonomy_term_hierarchy');
db_rename_table('term_node', 'taxonomy_term_node');
db_rename_table('term_relation', 'taxonomy_term_relation');
db_rename_table('term_synonym', 'taxonomy_term_synonym');
db_rename_table('vocabulary', 'taxonomy_vocabulary');
db_rename_table('vocabulary_node_types', 'taxonomy_vocabulary_node_type');
}
/**
* Add {vocabulary}.machine_name column.
*/
function taxonomy_update_7002() {
$field = array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => 'The vocabulary machine name.',
);
db_add_field('taxonomy_vocabulary', 'machine_name', $field);
// Do a direct query here, rather than calling taxonomy_get_vocabularies(),
// in case Taxonomy module is disabled.
$vids = db_query('SELECT vid FROM {taxonomy_vocabulary}')->fetchCol();
foreach ($vids as $vid) {
$machine_name = 'vocabulary_' . $vid;
db_update('taxonomy_vocabulary')
->fields(array('machine_name' => $machine_name))
->condition('vid', $vid)
->execute();
}
// The machine_name unique key can only be added after we ensure the
// machine_name column contains unique values.
db_add_unique_key('taxonomy_vocabulary', 'machine_name', array('machine_name'));
}
/**
* Remove the related terms setting from vocabularies.
*
* This setting has not been used since Drupal 6. The {taxonomy_relations} table
* itself is retained to allow for data to be upgraded.
*/
function taxonomy_update_7003() {
db_drop_field('taxonomy_vocabulary', 'relations');
}
/**
* Move taxonomy vocabulary associations for nodes to fields and field instances.
*/
function taxonomy_update_7004() {
$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,
),
'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',
'unsigned' => TRUE,
'not null' => TRUE,
'default'=> 0,
),
),
'indexes' => array(
'term_node' => array('tid', 'sticky', 'created'),
'nid' => array('nid'),
),
'foreign keys' => array(
'tracked_node' => array(
'table' => 'node',
'columns' => array('nid' => 'nid'),
),
'term' => array(
'table' => 'taxonomy_term_data',
'columns' => array('tid' => 'tid'),
),
),
);
db_create_table('taxonomy_index', $taxonomy_index);
// Use an inline version of Drupal 6 taxonomy_get_vocabularies() here since
// we can no longer rely on $vocabulary->nodes from the API function.
$result = db_query('SELECT v.*, n.type FROM {taxonomy_vocabulary} v LEFT JOIN {taxonomy_vocabulary_node_type} n ON v.vid = n.vid ORDER BY v.weight, v.name');
$vocabularies = array();
foreach ($result as $record) {
// If no node types are associated with a vocabulary, the LEFT JOIN will
// return a NULL value for type.
if (isset($record->type)) {
$node_types[$record->vid][$record->type] = $record->type;
unset($record->type);
$record->nodes = $node_types[$record->vid];
}
elseif (!isset($record->nodes)) {
$record->nodes = array();
}
$vocabularies[$record->vid] = $record;
}
foreach ($vocabularies as $vocabulary) {
$field_name = 'taxonomy_' . $vocabulary->machine_name;
$field = array(
'field_name' => $field_name,
'module' => 'taxonomy',
'type' => 'taxonomy_term_reference',
'cardinality' => $vocabulary->multiple || $vocabulary->tags ? FIELD_CARDINALITY_UNLIMITED : 1,
'settings' => array(
'required' => $vocabulary->required ? TRUE : FALSE,
'allowed_values' => array(
array(
'vocabulary' => $vocabulary->machine_name,
'parent' => 0,
),
),
),
);
_update_7000_field_create_field($field);
foreach ($vocabulary->nodes as $bundle) {
$instance = array(
'label' => $vocabulary->name,
'field_name' => $field_name,
'bundle' => $bundle,
'entity_type' => 'node',
'settings' => array(),
'description' => $vocabulary->help,
'required' => $vocabulary->required,
'widget' => array(),
'display' => array(
'default' => array(
'type' => 'taxonomy_term_reference_link',
'weight' => 10,
),
'teaser' => array(
'type' => 'taxonomy_term_reference_link',
'weight' => 10,
),
),
);
if ($vocabulary->tags) {
$instance['widget'] = array(
'type' => 'taxonomy_autocomplete',
'module' => 'taxonomy',
'settings' => array(
'size' => 60,
'autocomplete_path' => 'taxonomy/autocomplete',
),
);
}
else {
$instance['widget'] = array(
'type' => 'select',
'module' => 'options',
'settings' => array(),
);
}
_update_7000_field_create_instance($field, $instance);
}
}
// Some contrib projects stored term node associations without regard for the
// selections in the taxonomy_vocabulary_node_types table, or have more terms
// for a single node than the vocabulary allowed. We construct the
// taxonomyextra field to store all the extra stuff.
// Allowed values for this extra vocabs field is every vocabulary.
$allowed_values = array();
foreach (_update_7002_taxonomy_get_vocabularies() as $vocabulary) {
$allowed_values[] = array(
'vocabulary' => $vocabulary->machine_name,
'parent' => 0,
);
}
$field_name = 'taxonomyextra';
$field = array(
'field_name' => $field_name,
'module' => 'taxonomy',
'type' => 'taxonomy_term_reference',
'cardinality' => FIELD_CARDINALITY_UNLIMITED,
'settings' => array(
'required' => FALSE,
'allowed_values' => $allowed_values,
),
);
_update_7000_field_create_field($field);
foreach (_update_7000_node_get_types() as $bundle) {
$instance = array(
'label' => 'Taxonomy upgrade extras',
'field_name' => $field_name,
'entity_type' => 'node',
'bundle' => $bundle->type,
'settings' => array(),
'description' => 'Debris left over after upgrade from Drupal 6',
'required' => FALSE,
'widget' => array(
'type' => 'taxonomy_autocomplete',
'module' => 'taxonomy',
'settings' => array(),
),
'display' => array(
'default' => array(
'type' => 'taxonomy_term_reference_link',
'weight' => 10,
),
'teaser' => array(
'type' => 'taxonomy_term_reference_link',
'weight' => 10,
),
),
);
_update_7000_field_create_instance($field, $instance);
}
$fields = array('help', 'multiple', 'required', 'tags');
foreach ($fields as $field) {
db_drop_field('taxonomy_vocabulary', $field);
}
}
/**
* Migrate {taxonomy_term_node} table to field storage.
*
* @todo: This function can possibly be made much faster by wrapping a
* transaction around all the inserts.
*/
function taxonomy_update_7005(&$sandbox) {
// $sandbox contents:
// - total: The total number of term_node relationships to migrate.
// - count: The number of term_node relationships that have been
// migrated so far.
// - last: The db_query_range() offset to use when querying
// term_node; this field is incremented in quantities of $batch
// (1000) but at the end of each call to this function, last and
// count are the same.
// - vocabularies: An associative array mapping vocabulary id and node
// type to field name. If a voc id/node type pair does not appear
// in this array but a term_node relationship exists mapping a
// term in voc id to node of that type, the relationship is
// assigned to the taxonomymyextra field which allows terms of all
// vocabularies.
// - cursor[values], cursor[deltas]: The contents of $values and
// $deltas at the end of the previous call to this function. These
// need to be preserved across calls because a single batch of
// 1000 rows from term_node may end in the middle of the terms for
// a single node revision.
//
// $values is the array of values about to be/most recently inserted
// into the SQL data table for the taxonomy_term_reference
// field. Before $values is constructed for each record, the
// $values from the previous insert is checked to see if the two
// records are for the same node revision id; this enables knowing
// when to reset the delta counters which are incremented across all
// terms for a single field on a single revision, but reset for each
// new field and revision.
//
// $deltas is an associative array mapping field name to the number
// of term references stored so far for the current revision, which
// provides the delta value for each term reference data insert. The
// deltas are reset for each new revision.
$conditions = array(
'type' => 'taxonomy_term_reference',
'deleted' => 0,
);
$field_info = _update_7000_field_read_fields($conditions, 'field_name');
// This is a multi-pass update. On the first call we need to initialize some
// variables.
if (!isset($sandbox['total'])) {
$sandbox['last'] = 0;
$sandbox['count'] = 0;
// Run the same joins as the query that is used later to retrieve the
// term_node data, this ensures that bad records in that table - for
// tids which aren't in taxonomy_term_data or nids which aren't in {node}
// are not included in the count.
$sandbox['total'] = db_query('SELECT COUNT(*) FROM {taxonomy_term_data} td INNER JOIN {taxonomy_term_node} tn ON td.tid = tn.tid INNER JOIN {node} n ON tn.nid = n.nid LEFT JOIN {node} n2 ON tn.vid = n2.vid')->fetchField();
// Use an inline version of Drupal 6 taxonomy_get_vocabularies() here since
// we can no longer rely on $vocabulary->nodes from the API function.
$result = db_query('SELECT v.vid, v.machine_name, n.type FROM {taxonomy_vocabulary} v INNER JOIN {taxonomy_vocabulary_node_type} n ON v.vid = n.vid');
$vocabularies = array();
foreach ($result as $record) {
// If no node types are associated with a vocabulary, the LEFT JOIN will
// return a NULL value for type.
if (isset($record->type)) {
$vocabularies[$record->vid][$record->type] = 'taxonomy_'. $record->machine_name;
}
}
if (!empty($vocabularies)) {
$sandbox['vocabularies'] = $vocabularies;
}
db_create_table('taxonomy_update_7005', array(
'description' => 'Stores temporary data for taxonomy_update_7005.',
'fields' => array(
'n' => array(
'description' => 'Preserve order.',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'vocab_id' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'tid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
'nid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
'vid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => FALSE,
'default' => NULL,
),
'type' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'default' => '',
),
'created' => array(
'type' => 'int',
'not null' => FALSE,
),
'sticky' => array(
'type' => 'int',
'not null' => FALSE,
),
'status' => array(
'type' => 'int',
'not null' => FALSE,
),
'is_current' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => FALSE,
),
),
'primary key' => array('n'),
));
// Query selects all revisions at once and processes them in revision and
// term weight order.
$query = db_select('taxonomy_term_data', 'td');
// We are migrating term-node relationships. If there are none for a
// term, we do not need the term_data row.
$query->join('taxonomy_term_node', 'tn', 'td.tid = tn.tid');
// If a term-node relationship exists for a nid that does not exist, we
// cannot migrate it as we have no node to relate it to; thus we do not
// need that row from term_node.
$query->join('node', 'n', 'tn.nid = n.nid');
// If the current term-node relationship is for the current revision of
// the node, this left join will match and is_current will be non-NULL
// (we also get the current sticky and created in this case). This
// tells us whether to insert into the current data tables in addition
// to the revision data tables.
$query->leftJoin('node', 'n2', 'tn.vid = n2.vid');
$query->addField('td', 'vid', 'vocab_id');
$query->addField('td', 'tid');
$query->addField('tn', 'nid');
$query->addField('tn', 'vid');
$query->addField('n', 'type');
$query->addField('n2', 'created');
$query->addField('n2', 'sticky');
$query->addField('n2', 'status');
$query->addField('n2', 'nid', 'is_current');
// This query must return a consistent ordering across multiple calls.
// We need them ordered by node vid (since we use that to decide when
// to reset the delta counters) and by term weight so they appear
// within each node in weight order. However, tn.vid,td.weight is not
// guaranteed to be unique, so we add tn.tid as an additional sort key
// because tn.tid,tn.vid is the primary key of the D6 term_node table
// and so is guaranteed unique. Unfortunately it also happens to be in
// the wrong order which is less efficient, but c'est la vie.
$query->orderBy('tn.vid');
$query->orderBy('td.weight');
$query->orderBy('tn.tid');
// Work around a bug in the PostgreSQL driver that would result in fatal
// errors when this subquery is used in the insert query below. See
// https://drupal.org/node/2057693.
$fields = &$query->getFields();
unset($fields['td.weight']);
unset($fields['tn.tid']);
db_insert('taxonomy_update_7005')
->from($query)
->execute();
}
else {
// We do each pass in batches of 1000.
$batch = 1000;
$result = db_query_range('SELECT vocab_id, tid, nid, vid, type, created, sticky, status, is_current FROM {taxonomy_update_7005} ORDER BY n', $sandbox['last'], $batch);
if (isset($sandbox['cursor'])) {
$values = $sandbox['cursor']['values'];
$deltas = $sandbox['cursor']['deltas'];
}
else {
$deltas = array();
}
foreach ($result as $record) {
$sandbox['count'] += 1;
// Use the valid field for this vocabulary and node type or use the
// overflow vocabulary if there is no valid field.
$field_name = isset($sandbox['vocabularies'][$record->vocab_id][$record->type]) ? $sandbox['vocabularies'][$record->vocab_id][$record->type] : 'taxonomyextra';
$field = $field_info[$field_name];
// Start deltas from 0, and increment by one for each term attached to a
// node.
if (!isset($deltas[$field_name])) {
$deltas[$field_name] = 0;
}
if (isset($values)) {
// If the last inserted revision_id is the same as the current record,
// use the previous deltas to calculate the next delta.
if ($record->vid == $values[2]) {
// For limited cardinality fields, the delta must not be allowed to
// exceed the cardinality during the update. So ensure that the
// delta about to be inserted is within this limit.
// @see field_default_validate().
if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ($deltas[$field_name] + 1) > $field['cardinality']) {
// For excess values of a single-term vocabulary, switch over to
// the overflow field.
$field_name = 'taxonomyextra';
$field = $field_info[$field_name];
if (!isset($deltas[$field_name])) {
$deltas[$field_name] = 0;
}
}
}
else {
// When the record is a new revision, empty the deltas array.
$deltas = array($field_name => 0);
}
}
// Table and column found in the field's storage details. During upgrades,
// it's always SQL.
$table_name = "field_data_{$field_name}";
$revision_name = "field_revision_{$field_name}";
$value_column = $field_name . '_tid';
// Column names and values in field storage are the same for current and
// revision.
$columns = array('entity_type', 'entity_id', 'revision_id', 'bundle', 'language', 'delta', $value_column);
$values = array('node', $record->nid, $record->vid, $record->type, LANGUAGE_NONE, $deltas[$field_name]++, $record->tid);
// Insert rows into the revision table.
db_insert($revision_name)->fields($columns)->values($values)->execute();
// is_current column is a node ID if this revision is also current.
if ($record->is_current) {
db_insert($table_name)->fields($columns)->values($values)->execute();
// Only insert a record in the taxonomy index if the node is published.
if ($record->status) {
// Update the {taxonomy_index} table.
db_insert('taxonomy_index')
->fields(array('nid', 'tid', 'sticky', 'created',))
->values(array($record->nid, $record->tid, $record->sticky, $record->created))
->execute();
}
}
}
// Store the set of inserted values and the current revision's deltas in the
// sandbox.
$sandbox['cursor'] = array(
'values' => $values,
'deltas' => $deltas,
);
$sandbox['last'] += $batch;
}
if ($sandbox['count'] < $sandbox['total']) {
$sandbox['#finished'] = FALSE;
}
else {
db_drop_table('taxonomy_vocabulary_node_type');
db_drop_table('taxonomy_term_node');
// If there are no vocabs, we're done.
db_drop_table('taxonomy_update_7005');
$sandbox['#finished'] = TRUE;
// Determine necessity of taxonomyextras field.
$field = $field_info['taxonomyextra'];
$revision_name = 'field_revision_' . $field['field_name'];
$node_types = db_select($revision_name)->distinct()->fields($revision_name, array('bundle'))
->execute()->fetchCol();
if (empty($node_types)) {
// Delete the overflow field if there are no rows in the revision table.
_update_7000_field_delete_field('taxonomyextra');
}
else {
// Remove instances which are not actually used.
$bundles = db_query('SELECT bundle FROM {field_config_instance} WHERE field_name = :field_name', array(':field_name' => 'taxonomyextra'))->fetchCol();
$bundles = array_diff($bundles, $node_types);
foreach ($bundles as $bundle) {
_update_7000_field_delete_instance('taxonomyextra', 'node', $bundle);
}
}
}
}
/**
* Add {taxonomy_term_data}.format column.
*/
function taxonomy_update_7006() {
db_add_field('taxonomy_term_data', 'format', array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => FALSE,
'description' => 'The {filter_format}.format of the description.',
));
}
/**
* Add index on {taxonomy_term_data}.name column to speed up taxonomy_get_term_by_name().
*/
function taxonomy_update_7007() {
db_add_index('taxonomy_term_data', 'name', array('name'));
}
/**
* Change the weight columns to normal int.
*/
function taxonomy_update_7008() {
db_drop_index('taxonomy_term_data', 'taxonomy_tree');
db_change_field('taxonomy_term_data', 'weight', 'weight', array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'The weight of this term in relation to other terms.',
), array(
'indexes' => array(
'taxonomy_tree' => array('vid', 'weight', 'name'),
),
));
db_drop_index('taxonomy_vocabulary', 'list');
db_change_field('taxonomy_vocabulary', 'weight', 'weight', array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'The weight of this vocabulary in relation to other vocabularies.',
), array(
'indexes' => array(
'list' => array('weight', 'name'),
),
));
}
/**
* Change {taxonomy_term_data}.format into varchar.
*/
function taxonomy_update_7009() {
db_change_field('taxonomy_term_data', 'format', 'format', array(
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
'description' => 'The {filter_format}.format of the description.',
));
}
/**
* Change {taxonomy_index}.created to support signed int.
*/
function taxonomy_update_7010() {
db_change_field('taxonomy_index', 'created', 'created', array(
'description' => 'The Unix timestamp when the node was created.',
'type' => 'int',
'unsigned' => FALSE,
'not null' => TRUE,
'default'=> 0,
));
}
/**
* @addtogroup updates-7.x-extra
* @{
*/
/**
* Drop unpublished nodes from the index.
*/
function taxonomy_update_7011(&$sandbox) {
// Initialize information needed by the batch update system.
if (!isset($sandbox['progress'])) {
$sandbox['progress'] = 0;
$sandbox['max'] = db_query('SELECT COUNT(DISTINCT n.nid) FROM {node} n INNER JOIN {taxonomy_index} t ON n.nid = t.nid WHERE n.status = :status', array(':status' => NODE_NOT_PUBLISHED))->fetchField();
// If there's no data, don't bother with the extra work.
if (empty($sandbox['max'])) {
return;
}
}
// Process records in groups of 5000.
$limit = 5000;
$nids = db_query_range('SELECT DISTINCT n.nid FROM {node} n INNER JOIN {taxonomy_index} t ON n.nid = t.nid WHERE n.status = :status', 0, $limit, array(':status' => NODE_NOT_PUBLISHED))->fetchCol();
if (!empty($nids)) {
db_delete('taxonomy_index')
->condition('nid', $nids)
->execute();
}
// Update our progress information for the batch update.
$sandbox['progress'] += $limit;
// Indicate our current progress to the batch update system, if the update is
// not yet complete.
if ($sandbox['progress'] < $sandbox['max']) {
$sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
}
}
/**
* @} End of "addtogroup updates-7.x-extra".
*/

View file

@ -0,0 +1,40 @@
(function ($) {
/**
* 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.
*/
Drupal.behaviors.termDrag = {
attach: function (context, settings) {
var table = $('#taxonomy', context);
var tableDrag = Drupal.tableDrag.taxonomy; // Get the blocks tableDrag object.
var rows = $('tr', table).length;
// When a row is swapped, keep previous and next page classes set.
tableDrag.row.prototype.onSwap = function (swappedRow) {
$('tr.taxonomy-term-preview', table).removeClass('taxonomy-term-preview');
$('tr.taxonomy-term-divider-top', table).removeClass('taxonomy-term-divider-top');
$('tr.taxonomy-term-divider-bottom', table).removeClass('taxonomy-term-divider-bottom');
if (settings.taxonomy.backStep) {
for (var n = 0; n < settings.taxonomy.backStep; n++) {
$(table[0].tBodies[0].rows[n]).addClass('taxonomy-term-preview');
}
$(table[0].tBodies[0].rows[settings.taxonomy.backStep - 1]).addClass('taxonomy-term-divider-top');
$(table[0].tBodies[0].rows[settings.taxonomy.backStep]).addClass('taxonomy-term-divider-bottom');
}
if (settings.taxonomy.forwardStep) {
for (var n = rows - settings.taxonomy.forwardStep - 1; n < rows - 1; n++) {
$(table[0].tBodies[0].rows[n]).addClass('taxonomy-term-preview');
}
$(table[0].tBodies[0].rows[rows - settings.taxonomy.forwardStep - 2]).addClass('taxonomy-term-divider-top');
$(table[0].tBodies[0].rows[rows - settings.taxonomy.forwardStep - 1]).addClass('taxonomy-term-divider-bottom');
}
};
}
};
})(jQuery);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,181 @@
<?php
/**
* @file
* Page callbacks for the taxonomy module.
*/
/**
* Menu callback; displays all nodes associated with a term.
*
* @param $term
* The taxonomy term.
* @return
* The page content.
*/
function taxonomy_term_page($term) {
// If there is a menu link to this term, the link becomes the last part of
// the active trail, and the link name becomes the page title. Thus, we must
// explicitly set the page title to be the term title.
drupal_set_title($term->name);
// Build breadcrumb based on the hierarchy of the term.
$current = (object) array(
'tid' => $term->tid,
);
// @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 = taxonomy_get_parents($current->tid)) {
$current = array_shift($parents);
$breadcrumb[] = l($current->name, 'taxonomy/term/' . $current->tid);
}
$breadcrumb[] = l(t('Home'), NULL);
$breadcrumb = array_reverse($breadcrumb);
drupal_set_breadcrumb($breadcrumb);
drupal_add_feed('taxonomy/term/' . $term->tid . '/feed', 'RSS - ' . $term->name);
// Set the term path as the canonical URL to prevent duplicate content.
$uri = entity_uri('taxonomy_term', $term);
drupal_add_html_head_link(array('rel' => 'canonical', 'href' => url($uri['path'], $uri['options'])), TRUE);
// Set the non-aliased path as a default shortlink.
drupal_add_html_head_link(array('rel' => 'shortlink', 'href' => url($uri['path'], array_merge($uri['options'], array('alias' => TRUE)))), TRUE);
// Normally we would call taxonomy_term_show() here, but for backwards
// compatibility in Drupal 7 we do not want to do that (it produces different
// data structures and HTML markup than what Drupal 7 released with). Calling
// taxonomy_term_view() directly provides essentially the same thing, but
// allows us to wrap the rendered term in our desired array structure.
$build['term_heading'] = array(
'#prefix' => '<div class="term-listing-heading">',
'#suffix' => '</div>',
'term' => taxonomy_term_view($term, 'full'),
);
if ($nids = taxonomy_select_nodes($term->tid, TRUE, variable_get('default_nodes_main', 10))) {
$nodes = node_load_multiple($nids);
$build += node_view_multiple($nodes);
$build['pager'] = array(
'#theme' => 'pager',
'#weight' => 5,
);
}
else {
$build['no_content'] = array(
'#prefix' => '<p>',
'#markup' => t('There is currently no content classified with this term.'),
'#suffix' => '</p>',
);
}
return $build;
}
/**
* Generate the content feed for a taxonomy term.
*
* @param $term
* The taxonomy term.
*/
function taxonomy_term_feed($term) {
$channel['link'] = url('taxonomy/term/' . $term->tid, array('absolute' => TRUE));
$channel['title'] = variable_get('site_name', 'Drupal') . ' - ' . $term->name;
// Only display the description if we have a single term, to avoid clutter and confusion.
// HTML will be removed from feed description.
$channel['description'] = check_markup($term->description, $term->format, '', TRUE);
$nids = taxonomy_select_nodes($term->tid, FALSE, variable_get('feed_default_items', 10));
node_feed($nids, $channel);
}
/**
* Page callback: Outputs JSON for taxonomy autocomplete suggestions.
*
* Path: taxonomy/autocomplete
*
* This callback outputs term name suggestions in response to Ajax requests
* made by the taxonomy autocomplete widget for taxonomy term reference
* fields. The output is a JSON object of plain-text term suggestions, keyed by
* the user-entered value with the completed term name appended. Term names
* containing commas are wrapped in quotes.
*
* For example, suppose the user has entered the string 'red fish, blue' in the
* field, and there are two taxonomy terms, 'blue fish' and 'blue moon'. The
* JSON output would have the following structure:
* @code
* {
* "red fish, blue fish": "blue fish",
* "red fish, blue moon": "blue moon",
* };
* @endcode
*
* @param $field_name
* The name of the term reference field.
* @param $tags_typed
* (optional) A comma-separated list of term names entered in the
* autocomplete form element. Only the last term is used for autocompletion.
* Defaults to '' (an empty string).
*
* @see taxonomy_menu()
* @see taxonomy_field_widget_info()
*/
function taxonomy_autocomplete($field_name = '', $tags_typed = '') {
// If the request has a '/' in the search text, then the menu system will have
// split it into multiple arguments, recover the intended $tags_typed.
$args = func_get_args();
// Shift off the $field_name argument.
array_shift($args);
$tags_typed = implode('/', $args);
// Make sure the field exists and is a taxonomy field.
if (!($field = field_info_field($field_name)) || $field['type'] !== 'taxonomy_term_reference') {
// Error string. The JavaScript handler will realize this is not JSON and
// will display it as debugging information.
print t('Taxonomy field @field_name not found.', array('@field_name' => $field_name));
exit;
}
// The user enters a comma-separated list of tags. We only autocomplete the last tag.
$tags_typed = drupal_explode_tags($tags_typed);
$tag_last = drupal_strtolower(array_pop($tags_typed));
$term_matches = array();
if ($tag_last != '') {
// Part of the criteria for the query come from the field's own settings.
$vids = array();
$vocabularies = taxonomy_vocabulary_get_names();
foreach ($field['settings']['allowed_values'] as $tree) {
$vids[] = $vocabularies[$tree['vocabulary']]->vid;
}
$query = db_select('taxonomy_term_data', 't');
$query->addTag('translatable');
$query->addTag('taxonomy_term_access');
// Do not select already entered terms.
if (!empty($tags_typed)) {
$query->condition('t.name', $tags_typed, 'NOT IN');
}
// Select rows that match by term name.
$tags_return = $query
->fields('t', array('tid', 'name'))
->condition('t.vid', $vids)
->condition('t.name', '%' . db_like($tag_last) . '%', 'LIKE')
->range(0, 10)
->execute()
->fetchAllKeyed();
$prefix = count($tags_typed) ? drupal_implode_tags($tags_typed) . ', ' : '';
foreach ($tags_return as $tid => $name) {
$n = $name;
// Term names containing commas or quotes must be wrapped in quotes.
if (strpos($name, ',') !== FALSE || strpos($name, '"') !== FALSE) {
$n = '"' . str_replace('"', '""', $name) . '"';
}
$term_matches[$prefix . $n] = check_plain($name);
}
}
drupal_json_output($term_matches);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,189 @@
<?php
/**
* @file
* Builds placeholder replacement tokens for taxonomy terms and vocabularies.
*/
/**
* 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()) {
$replacements = array();
$sanitize = !empty($options['sanitize']);
if ($type == 'term' && !empty($data['term'])) {
$term = $data['term'];
foreach ($tokens as $name => $original) {
switch ($name) {
case 'tid':
$replacements[$original] = $term->tid;
break;
case 'name':
$replacements[$original] = $sanitize ? check_plain($term->name) : $term->name;
break;
case 'description':
$replacements[$original] = $sanitize ? check_markup($term->description, $term->format, '', TRUE) : $term->description;
break;
case 'url':
$uri = entity_uri('taxonomy_term', $term);
$replacements[$original] = url($uri['path'], array_merge($uri['options'], array('absolute' => TRUE)));
break;
case 'node-count':
$query = db_select('taxonomy_index');
$query->condition('tid', $term->tid);
$query->addTag('term_node_count');
$count = $query->countQuery()->execute()->fetchField();
$replacements[$original] = $count;
break;
case 'vocabulary':
$vocabulary = taxonomy_vocabulary_load($term->vid);
$replacements[$original] = check_plain($vocabulary->name);
break;
case 'parent':
if ($parents = taxonomy_get_parents($term->tid)) {
$parent = array_pop($parents);
$replacements[$original] = check_plain($parent->name);
}
break;
}
}
if ($vocabulary_tokens = token_find_with_prefix($tokens, 'vocabulary')) {
$vocabulary = taxonomy_vocabulary_load($term->vid);
$replacements += token_generate('vocabulary', $vocabulary_tokens, array('vocabulary' => $vocabulary), $options);
}
if (($vocabulary_tokens = token_find_with_prefix($tokens, 'parent')) && $parents = taxonomy_get_parents($term->tid)) {
$parent = array_pop($parents);
$replacements += token_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->vid;
break;
case 'name':
$replacements[$original] = $sanitize ? check_plain($vocabulary->name) : $vocabulary->name;
break;
case 'description':
$replacements[$original] = $sanitize ? filter_xss($vocabulary->description) : $vocabulary->description;
break;
case 'term-count':
$query = db_select('taxonomy_term_data');
$query->condition('vid', $vocabulary->vid);
$query->addTag('vocabulary_term_count');
$count = $query->countQuery()->execute()->fetchField();
$replacements[$original] = $count;
break;
case 'node-count':
$query = db_select('taxonomy_index', 'ti');
$query->addExpression('COUNT(DISTINCT ti.nid)');
$query->leftJoin('taxonomy_term_data', 'td', 'ti.tid = td.tid');
$query->condition('td.vid', $vocabulary->vid);
$query->addTag('vocabulary_node_count');
$count = $query->execute()->fetchField();
$replacements[$original] = $count;
break;
}
}
}
return $replacements;
}