".t("Use this form to select the source and target vocabularies").""; $help_message .= '

'.t("Switching a term from one vocabulary to another is done in two steps: first, choose your source and target vocabulary, then choose the source and target terms.").'

'; $help_message .= '

'.t("See the ").''.t("help for information on limitations to this module").'

'; return $help_message; case "Select vocabularies": $help_message = "

".t("Use this form to select the source and target terms")."

"; $help_message .= "

".t("The selected source term will be moved to become a sub-term of the selected target term")."

"; return $help_message; case "Select terms": $help_message = "

".t("Term movement results")."

"; return $help_message; } case 'admin/help#taxonomy_switch': return '

'.t('Moving terms between vocabularies').'

'.t("This module allows you to move a term, together with all its sub-terms and associated nodes, from one vocabulary to another. The switch is carried out in two steps: first choose the source and target vocabularies, then choose the source and target terms. The selected source term will be moved such that it becomes a sub-term of the selected target term.").'

'.t("The module will also maintain the coherence of the vocabulary definition:

  1. If the target vocabulary does not allow the definition of a term hierarchy, then the target term cannot be selected and the source term is moved to the root level of the target vocabulary.
  2. If the source vocabulary classifies content types which are not classified by the target vocabulary, then the latter is updated to classify the missing types

You can access the switch module from a tab on the categories administration page; note that the module requires 'administer taxonomy' user permissions").'

'.t('Easy node classification').'

'.t("A 'classify' tab is provided on node pages: this allows you to reclassify a node without the overhead of up- and down-loading the actual node content").'

'.t('Constraints').'

'.t("There are certain important limitations to this module:

  1. The module is unable at present to switch terms from vocabularies with multiple hierarchy enabled: these vocabularies are excluded from the 'From' list
  2. Moving the term will lose all 'Relations' information for this term, since Relations can only be defined within a single vocabulary

"); } } /** * Implementation of hook_menu: Define menu links. * * @note See hook_menu for a description of parameters and return values. */ function taxonomy_switch_menu() { $items = array(); /* TODO Non menu code that was placed in hook_menu under the '!$may_cache' block so that it could be run during initialization, should now be moved to hook_init. Previously we called hook_init twice, once early in the bootstrap process, second just after the bootstrap has finished. The first instance is now called boot instead of init. In Drupal 6, there are now two hooks that can be used by modules to execute code at the beginning of a page request. hook_boot() replaces hook_boot() in Drupal 5 and runs on each page request, even for cached pages. hook_boot() now only runs for non-cached pages and thus can be used for code that was previously placed in hook_menu() with $may_cache = FALSE: Dynamic menu items under a '!$may_cache' block can often be simplified to remove references to arg(n) and use of '%' to check conditions. See http://drupal.org/node/103114. The title and description arguments should not have strings wrapped in t(), because translation of these happen in a later stage in the menu system. */ if (!$may_cache) { $items['admin/content/taxonomy/switch'] = array( 'title' => 'Switch', 'access arguments' => array('administer taxonomy'), 'page callback' => 'taxonomy_switch', 'type' => MENU_LOCAL_TASK); if (arg(0) == 'node' && is_numeric(arg(1)) && user_access('administer taxonomy')) { $items[] = array('path' => 'node/'. arg(1) .'/classify', 'title' => t('classify'), 'callback' => 'taxonomy_switch_node', 'access' => user_access('administer taxonomy'), 'type' => MENU_LOCAL_TASK, 'weight' => 4); } } return $items; } /** * Handle the selection of different taxonomy switching forms */ function taxonomy_switch() { $output = ''; /* TODO The 'op' element in the form values is deprecated. Each button can have #validate and #submit functions associated with it. Thus, there should be one button that submits the form and which invokes the normal form_id_validate and form_id_submit handlers. Any additional buttons which need to invoke different validate or submit functionality should have button-specific functions. */ $op = $_POST['op']; // $form_values['op']; $edit = $_POST; if (empty($op)) { $op = arg(1); } switch ($op) { case 'Switch': $output = taxonomy_switch_execute($edit); case 'Select vocabularies': $output = drupal_get_form('taxonomy_switch_term_form', $edit); break; case 'Select terms': $output = taxonomy_switch_execute($edit); break; case 'switch': default: $output = drupal_get_form('taxonomy_switch_vocab_form', $edit); } print theme('page', $output); } /** * Handle the selection of different node switching forms */ function taxonomy_switch_node() { global $user; $op = $_POST['op']; $edit = $_POST; // arg(1) is the nid if ($op) { $node = module_invoke('node', 'load', arg(1)); taxonomy_node_save($node, $edit['taxonomy']); if (module_exists('checkout')) { module_invoke('checkout', 'checkin', arg(1)); } watchdog('content', '%type: reclassified %title.', array('%type' => $node->type, '%title' => $node->title), WATCHDOG_NOTICE, l(t('view'), 'node/'. $node->nid)); drupal_set_message(t('The document has been reclassified')); print theme('page', $output); return; } if (module_exists('checkout')) { $checkout_info = module_invoke('checkout', 'checkout', arg(1)); if ($checkout_info->uid == $user->uid) { $checked_out = FALSE; } else { $checked_out = TRUE; } } else { $checked_out = FALSE; } if (!$checked_out) { $output = drupal_get_form('taxonomy_switch_classify_form'); return $output; } else { module_invoke('checkout', 'message', $checkout_info); } } /* In Drupal 5, forms must be created in dedicated builder functions */ function taxonomy_switch_classify_form($form_values = NULL) { $form = array(); $form['taxonomy']['#tree'] = TRUE; $node = module_invoke('node', 'load', arg(1)); drupal_set_title(check_plain($node->title)); $node_vocabs = module_invoke('taxonomy', 'get_vocabularies', $node->type); foreach ($node_vocabs as $i => $vid) { $form['taxonomy'][$vid->vid] = module_invoke('taxonomy', 'form', $vid->vid); $node_terms = taxonomy_node_get_terms_by_vocabulary($node, $vid->vid); if ($vid->tags == 1) { $typed_string = array(); foreach($node_terms as $ix => $n_tid) { $typed_string[] .= $n_tid->name; } $form['taxonomy']['tags'][$vid->vid]['#title'] = $form['taxonomy'][$vid->vid]['#title']; $form['taxonomy']['tags'][$vid->vid]['#description'] = $form['taxonomy'][$vid->vid]['#description']; $form['taxonomy']['tags'][$vid->vid]['#default_value'] = implode(', ', $typed_string); $form['taxonomy']['tags'][$vid->vid]['#type'] = 'textfield'; $form['taxonomy']['tags'][$vid->vid]['#maxlength'] = 120; $form['taxonomy']['tags'][$vid->vid]['#size'] = 60; $form['taxonomy']['tags'][$vid->vid]['#autocomplete_path'] = 'taxonomy/autocomplete/'. $vid->vid; unset($form['taxonomy'][$vid->vid]); } else { $form['taxonomy'][$vid->vid]['#default_value'] = array_keys($node_terms); } if ($vid->required) { $form['taxonomy'][$vid->vid]['#required'] = TRUE; } } $form[] = array( '#type' => 'submit', '#value' => t("Classify"), ); // $form['#redirect'] = FALSE; return $form; } /** * Move term from one vocabulary to another and display result */ function taxonomy_switch_execute($edit) { $from_vid = module_invoke('taxonomy', 'vocabulary_load', $edit['taxonomy_switch_from_vid']); $to_vid = module_invoke('taxonomy', 'vocabulary_load', $edit['taxonomy_switch_to_vid']); $from_term = module_invoke('taxonomy', 'get_term', $edit['taxonomy_switch_from_term']); $output = t('The term ').''.$from_term->name.''.t(' has been moved from the source vocabulary ').''.$from_vid->name.''.t(' to the target vocabulary ').''.$to_vid->name.''.'
'; $output .= t('The term has been moved '); // Here we could lock the tables // db_query("LOCK TABLES {term_data} WRITE {term_hierarchy} WRITE {term_relation} WRITE {term_synonym}"); $children = module_invoke('taxonomy', 'get_tree', $from_term->vid, $from_term->tid); db_query('DELETE FROM {term_relation} WHERE tid1 = %d OR tid2 = %d', $from_term->tid, $from_term->tid); db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $from_term->tid); $ins = "vid = '". str_replace('%', '%%', db_escape_string($to_vid->vid)) ."'"; foreach ($children as $c => $from_term_child) { db_query('UPDATE {term_data} SET '. $ins .' WHERE tid = %d', $from_term_child->tid); } db_query('UPDATE {term_data} SET '. $ins .' WHERE tid = %d', $from_term->tid); /* taxonomy_get_tree($vid, $parent = 0, $depth = -1, $max_depth = NULL) $vid Which vocabulary to generate the tree for. $parent The term ID under which to generate the tree. If 0, generate the tree for the entire vocabulary. $depth Internal use only. $max_depth The number of levels of the tree to return. Leave NULL to return all levels.*/ switch ($edit['taxonomy_switch_to_hierarchy']) { case 0: $output .= t('to root level in the target vocabulary'); db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $from_term->tid, 0); break; case 1: $to_term = module_invoke('taxonomy', 'get_term', $edit['taxonomy_switch_to_term']); $to_term->name ? $output .= t('below the term '). '' . $to_term->name. '' : $output .= t('below the root'); $output .= t(' in the target vocabulary'); db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $from_term->tid, $edit['taxonomy_switch_to_term']); break; case 2: $output .= t('below the following terms in the multiple-hierarchy target vocabulary : ').'
'; foreach ($edit['taxonomy_switch_to_term'] as $i => $to_tid) { $to_term = module_invoke('taxonomy', 'get_term', $to_tid); $to_term->name ? $output .= '- '.$to_term->name.'
' : $output .= '- '.t('Root').'
'; db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $from_term->tid, $to_term->tid); } break; } // Update the content types in the target vocabulary if necessary if ($edit['taxonomy_switch_target_new_types']) { foreach (explode(',', $edit['taxonomy_switch_target_new_types']) as $type) { db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $to_vid->vid, $type); } } // db_query("UNLOCK TABLES"); return $output; } /** * taxonomy_switch_vocab_form */ function taxonomy_switch_vocab_form(&$form_state, $form_values = NULL) { if (!$form_values['vid']) { $form_values['vid'] = 0; } foreach (module_invoke('taxonomy', 'get_vocabularies') as $vid => $voc) { if ($voc->hierarchy < 2) { $vocs_from[$vid] = $voc->name; } } $form["vid_from"] = array( '#type' => 'select', '#title' => t("Switch FROM vocabulary"), '#default_value' => $form_values['vid_from'], '#options' => $vocs_from, '#description' => t("The vocabulary FROM which the term(s) will be switched."), ); foreach (module_invoke('taxonomy', 'get_vocabularies') as $vid => $voc) { $vocs_to[$vid] = $voc->name; } $form["vid_to"] = array( '#type' => 'select', '#title' => t("Switch TO vocabulary"), '#default_value' => $form_values['vid_to'], '#options' => $vocs_to, '#description' => t("The vocabulary TO which the term(s) will be switched."), ); $form[] = array( '#type' => 'submit', '#value' => t("Select vocabularies"), ); $form['#method'] = 'POST'; $form['#action'] = url('admin/content/taxonomy/switch'); $form['#attributes'] = array('enctype' => 'multipart/form-data'); // $form['#redirect'] = FALSE; return $form; } /** * Form to select source and target vocabularies */ function taxonomy_switch_term_form(&$form_state, $form_values = NULL) { $vid_from = module_invoke('taxonomy', 'vocabulary_load', $form_values['vid_from']); $vid_to = module_invoke('taxonomy', 'vocabulary_load', $form_values['vid_to']); // Control 1 - don't move terms with sub-terms into a vocabulary that does not support sub-terms $no_children = FALSE; if ($vid_to->hierarchy == 0 && $vid_from->hierarchy > 0) { $from_message = '

'.t('NOTE: The target vocabulary hierarchy is disabled. Terms with subterms are not available for movement into the target hierarchy').'

'; $no_children = TRUE; } else { if ($vid_from->hierarchy > 0) { $from_message = '

'.t('NOTE: moving a Term from this vocabulary will also move any of its sub-terms').'

'; } else { $from_message = ''; } } if ($no_children) { $tree = module_invoke('taxonomy', 'get_tree', $form_values['vid_from']); foreach ($tree as $tid_from => $term_from){ $children = count(module_invoke('taxonomy', 'get_children', $term_from->tid, $form_values['vid_from'])); if ($children == 0) { $terms_from[$term_from->tid] = $term_from->name; } } } else { foreach (module_invoke('taxonomy', 'get_tree', $form_values['vid_from']) as $tid_from => $term_from) { $terms_from[$term_from->tid] = str_pad($term_from->name, (strlen($term_from->name) + $term_from->depth), "-", STR_PAD_LEFT); } } // Control 2 - verify the compatibility between the content types of the two vocabularies $new_types = array_diff($vid_from->nodes, $vid_to->nodes); if (count($new_types) > 0 ){ $to_message = '

'.t('NOTE: The following content types are not currently classified by the target vocabulary : "') . implode(', ', $new_types) . '". ' . $vid_to->name . '' . t(' will be updated to classify these content types'); $form['taxonomy_switch_target_new_types'] = array('#type' => 'hidden', '#value' => implode(',',$new_types)); } else { $to_message = ''; } $form['taxonomy_switch_source'] = array( '#type' => 'fieldset', '#title' => t('SOURCE vocabulary : ').$vid_from->name, '#description' => $from_message, ); $form['taxonomy_switch_source']['taxonomy_switch_from_term'] = array( '#type' => 'select', '#title' => t('Source terms'), '#default_value' => 0, '#options' => $terms_from, '#description' => t('You must choose one from the list'), '#extra' => $extra = 0, '#multiple' => FALSE, '#required' => TRUE, ); // Target terms $terms_to[0] = t(''); foreach (module_invoke('taxonomy', 'get_tree', $form_values['vid_to']) as $tid_to => $term_to) { $terms_to[$term_to->tid] = str_pad($term_to->name, (strlen($term_to->name) + $term_to->depth), "-", STR_PAD_LEFT); } $form['taxonomy_switch_target'] = array( '#type' => 'fieldset', '#title' => t('TARGET vocabulary : ').$vid_to->name, '#description' => $to_message, ); switch ($vid_to->hierarchy) { case 0: $to_form = '

'.t('NOTE: The target vocabulary hierarchy is disabled. The term will be moved to level root').'

'; break; case 1: $form['taxonomy_switch_target']['taxonomy_switch_to_term'] = array( '#type' => 'select', '#title' => t('Target terms'), '#default_value' => 0, '#options' => $terms_to, '#description' => t('You must choose one from the list'), '#extra' => $extra = 0, '#multiple' => FALSE, '#required' => TRUE, ); break; case 2: $form['taxonomy_switch_target']['taxonomy_switch_to_term'] = array( '#type' => 'select', '#title' => t('Target terms'), '#default_value' => 0, '#options' => $terms_to, '#description' => 'You must choose at least one from the list', '#extra' => $extra = 0, '#multiple' => TRUE, '#required' => TRUE, ); break; } $form['taxonomy_switch_to_hierarchy'] = array( '#type' => 'hidden', '#value' => $vid_to->hierarchy, ); $form['taxonomy_switch_to_vid'] = array( '#type' => 'hidden', '#value' => $vid_to->vid, ); $form['taxonomy_switch_from_vid'] = array( '#type' => 'hidden', '#value' => $vid_from->vid, ); $form[] = array( '#type' => 'submit', '#value' => t("Select terms"), ); $form['#method'] = 'POST'; $form['#action'] = url('admin/content/taxonomy/switch'); $form['#attributes'] = array('enctype' => 'multipart/form-data'); // $form['#redirect'] = FALSE; return $form; } ?>