First commit
This commit is contained in:
commit
c6e2478c40
13918 changed files with 2303184 additions and 0 deletions
54
modules/taxonomy/taxonomy-term.tpl.php
Normal file
54
modules/taxonomy/taxonomy-term.tpl.php
Normal 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>
|
984
modules/taxonomy/taxonomy.admin.inc
Normal file
984
modules/taxonomy/taxonomy.admin.inc
Normal 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'];
|
||||
}
|
231
modules/taxonomy/taxonomy.api.php
Normal file
231
modules/taxonomy/taxonomy.api.php
Normal 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".
|
||||
*/
|
13
modules/taxonomy/taxonomy.css
Normal file
13
modules/taxonomy/taxonomy.css
Normal 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;
|
||||
}
|
15
modules/taxonomy/taxonomy.info
Normal file
15
modules/taxonomy/taxonomy.info
Normal 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"
|
||||
|
939
modules/taxonomy/taxonomy.install
Normal file
939
modules/taxonomy/taxonomy.install
Normal 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".
|
||||
*/
|
40
modules/taxonomy/taxonomy.js
Normal file
40
modules/taxonomy/taxonomy.js
Normal 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);
|
2059
modules/taxonomy/taxonomy.module
Normal file
2059
modules/taxonomy/taxonomy.module
Normal file
File diff suppressed because it is too large
Load diff
181
modules/taxonomy/taxonomy.pages.inc
Normal file
181
modules/taxonomy/taxonomy.pages.inc
Normal 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);
|
||||
}
|
2095
modules/taxonomy/taxonomy.test
Normal file
2095
modules/taxonomy/taxonomy.test
Normal file
File diff suppressed because it is too large
Load diff
189
modules/taxonomy/taxonomy.tokens.inc
Normal file
189
modules/taxonomy/taxonomy.tokens.inc
Normal 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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue