'admin/content/taxonomy_manager', 'title' => t('Taxonomy Manager'), 'description' => t('Administer vocabularies with the Taxonomy Manager'), 'callback' => 'taxonomy_manager_voc_list', 'access' => user_access('administer taxonomy'), 'type' => MENU_NORMAL_ITEM ); } else { if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'taxonomy_manager' && is_numeric(arg(3))) { $vid = arg(3); $items[] = array('path' => 'admin/content/taxonomy_manager/'. $vid, 'title' => t('Taxonomy Manager'), 'callback' => 'drupal_get_form', 'callback arguments' => array('taxonomy_manager_form', $vid), 'access' => user_access('administer taxonomy'), 'type' => MENU_CALLBACK ); } //if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'taxonomy_manager' && arg(3) == 'childform' && is_numeric(arg(4))) { //$parent = arg(4); $items[] = array('path' => 'admin/content/taxonomy_manager/childform', 'title' => t('Taxonomy Manager'), 'callback' => 'taxonomy_manager_tree_build_child_form', 'access' => user_access('administer taxonomy'), 'type' => MENU_CALLBACK ); //} $items[] = array('path' => 'admin/content/taxonomy_manager/weight', 'title' => t('Taxonomy Manager'), 'callback' => 'taxonomy_manager_update_weights', 'access' => user_access('administer taxonomy'), 'type' => MENU_CALLBACK ); //if (arg(4) && arg(5) && arg(6)) { $items[] = array('path' => 'admin/content/taxonomy_manager/siblingsform', 'title' => t('Taxonomy Manager'), 'callback' => 'taxonomy_manager_tree_build_siblings_form', 'access' => user_access('administer taxonomy'), 'type' => MENU_CALLBACK ); //} } return $items; } /** * list of vocabularies, which link to Taxonomy Manager interface */ function taxonomy_manager_voc_list() { $vocabularies = taxonomy_get_vocabularies(); $voc_list = array(); foreach ($vocabularies as $vocabulary) { $voc_list[] = l($vocabulary->name, 'admin/content/taxonomy_manager/'. $vocabulary->vid); } return theme('item_list', $voc_list, "Vocabularies:"); } /** * defines forms for taxonomy manager interface * * @param $vid vocabulary id */ function taxonomy_manager_form($vid) { $module_path = drupal_get_path('module', 'taxonomy_manager') .'/'; drupal_add_css($module_path .'css/taxonomy_manager.css'); drupal_add_js($module_path .'js/hideForm.js'); drupal_add_js($module_path .'js/updateWeight.js'); drupal_add_js(array('updateWeight' => array('up' => 'go-up', 'down' => 'go-down', 'url' => url('admin/content/taxonomy_manager/weight/'. $vid))), 'setting'); $form = array(); $voc = taxonomy_get_vocabulary($vid); if (!is_numeric($voc->vid)) { $text = t('No vocabulary with this ID available!. '); $text .= t('Check this !link_list for available vocabularies or !link_create a new one', array('!link_list' => l('list', 'admin/content/taxonomy_manager'), '!link_create' => l('create', 'admin/content/taxonomy/add/vocabulary'))); $form['text'] = array( '#value' => $text, ); return $form; } $form['vid'] = array('#type' => 'value', "#value" => $vid); if (_taxonomy_manager_voc_is_empty($vid)) { $text = t('No terms available'); $form['text'] = array( '#value' => $text, ); $form += taxonomy_manager_add_form(false); return $form; } $form['taxonomy_manager'] = array( '#type' => 'fieldset', '#title' => $voc->name, '#weight' => 10, '#tree' => TRUE, '#prefix' => '
', '#suffix' => '
', ); $form['taxonomy_manager']['tree'] = array( '#type' => 'taxonomy_manager_tree', '#vid' => $vid, '#pager' => TRUE, ); $form['toolbar'] = array( '#type' => 'fieldset', '#title' => t('Toolbar'), '#weight' => -10, ); $form['toolbar']['up_link'] = array( '#type' => 'markup', '#value' => theme("image", $module_path ."images/go-up.png", "go up", NULL, array('id' => 'go-up')), '#prefix' => '
', ); $form['toolbar']['down_link'] = array( '#type' => 'markup', '#value' => theme("image", $module_path ."images/go-down.png", "go down", NULL, array('id' => 'go-down')), ); $form['toolbar']['delete_confirm'] = array( '#type' => 'markup', '#value' => theme("image", $module_path ."images/list-remove.png", "remove", NULL, array('id' => 'delete-confirm')), ); $form['toolbar']['add_show'] = array( '#type' => 'markup', '#value' => theme("image", $module_path ."images/list-add.png", "add", NULL, array('id' => 'add-show')), '#suffix' => '
', ); $form['toolbar']['merge_show'] = array( '#type' => 'button', '#value' => t('Merge selected into...'), '#theme' => 'no_submit_button', '#prefix' => '
' ); $form['toolbar']['move_show'] = array( '#type' => 'button', '#value' => t('Move selected to...'), '#theme' => 'no_submit_button', '#suffix' => '
' ); $form['toolbar']['wrapper'] = array( '#type' => 'markup', '#value' => '
', '#weight' => 20, ); $form += taxonomy_manager_add_form(); $form += taxonomy_manager_merge_form($voc); $form += taxonomy_manager_move_form($voc); $form += taxonomy_manager_confirm_delete($voc); return $form; } /** * function gets called by the taxonomy_manager_tree form type (form_id +_operations) * return an form array with values to show next to every term value */ function taxonomy_manager_tree_operations() { $module_path = drupal_get_path('module', 'taxonomy_manager') .'/'; $form = array(); $form['up'] = array('#value' => theme("image", $module_path ."images/go-up-small.png", "go up", NULL, array('class' => 'term-up'))); $form['down'] = array('#value' => theme("image", $module_path ."images/go-down-small.png", "go down", NULL, array('class' => 'term-down'))); return $form; } /** * confirmation form for deleting selected terms */ function taxonomy_manager_confirm_delete($voc) { drupal_add_js(array('hideForm' => array( 'show_button' => 'delete-confirm', 'hide_button' => 'edit-delete-cancel', 'div' => 'del-confirm-form')), 'setting'); $text = t('Confirmation required! Remember all term specific data will be lost!'); $form = array(); $form['delete'] = array( '#type' => 'fieldset', '#tree' => TRUE, '#title' => t('Confirmation'), '#prefix' => '
', '#suffix' => '
', ); $form['delete']['text'] = array( '#value' => "". $text ."", ); if ($voc->hierarchy != 0) { $options = array( 'delete_orphans' => t('Delete orphans'), ); $form['delete']['options'] = array( '#type' => 'checkboxes', '#title' => t('Options'), '#options' => $options, ); } $form['delete']['delete'] = array( '#type' => 'submit', '#value' => t('Delete'), ); $form['delete']['cancel'] = array( '#type' => 'button', '#value' => t('Cancel'), '#theme' => 'no_submit_button', ); return $form; } /** * form for adding multiple terms */ function taxonomy_manager_add_form($hide_form = TRUE) { if ($hide_form) { drupal_add_js(array('hideForm' => array( 'show_button' => 'add-show', 'hide_button' => 'edit-add-cancel', 'div' => 'add-form')), 'setting'); } $form = array(); $form['add'] = array( '#type' => 'fieldset', '#tree' => TRUE, '#title' => t('Add new terms'), '#prefix' => '
', '#suffix' => '
', '#description' => t('Depending on the hierarchy settings, you can set parents (one ore more). Selected terms will get the parents of the new inserted'), ); for ($i=0; $i<5; $i++) { $form['add']['term'][$i] = array( '#type' => 'textfield', ); } $form['add']['add'] = array( '#type' => 'submit', '#value' => t('Add'), ); $form['add']['cancel'] = array( '#type' => 'button', '#value' => t('Cancel'), '#theme' => 'no_submit_button', ); return $form; } /** * form for merging terms */ function taxonomy_manager_merge_form($voc) { drupal_add_js(array('hideForm' => array( 'show_button' => 'edit-merge-show', 'hide_button' => 'edit-merge-cancel', 'div' => 'merge-form')), 'setting'); $form = array(); $form['merge'] = array( '#type' => 'fieldset', '#tree' => TRUE, '#title' => t('Merging of terms'), '#prefix' => '
', '#suffix' => '
', ); $help = t("You can choose either an exisiting term or either typ in a new term, which will be created automatically, as main term (selected terms get merged into this term)"); $form['merge']['main_term'] = array( '#type' => 'textfield', '#title' => t('Main term'), '#description' => $help, '#required' => FALSE, '#autocomplete_path' => 'taxonomy/autocomplete/'. $voc->vid, ); $options = array(); switch ($voc->hierarchy) { //multiple hierarchy case 2: $options['collect_parents'] = t('Collect all parents of merging terms an add it to main term'); //single hierarchy case 1: $options['collect_children'] = t('Collect all children of merging terms an add it to main term'); break; } if ($voc->relations) { $options['collect_relations'] = t('Collect all relations of merging terms and add it to main term'); } if (count($options) > 0) { $form['merge']['options'] = array( '#type' => 'checkboxes', '#title' => t('Options'), '#options' => $options, ); } $form['merge']['submit'] = array( '#type' => 'submit', '#value' => t('Merge'), ); $form['merge']['cancel'] = array( '#type' => 'button', '#value' => t('Cancel'), '#theme' => 'no_submit_button', ); return $form; } /** * form for moving terms in hierarchies */ function taxonomy_manager_move_form($voc) { drupal_add_js(array('hideForm' => array( 'show_button' => 'edit-move-show', 'hide_button' => 'edit-move-cancel', 'div' => 'move-form')), 'setting'); $form = array(); $form['move'] = array( '#type' => 'fieldset', '#tree' => TRUE, '#title' => t('Moving of terms'), '#prefix' => '
', '#suffix' => '
', ); $help = t("You can choose either an exisiting term or either typ in a new term, which will be created automatically, as parent term (selected terms get children of this term). If multiple parents are allowed, you can seperate more terms in the autocomplete form with ','. If selected terms should have no parent, leave it blank!"); $form['move']['parents'] = array( '#type' => 'textfield', '#title' => t('Parent term(s)'), '#description' => $help, '#required' => FALSE, '#autocomplete_path' => 'taxonomy/autocomplete/'. $voc->vid, ); $options = array(); if ($voc->hierarchy == 2) { $options['keep_old_parents'] = t('Don\'t delete old parents, only add new parents (multiple parents)'); } if (count($options) > 0) { $form['move']['options'] = array( '#type' => 'checkboxes', '#title' => t('Options'), '#options' => $options, ); } $form['move']['submit'] = array( '#type' => 'submit', '#value' => t('Move'), ); $form['move']['cancel'] = array( '#type' => 'button', '#value' => t('Cancel'), '#theme' => 'no_submit_button', ); return $form; } /** * validates the form */ function taxonomy_manager_form_validate($form_id, $form_values) { $selected_tids = array(); $selected_tids = $form_values['taxonomy_manager']['tree']['selected_terms']; switch ($form_values['op']) { case t('Delete'): if (count($selected_tids) < 1) { form_set_error('delete', t("No terms selected")); } break; case t('Add'): //check for parents concerning voc settings $voc = taxonomy_get_vocabulary($form_values['vid']); if ($voc->hierarchy == 0 && count($selected_tids) > 0) { form_set_error('add', t('This vocabulary doesn\'t provide hierarchies. Please unselect terms in the list before adding new terms.')); } else if ($voc->hierarchy == 1 && count($selected_tids) > 1) { form_set_error('add', t('This vocabulary provides only single hirarchies. Please select only one term in the list (at maximum)')); } break; case t('Merge'): $main_terms = array(); $main_terms = taxonomy_manager_autocomplete_tags_get_tids($form_values['merge']['main_term'], $form_values['vid']); if (count($main_terms) > 1) { form_set_error('merge][main_term', t("The field %title doesn't allows multiple values", array('%title' => "'". t('Main term') ."'"))); } if (!is_numeric($main_terms[0]) || $main_terms[0] == 0) { form_set_error('merge][main_term', t("Error reading input from %title", array('%title' => "'". t('Main term') ."'"))); } if (count($selected_tids) < 1) { form_set_error('merge', t("No terms for merging selected")); } break; case t('Move'): if (count($selected_tids) < 1) { form_set_error('move', t("No terms selected")); } break; } } /** * submits the taxonomy manager form */ function taxonomy_manager_form_submit($form_id, $form_values) { $selected_tids = array(); $selected_tids = $form_values['taxonomy_manager']['tree']['selected_terms']; switch ($form_values['op']) { case t('Delete'): taxonomy_manager_delete_terms($selected_tids, $form_values['delete']['options']); drupal_set_message("Selected terms deleted"); break; case t('Add'): foreach ($form_values['add']['term'] as $value) { if (!empty($value)) { $term = array(); $term['name'] = $value; $term['vid'] = $form_values['vid']; $term['parent'] = $selected_tids; taxonomy_save_term($term); } } drupal_set_message("Terms added"); break; case t('Merge'): $main_terms = taxonomy_manager_autocomplete_tags_get_tids($form_values['merge']['main_term'], $form_values['vid']); $main_term = $main_terms[0]; taxonomy_manager_merge($main_term, $selected_tids, $form_values['merge']['options']); drupal_set_message("Terms merged"); break; case t('Move'): $parents = taxonomy_manager_autocomplete_tags_get_tids($form_values['move']['parents'], $form_values['vid']); if (count($parents) == 0) $parents[0] = 0; //if empty, delete all parents taxonomy_manager_move($parents, $selected_tids, $form_values['move']['options']); drupal_set_message("Terms moved"); break; } drupal_goto('admin/content/taxonomy_manager/'. $form_values['vid']); } /** * checks if voc has already terms saved * * @param $vid voc id * @return true, if terms already exists, else false */ function _taxonomy_manager_voc_is_empty($vid) { $count = db_result(db_query("SELECT t.* FROM {term_data} t INNER JOIN {term_hierarchy} h ON t.tid = h.tid WHERE vid = %d AND h.parent = 0 LIMIT 0,1", $vid)); if ($count == 0) return true; return false; } /** * deletes terms from the database * optional orphans (terms where parent get deleted) can be deleted as well * * (difference to taxonomy_del_term: deletion of orphans optional) * * @param $tids array of term id to delete * @param $options associative array with options * if $options['delete_orphans'] is true, orphans get deleted */ function taxonomy_manager_delete_terms($tids, $options = array()) { if (!is_array($tids)) array($tids); while (count($tids) > 0) { $orphans = array(); foreach ($tids as $tid) { if ($children = taxonomy_get_children($tid)) { foreach ($children as $child) { $parents = taxonomy_get_parents($child->tid); if ($options['delete_orphans']) { if (count($parents) == 1) { $orphans[] = $child->tid; } } else { db_query("DELETE FROM {term_hierarchy} WHERE tid = %d AND parent = %d", $child->tid, $tid); if (count($parents) == 1) { if (!db_result(db_query("SELECT COUNT(*) FROM {term_hierarchy} WHERE tid = %d AND parent = 0", $child->tid))) { db_query("INSERT INTO {term_hierarchy} (parent, tid) VALUES(0, %d)", $child->tid); } } } } } db_query('DELETE FROM {term_data} WHERE tid = %d', $tid); db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $tid); db_query('DELETE FROM {term_relation} WHERE tid1 = %d OR tid2 = %d', $tid, $tid); db_query('DELETE FROM {term_synonym} WHERE tid = %d', $tid); db_query('DELETE FROM {term_node} WHERE tid = %d', $tid); $term = (array) taxonomy_get_term($tid); module_invoke_all('taxonomy', 'delete', 'term', $term); $tids = $orphans; } } } /** * moves terms in hierarchies to other parents * * @param $parents * array of parent term ids to where children can be moved * array should only contain more parents if multi hiearchy enabled * if array contains 0, terms get placed to first (root) level * @param $children * array of term ids to move * @param $options * array of additional options for moving * 'keep_old_parents': if true, exisiting parents doesn't get deleted (only possible with multi hierarchies) */ function taxonomy_manager_move($parents, $children, $options = array()) { if (!is_array($parents)) array($parents); foreach ($children as $child) { if (!$options['keep_old_parents']) { db_query("DELETE FROM {term_hierarchy} WHERE tid = %d", $child); } foreach ($parents as $parent) { db_query("INSERT INTO {term_hierarchy} SET parent = %d, tid = %d", $parent, $child, $child); } } } /** * merges terms into another term (main term), all merged term get added * to the main term as synonyms. * term_node relations are updated automatically (node with one of merging terms gets main term assigned) * after all opterions are done (adding of hierarchies, relations is optional) merging * terms get deleted * * @param $main_term * id of term where other terms get merged into * @param $merging_terms * array of term ids, which get merged into main term and afterwards deleted * @param $options * array with additional options, possible values: * 'collect_parents': if true, all parents of merging terms get added to main term (only possible with multi hierarchies) * 'collect_children': if true, all children of merging terms get added to main term * 'collect_relations': if true, all relations of merging terms are transfered to main term */ function taxonomy_manager_merge($main_term, $merging_terms, $options = array()) { //TODO: add hook, so that other modules can consider changes foreach ($merging_terms as $merge_term) { //update node-relations $sql = db_query("SELECT nid FROM {term_node} WHERE tid = %d", $merge_term); while ($obj = db_fetch_object($sql)) { $nid = $obj->nid; db_query("DELETE FROM {term_node} WHERE tid = %d AND nid = %d", $merge_term, $nid); if (!db_result(db_query("SELECT COUNT(*) FROM {term_node} WHERE tid = %d AND nid = %d", $main_term, $nid))) { db_query("INSERT INTO {term_node} (tid, nid) VALUES (%d, %d)", $main_term, $nid); } } if ($options['collect_parents']) { $parents = taxonomy_get_parents($merge_term); foreach ($parents as $parent_tid => $parent_term) { if (!db_result(db_query("SELECT COUNT(*) FROM {term_hierarchy} WHERE tid = %d AND parent = %d", $main_term, $parent_tid))) { db_query("INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)", $main_term, $parent_tid); } } } if ($options['collect_children']) { $children = taxonomy_get_children($merge_term); foreach ($children as $child_tid => $child_term) { if (!db_result(db_query("SELECT COUNT(*) FROM {term_hierarchy} WHERE tid = %d AND parent = %d", $child_tid, $main_term))) { db_query("INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)", $child_tid, $main_term); } } } if ($options['collect_relations']) { $relations = taxonomy_get_related($merge_term); foreach ($relations as $related_tid => $relation) { if ($relation->tid1 == $merge_term) { if (!db_result(db_query("SELECT COUNT(*) FROM {term_relation} WHERE tid1 = %d AND tid2 = %d", $main_term, $related_tid))) { db_query("INSERT INTO {term_relation} (tid1, tid2) VALUES (%d, %d)", $main_term, $related_tid); } } else if ($relation->tid2 == $merge_term) { if (!db_result(db_query("SELECT COUNT(*) FROM {term_relation} WHERE tid2 = %d AND tid1 = %d", $main_term, $related_tid))) { db_query("INSERT INTO {term_relation} (tid2, tid1) VALUES (%d, %d)", $main_term, $related_tid); } } } } //save merged term as synonym $term = taxonomy_get_term($merge_term); if (!db_result(db_query("SELECT COUNT(*) FROM {term_synonym} WHERE tid = %d AND name = '%s'", $main_term, $term->name))) { db_query("INSERT INTO {term_synonym} (tid, name) VALUES (%d, '%s')", $main_term, $term->name); } taxonomy_manager_delete_terms(array($merge_term)); } } /** * helper function for getting out of term ids from autocomplete fields * non-exsiting terms get inserted autmatically * (part of taxonomy_node_save) * * @param $typed_input input string of form field * @param $vid vocabulary id * @return array of term ids */ function taxonomy_manager_autocomplete_tags_get_tids($typed_input, $vid) { $tids = array(); $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x'; preg_match_all($regexp, $typed_input, $matches); $typed_terms = array_unique($matches[1]); foreach ($typed_terms as $typed_term) { $typed_term = str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1', $typed_term)); $typed_term = trim($typed_term); if ($typed_term == "") { continue; } $possibilities = taxonomy_get_term_by_name($typed_term); $typed_term_tid = NULL; // tid match if any. foreach ($possibilities as $possibility) { if ($possibility->vid == $vid) { $typed_term_tid = $possibility->tid; $tids[] = $typed_term_tid; } } if (!$typed_term_tid) { $edit = array('vid' => $vid, 'name' => $typed_term); $status = taxonomy_save_term($edit); $typed_term_tid = $edit['tid']; $tids[] = $typed_term_tid; } } return $tids; } /** * callback for updating weights * data send through AJAX, $_POST * $_POST[$tid] => $weight * */ function taxonomy_manager_update_weights($vid) { $weights = $_POST; if (is_array($weights)) { foreach ($weights as $tid => $weight) { if (is_numeric($tid) && is_numeric($weight)) { if(_taxonomy_manager_tree_term_valid($tid, $vid)) { db_query("UPDATE {term_data} SET weight = %d WHERE tid = %d", $weight, $tid); } } } } exit(); } //if weightening in javascript gets slow, a way of calculating it on server side... /*function taxonomy_manager_update_weights($term_to_swap, $up, $parent = 0) { $vid = db_result(db_query("SELECT vid FROM {term_data} WHERE tid = %d", $term_to_swap)); $siblings = taxonomy_get_tree($vid, $parent, -1, 1); $found = false; for ($i=0; !$found && $itid) { $position = $i; $found = true; } } if ($up) { $up_term = $siblings[$position]; $down_term = $siblings[$position+1]; $up_term->position = $position; $down_term->position = $position+1; } else { $up_term = $siblings[$position-1]; $down_term = $siblings[$position]; $up_term->position = $position-1; $down_term->position = $position; } $weights = taoxnomy_manager_swap_weights($up_term, $down_term, $siblings); foreach ($weights as $tid => $weight) { db_query("UPDATE {term_data} SET weight = %d WHERE tid = %d", $weight, $tid); } } function taoxnomy_manager_swap_weights($up_term, $down_term, $siblings) { $weights = array(); if ($up_term->weight == $down_term->weight) { $weights[$up_term->tid] = $up_term->weight-1; } else { $weights[$up_term->tid] = $down_term->weight; $weights[$down_term->tid] = $up_term->weight; } if ($siblings[$up_term->position-1]->weight == $up_term->weight) { for ($i=0; $i<$up_term->position; $i++) { $weights[$siblings[$i]->tid] = $siblings[$i]->weight-1; } } else if ($siblings[$down_term->position+1]->weight == $down_term->weight) { for ($i=$down_term->position; $itid] = $siblings[$i]->weight+1; } } return $weights; }*/ /** * theme function for taxonomy manager form */ function theme_taxonomy_manager_form($form) { $pager = theme('pager', NULL, TAXONOMY_MANAGER_TREE_PAGE_SIZE, 0); $tree = drupal_render($form['taxonomy_manager']); $top = drupal_render($form); $output = $top . $pager . $tree; return $output; } /** * themes a real button form type (no form submit) */ function theme_no_submit_button($element) { // Make sure not to overwrite classes. if (isset($element['#attributes']['class'])) { $element['#attributes']['class'] = 'form-'. $element['#button_type'] .' '. $element['#attributes']['class']; } else { $element['#attributes']['class'] = 'form-'. $element['#button_type']; } return '\n"; } /****************************************** * TAXONOMY TREE FORM ELEMENT DEFINITION * * how to use: * $form['name'] = array( * '#type' => 'taxonomy_manager_tree', * '#vid' => $vid, * ); * * additional parameter: * #pager: TRUE / FALSE, * whether to use pagers (drupal pager, load of nested children, load of siblings) * or to load the whole tree on page generation * #parent: only children on this parent will be loaded * #siblings_page: current page for loading pf next siblings, internal use * * defining term operations: * to add values (operations,..) to each term, add a function, which return a form array * 'tree_form_id'_operations * * how to retrieve selected values: * selected terms ids are available in validate / submit function in * $form_values['name']['selected_terms']; * ******************************************/ /** * Implementation of hook_elements */ function taxonomy_manager_elements() { $type['taxonomy_manager_tree'] = array( '#input' => TRUE, '#process' => array('taxonomy_manager_tree_process_elements' => array()), '#tree' => TRUE, ); return $type; } /** * Processes the tree form element * * @param $element * @return the tree element */ function taxonomy_manager_tree_process_elements($element) { $module_path = drupal_get_path('module', 'taxonomy_manager') .'/'; $id = form_clean_id(implode('-', $element['#parents'])); if (!$element['#parent'] && !$element['#siblings_page']) { drupal_add_css($module_path .'css/taxonomy_manager.css'); drupal_add_js($module_path .'js/tree.js'); drupal_add_js($module_path .'js/childForm.js'); drupal_add_js($module_path .'js/siblingsForm.js'); drupal_add_js(array('siblingsForm' => array('url' => url('admin/content/taxonomy_manager/siblingsform'), 'modulePath' => $module_path)), 'setting'); drupal_add_js(array('childForm' => array('url' => url('admin/content/taxonomy_manager/childform'), 'modulePath' => $module_path)), 'setting'); drupal_add_js(array('taxonomy_manager' => array('modulePath' => $module_path)), 'setting'); drupal_add_js(array('taxonomytree' => array('id' => $id)), 'setting'); } if (!is_array($element['#operations'])) { $opertions_callback = implode('_', $element['#parents']) .'_operations'; if (function_exists($opertions_callback)) { $element['#operations'] = $opertions_callback(); } } $vid = $element['#vid']; $tree = _taxonomy_manager_tree_get_item($element['#vid'], $element['#parent'], $element['#pager'], $element['#siblings_page']); if ($element['#pager'] && !($element['#parent'] || $element['#siblings_page'])) { $element['pager'] = array('#value' => theme('pager', NULL, TAXONOMY_MANAGER_TREE_PAGE_SIZE)); } $element['#tree'] = TRUE; $element['#id'] = $id; $element['#validate'] = array('taxonomy_manager_tree_validate' => array()); $element['selected_terms'] = array('#type' => 'value', '#value' => array()); taxonomy_manager_tree_build_form($index = 0, $tree, $element['#elements'], $element, $element['#parents'], !$element['#pager'], $element['#siblings_page']); return $element; } /** * loads tree with terms (depending on various settings) * * @param $vid * @param $parent * @param $pager * @param $siblings_page * @return array with term elements */ function _taxonomy_manager_tree_get_item($vid, $parent = 0, $pager = FALSE, $siblings_page = 0) { $tree = array(); if ($pager) { if ($parent || $siblings_page) { $start = ($siblings_page-1) * TAXONOMY_MANAGER_TREE_PAGE_SIZE; $result = db_query_range("SELECT t.* FROM {term_data} t INNER JOIN {term_hierarchy} h ON t.tid = h.tid WHERE vid = %d AND h.parent = %d ORDER BY weight, name", $vid, $parent, $start, TAXONOMY_MANAGER_TREE_PAGE_SIZE); } else { $result = pager_query("SELECT t.* FROM {term_data} t INNER JOIN {term_hierarchy} h ON t.tid = h.tid WHERE vid = %d AND h.parent = 0 ORDER BY weight, name", TAXONOMY_MANAGER_TREE_PAGE_SIZE, 0, NULL, array($vid)); } while ($term = db_fetch_object($result)) { $tree[] = $term; } } else { $tree = taxonomy_get_tree($vid, $parent); } return $tree; } /** * recursive function for building nested form array * with checkboxes and weight forms for each term * * nested form array are allways appended to parent-form['children'] * * @param $index current index in tree, start with 0 * @param $tree of terms (generated by taxonomy_get_tree) * @return a form array */ function taxonomy_manager_tree_build_form(&$index, $tree, &$form, $root_form, $parents = array(), $build_subtrees = TRUE, $page = 0) { $current_depth = $tree[$index]->depth; while ($index < count($tree) && $tree[$index]->depth >= $current_depth) { $term = $tree[$index]; $attributes = array(); $this_parents = $parents; $this_parents[] = $term->tid; $form[$term->tid]['checkbox'] = array('#type' => 'checkbox', '#title' => $term->name, '#return_value' => 1); $form[$term->tid]['weight'] = array('#type' => 'hidden', '#value' => $term->weight, '#attributes' => array('class' => 'weight-form')); $form[$term->tid]['tid'] = array('#type' => 'hidden', '#value' => $term->tid, '#attributes' => array('class' => 'term-id')); if (is_array($root_form['#operations'])) { $form[$term->tid]['operations'] = $root_form['#operations']; } if ($page) { if ($index == (TAXONOMY_MANAGER_TREE_PAGE_SIZE - 1)) { $module_path = drupal_get_path('module', 'taxonomy_manager') .'/'; $form[$term->tid]['has-more-siblings'] = array( '#type' => 'markup', '#value' => theme("image", $module_path ."images/2downarrow.png", "more", NULL, array('class' => 'load-siblings')), ); $form[$term->tid]['page'] = array( '#type' => 'hidden', '#value' => $page, '#attributes' => array('class' => 'page'), ); $next_count = _taxonomy_manager_tree_get_next_siblings_count($term->vid, $page, $root_form['#parent']); $form[$term->tid]['next_count'] = array('#value' => $next_count); $form[$term->tid]['#attributes']['class'] .= 'has-more-siblings '; } } _taxonomy_manager_tree_element_set_params($this_parents, $form[$term->tid]); $index++; if ($build_subtrees) { if ($tree[$index]->depth > $current_depth) { $form[$term->tid]['#attributes']['class'] .= _taxonomy_manager_tree_term_get_class($index-1, $tree); taxonomy_manager_tree_build_form($index, $tree, $form[$term->tid]['children'], $root_form, array_merge($this_parents, array('children'))); } else { if ((count($tree)-1 == $index-1) || ($tree[$index]->depth < $current_depth)) { $form[$term->tid]['#attributes']['class'] .= 'last '; } } } else { if (_taxonomy_manager_tree_term_has_children($term->tid)) { $form[$term->tid]['#attributes']['class'] .= 'has-children '; if ($index-1 == count($tree)-1) { $form[$term->tid]['#attributes']['class'] .= 'lastExpandable '; } else { $form[$term->tid]['#attributes']['class'] .= 'expandable '; } } else { if ($index-1 == count($tree)-1) { $form[$term->tid]['#attributes']['class'] .= 'last '; } } } } } /** * adds #id and #name to all form elements * * @param $parents * @param $form */ function _taxonomy_manager_tree_element_set_params($parents, &$form) { foreach (element_children($form) as $field_name) { $field_parents = array_merge($parents, array($field_name)); $form[$field_name]['#tree'] = TRUE; $form[$field_name]['#post'] = array(); $form[$field_name]['#parents'] = $field_parents; $form[$field_name]['#id'] = form_clean_id('edit-'. implode('-', $field_parents)); $form[$field_name]['#name'] = array_shift($field_parents) .'['. implode('][', $field_parents) .']'; } } /** * calculates class type (expandable, lastExpandable) for current element * * @param $current_index in tree array * @param $tree array with terms * @return expandable or lastExpandable */ function _taxonomy_manager_tree_term_get_class($current_index, $tree) { $class = ''; $term = $tree[$current_index]; $next = $tree[++$current_index]; $children = false; while ($next->depth > $term->depth) { $children = true; $next = $tree[++$current_index]; } if ($next->depth == $term->depth) { $class = 'expandable '; } else { $class = 'lastExpandable '; } return $class; } /** * checks if a term has children * * @param $tid * @return true, if term has children, else false */ function _taxonomy_manager_tree_term_has_children($tid) { $count = db_result(db_query("SELECT COUNT(tid) FROM {term_hierarchy} WHERE parent = %d", $tid)); if ($count == 0) { return false; } return true; } /** * calculates number of next siblings if using paging * * @param $vid * @param $page * @param $parent * @return next page size */ function _taxonomy_manager_tree_get_next_siblings_count($vid, $page, $parent = 0) { $count = db_result(db_query("SELECT COUNT(t.tid) FROM {term_data} t INNER JOIN {term_hierarchy} h ON t.tid = h.tid WHERE vid = %d AND h.parent = %d", $vid, $parent)); $current_count = TAXONOMY_MANAGER_TREE_PAGE_SIZE * $page; $diff = $count - $current_count; if ($diff > TAXONOMY_MANAGER_TREE_PAGE_SIZE) { $diff = TAXONOMY_MANAGER_TREE_PAGE_SIZE; } return $diff; } /** * callback for generating and rendering nested child forms (AHAH) * * @param $tree_id * @param $parent term id of parent, that is expanded and of which children have to be loaded */ function taxonomy_manager_tree_build_child_form($tree_id, $parent) { $vid = db_result(db_query("SELECT vid FROM {term_data} WHERE tid = %d", $parent)); $child_form = array( '#type' => 'taxonomy_manager_tree', '#vid' => $vid, '#parent' => $parent, '#pager' => TRUE, '#siblings_page' => 1, ); $opertions_callback = str_replace('-', '_', $tree_id) .'_operations'; if (function_exists($opertions_callback)) { $child_form['#operations'] = $opertions_callback(); } _taxonomy_manager_tree_sub_forms_set_parents($tree_id, $parent, $child_form); $child_form = form_builder('taxonomy_manager_form', $child_form); print drupal_render($child_form); exit(); } /** * callback for generating and rendering next siblings terms form (AHAH) * * @param $tree_id * @param $page current page * @param $prev_tid last sibling, that appears * @param $parent if in hierarchies, parent id */ function taxonomy_manager_tree_build_siblings_form($tree_id, $page, $prev_tid, $parent = 0) { $vid = db_result(db_query("SELECT vid FROM {term_data} WHERE tid = %d", $prev_tid)); $siblings_form = array( '#type' => 'taxonomy_manager_tree', '#vid' => $vid, '#parent' => $parent, '#pager' => TRUE, '#siblings_page' => $page+1, ); $opertions_callback = str_replace('-', '_', $tree_id) .'_operations'; if (function_exists($opertions_callback)) { $child_form['#operations'] = $opertions_callback(); } _taxonomy_manager_tree_sub_forms_set_parents($tree_id, $parent, $siblings_form); $siblings_form = form_builder('taxonomy_manager_form', $siblings_form); $output = drupal_render($siblings_form); //cutting of ... can this be done cleaner? $output = drupal_substr($output, 4, -5); print $output; exit(); } /** * sets parents depending on form_id and hierarchical parents * * @param $tree_id * @param $parent term id * @param $form */ function _taxonomy_manager_tree_sub_forms_set_parents($tree_id, $parent, &$form) { $tree_ids = explode('-', $tree_id); foreach ($tree_ids as $key => $id) { $form['#parents'][] = $id; } if ($parent) { $all_parents = taxonomy_get_parents_all($parent); for ($i=count($all_parents)-1; $i >= 0; $i--) { $form['#parents'][] = $all_parents[$i]->tid; $form['#parents'][] = 'children'; } } } /** * validates submitted form values * checks if selected terms really belong to initial voc, if not --> form_set_error * * if all is valid, selected values get added to 'selected_terms' for easy use in submit * * @param $form */ function taxonomy_manager_tree_validate($form) { $selected = array(); $selected = _taxonomy_manager_tree_get_selected_terms($form['#value']); $vid = $form['#vid']; foreach ($selected as $tid) { if (!_taxonomy_manager_tree_term_valid($tid, $vid)) { form_set_error('', t('An illegal choice has been detected. Please contact the site administrator.')); } } form_set_value($form['selected_terms'], $selected); } /** * checks if term id belongs to vocabulary * * @param $tid term id * @param $vid voc id * @return true, if term belongs to voc, else false */ function _taxonomy_manager_tree_term_valid($tid, $vid) { $term = taxonomy_get_term($tid); if ($term->vid != $vid) return false; return true; } /** * returns term ids of selected checkboxes * * goes through nested form array recursivly * * @param $form_values * @return an array with ids of selected terms */ function _taxonomy_manager_tree_get_selected_terms($form_values) { $tids = array(); if (is_array($form_values)) { foreach ($form_values as $tid => $form_value) { if ($form_value['checkbox']) { $tids[$tid] = $tid; } if (is_array($form_value['children'])) { $tids += _taxonomy_manager_tree_get_selected_terms($form_value['children']); } } } return $tids; } /** * theme function for root element * * @param $element * @return html output */ function theme_taxonomy_manager_tree(&$element) { $tree = theme('taxonomy_manager_tree_elements', $element['#elements']); if(!$element['#parent'] && !$element['#siblings_page']) { $output = '
'; $output .= $tree; $output .= '
'; return theme('form_element', $element, $output); } return $tree; } /** * recursive theme function for term elements * * @param $element * @return html lists */ function theme_taxonomy_manager_tree_elements($element) { $output .= ""; return $output; }