/[drupal]/contributions/modules/taxonomy_manager/taxonomy_manager.module
ViewVC logotype

Contents of /contributions/modules/taxonomy_manager/taxonomy_manager.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.5 - (show annotations) (download) (as text)
Sun Jul 29 16:23:52 2007 UTC (2 years, 4 months ago) by mh86
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-5
Changes since 1.4: +592 -135 lines
File MIME type: text/x-php
added paging mechanism, seperated tree form as own form element type
1 <?php
2
3 // $Id: taxonomy_manager.module,v 1.4 2007/07/22 15:08:11 mh86 Exp $
4
5 /**
6 *
7 * @file
8 * Taxonomy Manager
9 *
10 * Administration interface for managing taxonomy vocabularies
11 *
12 */
13
14 define('TAXONOMY_MANAGER_TREE_PAGE_SIZE', 50);
15
16
17 /**
18 * Implementation of hook_menu
19 */
20 function taxonomy_manager_menu($may_cache) {
21 $items = array();
22
23 if ($may_cache) {
24 $items[] = array('path' => 'admin/content/taxonomy_manager',
25 'title' => t('Taxonomy Manager'),
26 'description' => t('Administer vocabularies with the Taxonomy Manager'),
27 'callback' => 'taxonomy_manager_voc_list',
28 'access' => user_access('administer taxonomy'),
29 'type' => MENU_NORMAL_ITEM
30 );
31 }
32 else {
33 if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'taxonomy_manager' && is_numeric(arg(3))) {
34 $vid = arg(3);
35 $items[] = array('path' => 'admin/content/taxonomy_manager/'. $vid,
36 'title' => t('Taxonomy Manager'),
37 'callback' => 'drupal_get_form',
38 'callback arguments' => array('taxonomy_manager_form', $vid),
39 'access' => user_access('administer taxonomy'),
40 'type' => MENU_CALLBACK
41 );
42 }
43
44 //if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'taxonomy_manager' && arg(3) == 'childform' && is_numeric(arg(4))) {
45 //$parent = arg(4);
46 $items[] = array('path' => 'admin/content/taxonomy_manager/childform',
47 'title' => t('Taxonomy Manager'),
48 'callback' => 'taxonomy_manager_tree_build_child_form',
49 'access' => user_access('administer taxonomy'),
50 'type' => MENU_CALLBACK
51 );
52 //}
53
54 $items[] = array('path' => 'admin/content/taxonomy_manager/weight',
55 'title' => t('Taxonomy Manager'),
56 'callback' => 'taxonomy_manager_update_weights',
57 'access' => user_access('administer taxonomy'),
58 'type' => MENU_CALLBACK
59 );
60 //if (arg(4) && arg(5) && arg(6)) {
61
62 $items[] = array('path' => 'admin/content/taxonomy_manager/siblingsform',
63 'title' => t('Taxonomy Manager'),
64 'callback' => 'taxonomy_manager_tree_build_siblings_form',
65 'access' => user_access('administer taxonomy'),
66 'type' => MENU_CALLBACK
67 );
68 //}
69 }
70
71 return $items;
72 }
73
74 /**
75 * list of vocabularies, which link to Taxonomy Manager interface
76 */
77 function taxonomy_manager_voc_list() {
78 $vocabularies = taxonomy_get_vocabularies();
79 $voc_list = array();
80
81 foreach ($vocabularies as $vocabulary) {
82 $voc_list[] = l($vocabulary->name, 'admin/content/taxonomy_manager/'. $vocabulary->vid);
83 }
84
85 return theme('item_list', $voc_list, "Vocabularies:");
86
87 }
88
89 /**
90 * defines forms for taxonomy manager interface
91 *
92 * @param $vid vocabulary id
93 */
94 function taxonomy_manager_form($vid) {
95 $module_path = drupal_get_path('module', 'taxonomy_manager') .'/';
96 drupal_add_css($module_path .'css/taxonomy_manager.css');
97 drupal_add_js($module_path .'js/hideForm.js');
98 drupal_add_js($module_path .'js/updateWeight.js');
99
100 drupal_add_js(array('updateWeight' => array('up' => 'go-up', 'down' => 'go-down', 'url' => url('admin/content/taxonomy_manager/weight/'. $vid))), 'setting');
101
102 $form = array();
103
104 $voc = taxonomy_get_vocabulary($vid);
105
106 if (!is_numeric($voc->vid)) {
107 $text = t('No vocabulary with this ID available!. ');
108 $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')));
109 $form['text'] = array(
110 '#value' => $text,
111 );
112 return $form;
113 }
114
115 $form['vid'] = array('#type' => 'value', "#value" => $vid);
116
117 if (_taxonomy_manager_voc_is_empty($vid)) {
118 $text = t('No terms available');
119 $form['text'] = array(
120 '#value' => $text,
121 );
122 $form += taxonomy_manager_add_form(false);
123 return $form;
124 }
125
126
127 $form['taxonomy_manager'] = array(
128 '#type' => 'fieldset',
129 '#title' => $voc->name,
130 '#weight' => 10,
131 '#tree' => TRUE,
132 '#prefix' => '<div id="taxonomytree">',
133 '#suffix' => '</div>',
134 );
135
136 $form['taxonomy_manager']['tree'] = array(
137 '#type' => 'taxonomy_manager_tree',
138 '#vid' => $vid,
139 '#pager' => TRUE,
140 );
141
142 $form['toolbar'] = array(
143 '#type' => 'fieldset',
144 '#title' => t('Toolbar'),
145 '#weight' => -10,
146 );
147
148 $form['toolbar']['up_link'] = array(
149 '#type' => 'markup',
150 '#value' => theme("image", $module_path ."images/go-up.png", "go up", NULL, array('id' => 'go-up')),
151 '#prefix' => '<div id="taxonomy-toolbar"><div id="taxonomy-toolbar-images">',
152 );
153
154 $form['toolbar']['down_link'] = array(
155 '#type' => 'markup',
156 '#value' => theme("image", $module_path ."images/go-down.png", "go down", NULL, array('id' => 'go-down')),
157 );
158
159 $form['toolbar']['delete_confirm'] = array(
160 '#type' => 'markup',
161 '#value' => theme("image", $module_path ."images/list-remove.png", "remove", NULL, array('id' => 'delete-confirm')),
162 );
163
164 $form['toolbar']['add_show'] = array(
165 '#type' => 'markup',
166 '#value' => theme("image", $module_path ."images/list-add.png", "add", NULL, array('id' => 'add-show')),
167 '#suffix' => '</div>',
168 );
169
170 $form['toolbar']['merge_show'] = array(
171 '#type' => 'button',
172 '#value' => t('Merge selected into...'),
173 '#theme' => 'no_submit_button',
174 '#prefix' => '<div id="taxonomy-toolbar-buttons">'
175 );
176
177 $form['toolbar']['move_show'] = array(
178 '#type' => 'button',
179 '#value' => t('Move selected to...'),
180 '#theme' => 'no_submit_button',
181 '#suffix' => '</div>'
182 );
183
184 $form['toolbar']['wrapper'] = array(
185 '#type' => 'markup',
186 '#value' => '<div id="taxonomy-toolbar-throbber"></div></div><div class="clear"></div>',
187 '#weight' => 20,
188 );
189
190 $form += taxonomy_manager_add_form();
191
192 $form += taxonomy_manager_merge_form($voc);
193
194 $form += taxonomy_manager_move_form($voc);
195
196 $form += taxonomy_manager_confirm_delete($voc);
197
198 return $form;
199 }
200
201 /**
202 * function gets called by the taxonomy_manager_tree form type (form_id +_operations)
203 * return an form array with values to show next to every term value
204 */
205 function taxonomy_manager_tree_operations() {
206 $module_path = drupal_get_path('module', 'taxonomy_manager') .'/';
207 $form = array();
208 $form['up'] = array('#value' => theme("image", $module_path ."images/go-up-small.png", "go up", NULL, array('class' => 'term-up')));
209 $form['down'] = array('#value' => theme("image", $module_path ."images/go-down-small.png", "go down", NULL, array('class' => 'term-down')));
210 return $form;
211 }
212
213 /**
214 * confirmation form for deleting selected terms
215 */
216 function taxonomy_manager_confirm_delete($voc) {
217 drupal_add_js(array('hideForm' => array(
218 'show_button' => 'delete-confirm',
219 'hide_button' => 'edit-delete-cancel',
220 'div' => 'del-confirm-form')), 'setting');
221
222 $text = t('Confirmation required! Remember all term specific data will be lost!');
223
224 $form = array();
225
226 $form['delete'] = array(
227 '#type' => 'fieldset',
228 '#tree' => TRUE,
229 '#title' => t('Confirmation'),
230 '#prefix' => '<div id="del-confirm-form">',
231 '#suffix' => '</div>',
232 );
233
234 $form['delete']['text'] = array(
235 '#value' => "<b>". $text ."</b>",
236 );
237
238 if ($voc->hierarchy != 0) {
239 $options = array(
240 'delete_orphans' => t('Delete orphans'),
241 );
242
243 $form['delete']['options'] = array(
244 '#type' => 'checkboxes',
245 '#title' => t('Options'),
246 '#options' => $options,
247 );
248 }
249
250 $form['delete']['delete'] = array(
251 '#type' => 'submit',
252 '#value' => t('Delete'),
253 );
254
255 $form['delete']['cancel'] = array(
256 '#type' => 'button',
257 '#value' => t('Cancel'),
258 '#theme' => 'no_submit_button',
259 );
260
261 return $form;
262 }
263
264 /**
265 * form for adding multiple terms
266 */
267 function taxonomy_manager_add_form($hide_form = TRUE) {
268 if ($hide_form) {
269 drupal_add_js(array('hideForm' => array(
270 'show_button' => 'add-show',
271 'hide_button' => 'edit-add-cancel',
272 'div' => 'add-form')), 'setting');
273 }
274
275 $form = array();
276
277 $form['add'] = array(
278 '#type' => 'fieldset',
279 '#tree' => TRUE,
280 '#title' => t('Add new terms'),
281 '#prefix' => '<div id="add-form">',
282 '#suffix' => '</div>',
283 '#description' => t('Depending on the hierarchy settings, you can set parents (one ore more).
284 Selected terms will get the parents of the new inserted'),
285 );
286
287 for ($i=0; $i<5; $i++) {
288 $form['add']['term'][$i] = array(
289 '#type' => 'textfield',
290 );
291 }
292 $form['add']['add'] = array(
293 '#type' => 'submit',
294 '#value' => t('Add'),
295 );
296
297 $form['add']['cancel'] = array(
298 '#type' => 'button',
299 '#value' => t('Cancel'),
300 '#theme' => 'no_submit_button',
301 );
302
303 return $form;
304
305 }
306
307 /**
308 * form for merging terms
309 */
310 function taxonomy_manager_merge_form($voc) {
311 drupal_add_js(array('hideForm' => array(
312 'show_button' => 'edit-merge-show',
313 'hide_button' => 'edit-merge-cancel',
314 'div' => 'merge-form')), 'setting');
315
316 $form = array();
317
318 $form['merge'] = array(
319 '#type' => 'fieldset',
320 '#tree' => TRUE,
321 '#title' => t('Merging of terms'),
322 '#prefix' => '<div id="merge-form">',
323 '#suffix' => '</div>',
324 );
325
326 $help = t("You can choose either an exisiting term or either typ in a new
327 term, which will be created automatically, as main term (selected terms
328 get merged into this term)");
329
330 $form['merge']['main_term'] = array(
331 '#type' => 'textfield',
332 '#title' => t('Main term'),
333 '#description' => $help,
334 '#required' => FALSE,
335 '#autocomplete_path' => 'taxonomy/autocomplete/'. $voc->vid,
336 );
337
338 $options = array();
339
340 switch ($voc->hierarchy) {
341 //multiple hierarchy
342 case 2:
343 $options['collect_parents'] = t('Collect all parents of merging terms an add it to main term');
344
345 //single hierarchy
346 case 1:
347 $options['collect_children'] = t('Collect all children of merging terms an add it to main term');
348 break;
349 }
350
351 if ($voc->relations) {
352 $options['collect_relations'] = t('Collect all relations of merging terms and add it to main term');
353 }
354
355 if (count($options) > 0) {
356 $form['merge']['options'] = array(
357 '#type' => 'checkboxes',
358 '#title' => t('Options'),
359 '#options' => $options,
360 );
361 }
362
363 $form['merge']['submit'] = array(
364 '#type' => 'submit',
365 '#value' => t('Merge'),
366 );
367
368 $form['merge']['cancel'] = array(
369 '#type' => 'button',
370 '#value' => t('Cancel'),
371 '#theme' => 'no_submit_button',
372 );
373
374 return $form;
375 }
376
377 /**
378 * form for moving terms in hierarchies
379 */
380 function taxonomy_manager_move_form($voc) {
381 drupal_add_js(array('hideForm' => array(
382 'show_button' => 'edit-move-show',
383 'hide_button' => 'edit-move-cancel',
384 'div' => 'move-form')), 'setting');
385
386 $form = array();
387
388 $form['move'] = array(
389 '#type' => 'fieldset',
390 '#tree' => TRUE,
391 '#title' => t('Moving of terms'),
392 '#prefix' => '<div id="move-form">',
393 '#suffix' => '</div>',
394 );
395
396 $help = t("You can choose either an exisiting term or either typ in a new
397 term, which will be created automatically, as parent term (selected terms
398 get children of this term). If multiple parents are allowed, you can seperate
399 more terms in the autocomplete form with ','.
400 If selected terms should have no parent, leave it blank!");
401
402 $form['move']['parents'] = array(
403 '#type' => 'textfield',
404 '#title' => t('Parent term(s)'),
405 '#description' => $help,
406 '#required' => FALSE,
407 '#autocomplete_path' => 'taxonomy/autocomplete/'. $voc->vid,
408 );
409
410 $options = array();
411
412 if ($voc->hierarchy == 2) {
413 $options['keep_old_parents'] = t('Don\'t delete old parents, only add new parents (multiple parents)');
414 }
415
416 if (count($options) > 0) {
417 $form['move']['options'] = array(
418 '#type' => 'checkboxes',
419 '#title' => t('Options'),
420 '#options' => $options,
421 );
422 }
423
424 $form['move']['submit'] = array(
425 '#type' => 'submit',
426 '#value' => t('Move'),
427 );
428
429 $form['move']['cancel'] = array(
430 '#type' => 'button',
431 '#value' => t('Cancel'),
432 '#theme' => 'no_submit_button',
433 );
434
435 return $form;
436 }
437
438
439 /**
440 * validates the form
441 */
442 function taxonomy_manager_form_validate($form_id, $form_values) {
443 $selected_tids = array();
444
445 $selected_tids = $form_values['taxonomy_manager']['tree']['selected_terms'];
446
447 switch ($form_values['op']) {
448 case t('Delete'):
449 if (count($selected_tids) < 1) {
450 form_set_error('delete', t("No terms selected"));
451 }
452
453 break;
454
455 case t('Add'):
456 //check for parents concerning voc settings
457 $voc = taxonomy_get_vocabulary($form_values['vid']);
458
459 if ($voc->hierarchy == 0 && count($selected_tids) > 0) {
460 form_set_error('add', t('This vocabulary doesn\'t provide hierarchies. Please unselect terms in the list before adding new terms.'));
461 }
462 else if ($voc->hierarchy == 1 && count($selected_tids) > 1) {
463 form_set_error('add', t('This vocabulary provides only single hirarchies. Please select only one term in the list (at maximum)'));
464 }
465
466 break;
467
468 case t('Merge'):
469 $main_terms = array();
470 $main_terms = taxonomy_manager_autocomplete_tags_get_tids($form_values['merge']['main_term'], $form_values['vid']);
471 if (count($main_terms) > 1) {
472 form_set_error('merge][main_term', t("The field %title doesn't allows multiple values", array('%title' => "'". t('Main term') ."'")));
473 }
474 if (!is_numeric($main_terms[0]) || $main_terms[0] == 0) {
475 form_set_error('merge][main_term', t("Error reading input from %title", array('%title' => "'". t('Main term') ."'")));
476 }
477 if (count($selected_tids) < 1) {
478 form_set_error('merge', t("No terms for merging selected"));
479 }
480 break;
481
482 case t('Move'):
483 if (count($selected_tids) < 1) {
484 form_set_error('move', t("No terms selected"));
485 }
486 break;
487 }
488 }
489
490 /**
491 * submits the taxonomy manager form
492 */
493 function taxonomy_manager_form_submit($form_id, $form_values) {
494 $selected_tids = array();
495
496 $selected_tids = $form_values['taxonomy_manager']['tree']['selected_terms'];
497
498 switch ($form_values['op']) {
499 case t('Delete'):
500 taxonomy_manager_delete_terms($selected_tids, $form_values['delete']['options']);
501 drupal_set_message("Selected terms deleted");
502 break;
503
504 case t('Add'):
505 foreach ($form_values['add']['term'] as $value) {
506
507 if (!empty($value)) {
508 $term = array();
509 $term['name'] = $value;
510 $term['vid'] = $form_values['vid'];
511 $term['parent'] = $selected_tids;
512 taxonomy_save_term($term);
513 }
514 }
515 drupal_set_message("Terms added");
516 break;
517
518 case t('Merge'):
519 $main_terms = taxonomy_manager_autocomplete_tags_get_tids($form_values['merge']['main_term'], $form_values['vid']);
520 $main_term = $main_terms[0];
521 taxonomy_manager_merge($main_term, $selected_tids, $form_values['merge']['options']);
522 drupal_set_message("Terms merged");
523 break;
524
525 case t('Move'):
526 $parents = taxonomy_manager_autocomplete_tags_get_tids($form_values['move']['parents'], $form_values['vid']);
527 if (count($parents) == 0) $parents[0] = 0; //if empty, delete all parents
528 taxonomy_manager_move($parents, $selected_tids, $form_values['move']['options']);
529
530 drupal_set_message("Terms moved");
531 break;
532
533 }
534
535 drupal_goto('admin/content/taxonomy_manager/'. $form_values['vid']);
536 }
537
538 /**
539 * checks if voc has already terms saved
540 *
541 * @param $vid voc id
542 * @return true, if terms already exists, else false
543 */
544 function _taxonomy_manager_voc_is_empty($vid) {
545 $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));
546 if ($count == 0) return true;
547 return false;
548 }
549
550
551 /**
552 * deletes terms from the database
553 * optional orphans (terms where parent get deleted) can be deleted as well
554 *
555 * (difference to taxonomy_del_term: deletion of orphans optional)
556 *
557 * @param $tids array of term id to delete
558 * @param $options associative array with options
559 * if $options['delete_orphans'] is true, orphans get deleted
560 */
561 function taxonomy_manager_delete_terms($tids, $options = array()) {
562 if (!is_array($tids)) array($tids);
563 while (count($tids) > 0) {
564 $orphans = array();
565 foreach ($tids as $tid) {
566 if ($children = taxonomy_get_children($tid)) {
567 foreach ($children as $child) {
568 $parents = taxonomy_get_parents($child->tid);
569 if ($options['delete_orphans']) {
570 if (count($parents) == 1) {
571 $orphans[] = $child->tid;
572 }
573 }
574 else {
575 db_query("DELETE FROM {term_hierarchy} WHERE tid = %d AND parent = %d", $child->tid, $tid);
576 if (count($parents) == 1) {
577 if (!db_result(db_query("SELECT COUNT(*) FROM {term_hierarchy} WHERE tid = %d AND parent = 0", $child->tid))) {
578 db_query("INSERT INTO {term_hierarchy} (parent, tid) VALUES(0, %d)", $child->tid);
579 }
580 }
581 }
582 }
583 }
584
585 db_query('DELETE FROM {term_data} WHERE tid = %d', $tid);
586 db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $tid);
587 db_query('DELETE FROM {term_relation} WHERE tid1 = %d OR tid2 = %d', $tid, $tid);
588 db_query('DELETE FROM {term_synonym} WHERE tid = %d', $tid);
589 db_query('DELETE FROM {term_node} WHERE tid = %d', $tid);
590
591 $term = (array) taxonomy_get_term($tid);
592 module_invoke_all('taxonomy', 'delete', 'term', $term);
593 $tids = $orphans;
594 }
595 }
596 }
597
598
599 /**
600 * moves terms in hierarchies to other parents
601 *
602 * @param $parents
603 * array of parent term ids to where children can be moved
604 * array should only contain more parents if multi hiearchy enabled
605 * if array contains 0, terms get placed to first (root) level
606 * @param $children
607 * array of term ids to move
608 * @param $options
609 * array of additional options for moving
610 * 'keep_old_parents': if true, exisiting parents doesn't get deleted (only possible with multi hierarchies)
611 */
612 function taxonomy_manager_move($parents, $children, $options = array()) {
613 if (!is_array($parents)) array($parents);
614
615 foreach ($children as $child) {
616 if (!$options['keep_old_parents']) {
617 db_query("DELETE FROM {term_hierarchy} WHERE tid = %d", $child);
618 }
619 foreach ($parents as $parent) {
620 db_query("INSERT INTO {term_hierarchy} SET parent = %d, tid = %d", $parent, $child, $child);
621 }
622 }
623 }
624
625 /**
626 * merges terms into another term (main term), all merged term get added
627 * to the main term as synonyms.
628 * term_node relations are updated automatically (node with one of merging terms gets main term assigned)
629 * after all opterions are done (adding of hierarchies, relations is optional) merging
630 * terms get deleted
631 *
632 * @param $main_term
633 * id of term where other terms get merged into
634 * @param $merging_terms
635 * array of term ids, which get merged into main term and afterwards deleted
636 * @param $options
637 * array with additional options, possible values:
638 * 'collect_parents': if true, all parents of merging terms get added to main term (only possible with multi hierarchies)
639 * 'collect_children': if true, all children of merging terms get added to main term
640 * 'collect_relations': if true, all relations of merging terms are transfered to main term
641 */
642 function taxonomy_manager_merge($main_term, $merging_terms, $options = array()) {
643 //TODO: add hook, so that other modules can consider changes
644 foreach ($merging_terms as $merge_term) {
645
646 //update node-relations
647 $sql = db_query("SELECT nid FROM {term_node} WHERE tid = %d", $merge_term);
648 while ($obj = db_fetch_object($sql)) {
649 $nid = $obj->nid;
650 db_query("DELETE FROM {term_node} WHERE tid = %d AND nid = %d", $merge_term, $nid);
651 if (!db_result(db_query("SELECT COUNT(*) FROM {term_node} WHERE tid = %d AND nid = %d", $main_term, $nid))) {
652 db_query("INSERT INTO {term_node} (tid, nid) VALUES (%d, %d)", $main_term, $nid);
653 }
654 }
655
656 if ($options['collect_parents']) {
657 $parents = taxonomy_get_parents($merge_term);
658 foreach ($parents as $parent_tid => $parent_term) {
659 if (!db_result(db_query("SELECT COUNT(*) FROM {term_hierarchy} WHERE tid = %d AND parent = %d", $main_term, $parent_tid))) {
660 db_query("INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)", $main_term, $parent_tid);
661 }
662 }
663 }
664
665 if ($options['collect_children']) {
666 $children = taxonomy_get_children($merge_term);
667 foreach ($children as $child_tid => $child_term) {
668 if (!db_result(db_query("SELECT COUNT(*) FROM {term_hierarchy} WHERE tid = %d AND parent = %d", $child_tid, $main_term))) {
669 db_query("INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)", $child_tid, $main_term);
670 }
671 }
672 }
673
674 if ($options['collect_relations']) {
675 $relations = taxonomy_get_related($merge_term);
676 foreach ($relations as $related_tid => $relation) {
677 if ($relation->tid1 == $merge_term) {
678 if (!db_result(db_query("SELECT COUNT(*) FROM {term_relation} WHERE tid1 = %d AND tid2 = %d", $main_term, $related_tid))) {
679 db_query("INSERT INTO {term_relation} (tid1, tid2) VALUES (%d, %d)", $main_term, $related_tid);
680 }
681 }
682 else if ($relation->tid2 == $merge_term) {
683 if (!db_result(db_query("SELECT COUNT(*) FROM {term_relation} WHERE tid2 = %d AND tid1 = %d", $main_term, $related_tid))) {
684 db_query("INSERT INTO {term_relation} (tid2, tid1) VALUES (%d, %d)", $main_term, $related_tid);
685 }
686 }
687 }
688 }
689
690 //save merged term as synonym
691 $term = taxonomy_get_term($merge_term);
692 if (!db_result(db_query("SELECT COUNT(*) FROM {term_synonym} WHERE tid = %d AND name = '%s'", $main_term, $term->name))) {
693 db_query("INSERT INTO {term_synonym} (tid, name) VALUES (%d, '%s')", $main_term, $term->name);
694 }
695
696 taxonomy_manager_delete_terms(array($merge_term));
697 }
698 }
699
700 /**
701 * helper function for getting out of term ids from autocomplete fields
702 * non-exsiting terms get inserted autmatically
703 * (part of taxonomy_node_save)
704 *
705 * @param $typed_input input string of form field
706 * @param $vid vocabulary id
707 * @return array of term ids
708 */
709 function taxonomy_manager_autocomplete_tags_get_tids($typed_input, $vid) {
710 $tids = array();
711
712 $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x';
713 preg_match_all($regexp, $typed_input, $matches);
714 $typed_terms = array_unique($matches[1]);
715
716 foreach ($typed_terms as $typed_term) {
717 $typed_term = str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1', $typed_term));
718 $typed_term = trim($typed_term);
719 if ($typed_term == "") { continue; }
720
721 $possibilities = taxonomy_get_term_by_name($typed_term);
722 $typed_term_tid = NULL; // tid match if any.
723 foreach ($possibilities as $possibility) {
724 if ($possibility->vid == $vid) {
725 $typed_term_tid = $possibility->tid;
726 $tids[] = $typed_term_tid;
727 }
728 }
729
730 if (!$typed_term_tid) {
731 $edit = array('vid' => $vid, 'name' => $typed_term);
732 $status = taxonomy_save_term($edit);
733 $typed_term_tid = $edit['tid'];
734 $tids[] = $typed_term_tid;
735 }
736 }
737 return $tids;
738 }
739
740 /**
741 * callback for updating weights
742 * data send through AJAX, $_POST
743 * $_POST[$tid] => $weight
744 *
745 */
746 function taxonomy_manager_update_weights($vid) {
747 $weights = $_POST;
748 if (is_array($weights)) {
749 foreach ($weights as $tid => $weight) {
750 if (is_numeric($tid) && is_numeric($weight)) {
751 if(_taxonomy_manager_tree_term_valid($tid, $vid)) {
752 db_query("UPDATE {term_data} SET weight = %d WHERE tid = %d", $weight, $tid);
753 }
754 }
755 }
756 }
757 exit();
758 }
759
760
761 //if weightening in javascript gets slow, a way of calculating it on server side...
762
763 /*function taxonomy_manager_update_weights($term_to_swap, $up, $parent = 0) {
764 $vid = db_result(db_query("SELECT vid FROM {term_data} WHERE tid = %d", $term_to_swap));
765 $siblings = taxonomy_get_tree($vid, $parent, -1, 1);
766 $found = false;
767 for ($i=0; !$found && $i<count($siblings); $i++) {
768 if ($term_to_swap == $siblings[$i]->tid) {
769 $position = $i;
770 $found = true;
771 }
772 }
773 if ($up) {
774 $up_term = $siblings[$position];
775 $down_term = $siblings[$position+1];
776 $up_term->position = $position;
777 $down_term->position = $position+1;
778 }
779 else {
780 $up_term = $siblings[$position-1];
781 $down_term = $siblings[$position];
782 $up_term->position = $position-1;
783 $down_term->position = $position;
784 }
785
786 $weights = taoxnomy_manager_swap_weights($up_term, $down_term, $siblings);
787
788 foreach ($weights as $tid => $weight) {
789 db_query("UPDATE {term_data} SET weight = %d WHERE tid = %d", $weight, $tid);
790 }
791 }
792
793 function taoxnomy_manager_swap_weights($up_term, $down_term, $siblings) {
794 $weights = array();
795
796 if ($up_term->weight == $down_term->weight) {
797 $weights[$up_term->tid] = $up_term->weight-1;
798 }
799 else {
800 $weights[$up_term->tid] = $down_term->weight;
801 $weights[$down_term->tid] = $up_term->weight;
802 }
803
804 if ($siblings[$up_term->position-1]->weight == $up_term->weight) {
805 for ($i=0; $i<$up_term->position; $i++) {
806 $weights[$siblings[$i]->tid] = $siblings[$i]->weight-1;
807 }
808 }
809 else if ($siblings[$down_term->position+1]->weight == $down_term->weight) {
810 for ($i=$down_term->position; $i<count($siblings); $i++) {
811 $weights[$siblings[$i]->tid] = $siblings[$i]->weight+1;
812 }
813 }
814
815 return $weights;
816 }*/
817
818 /**
819 * theme function for taxonomy manager form
820 */
821 function theme_taxonomy_manager_form($form) {
822 $pager = theme('pager', NULL, TAXONOMY_MANAGER_TREE_PAGE_SIZE, 0);
823 $tree = drupal_render($form['taxonomy_manager']);
824 $top = drupal_render($form);
825 $output = $top . $pager . $tree;
826
827 return $output;
828 }
829
830 /**
831 * themes a real button form type (no form submit)
832 */
833 function theme_no_submit_button($element) {
834 // Make sure not to overwrite classes.
835 if (isset($element['#attributes']['class'])) {
836 $element['#attributes']['class'] = 'form-'. $element['#button_type'] .' '. $element['#attributes']['class'];
837 }
838 else {
839 $element['#attributes']['class'] = 'form-'. $element['#button_type'];
840 }
841
842 return '<input type="button" '. (empty($element['#name']) ? '' : 'name="'. $element['#name'] .'" ') .'id="'. $element['#id'].'" value="'. check_plain($element['#value']) .'" '. drupal_attributes($element['#attributes']) ." />\n";
843 }
844
845
846 /******************************************
847 * TAXONOMY TREE FORM ELEMENT DEFINITION
848 *
849 * how to use:
850 * $form['name'] = array(
851 * '#type' => 'taxonomy_manager_tree',
852 * '#vid' => $vid,
853 * );
854 *
855 * additional parameter:
856 * #pager: TRUE / FALSE,
857 * whether to use pagers (drupal pager, load of nested children, load of siblings)
858 * or to load the whole tree on page generation
859 * #parent: only children on this parent will be loaded
860 * #siblings_page: current page for loading pf next siblings, internal use
861 *
862 * defining term operations:
863 * to add values (operations,..) to each term, add a function, which return a form array
864 * 'tree_form_id'_operations
865 *
866 * how to retrieve selected values:
867 * selected terms ids are available in validate / submit function in
868 * $form_values['name']['selected_terms'];
869 *
870 ******************************************/
871
872 /**
873 * Implementation of hook_elements
874 */
875 function taxonomy_manager_elements() {
876 $type['taxonomy_manager_tree'] = array(
877 '#input' => TRUE,
878 '#process' => array('taxonomy_manager_tree_process_elements' => array()),
879 '#tree' => TRUE,
880 );
881
882 return $type;
883 }
884
885 /**
886 * Processes the tree form element
887 *
888 * @param $element
889 * @return the tree element
890 */
891 function taxonomy_manager_tree_process_elements($element) {
892 $module_path = drupal_get_path('module', 'taxonomy_manager') .'/';
893 $id = form_clean_id(implode('-', $element['#parents']));
894
895 if (!$element['#parent'] && !$element['#siblings_page']) {
896 drupal_add_css($module_path .'css/taxonomy_manager.css');
897 drupal_add_js($module_path .'js/tree.js');
898 drupal_add_js($module_path .'js/childForm.js');
899 drupal_add_js($module_path .'js/siblingsForm.js');
900
901 drupal_add_js(array('siblingsForm' => array('url' => url('admin/content/taxonomy_manager/siblingsform'), 'modulePath' => $module_path)), 'setting');
902 drupal_add_js(array('childForm' => array('url' => url('admin/content/taxonomy_manager/childform'), 'modulePath' => $module_path)), 'setting');
903 drupal_add_js(array('taxonomy_manager' => array('modulePath' => $module_path)), 'setting');
904 drupal_add_js(array('taxonomytree' => array('id' => $id)), 'setting');
905 }
906
907 if (!is_array($element['#operations'])) {
908 $opertions_callback = implode('_', $element['#parents']) .'_operations';
909 if (function_exists($opertions_callback)) {
910 $element['#operations'] = $opertions_callback();
911 }
912 }
913
914 $vid = $element['#vid'];
915 $tree = _taxonomy_manager_tree_get_item($element['#vid'], $element['#parent'], $element['#pager'], $element['#siblings_page']);
916
917 if ($element['#pager'] && !($element['#parent'] || $element['#siblings_page'])) {
918 $element['pager'] = array('#value' => theme('pager', NULL, TAXONOMY_MANAGER_TREE_PAGE_SIZE));
919 }
920 $element['#tree'] = TRUE;
921 $element['#id'] = $id;
922 $element['#validate'] = array('taxonomy_manager_tree_validate' => array());
923 $element['selected_terms'] = array('#type' => 'value', '#value' => array());
924
925 taxonomy_manager_tree_build_form($index = 0, $tree, $element['#elements'], $element, $element['#parents'], !$element['#pager'], $element['#siblings_page']);
926
927 return $element;
928 }
929
930 /**
931 * loads tree with terms (depending on various settings)
932 *
933 * @param $vid
934 * @param $parent
935 * @param $pager
936 * @param $siblings_page
937 * @return array with term elements
938 */
939 function _taxonomy_manager_tree_get_item($vid, $parent = 0, $pager = FALSE, $siblings_page = 0) {
940 $tree = array();
941 if ($pager) {
942 if ($parent || $siblings_page) {
943 $start = ($siblings_page-1) * TAXONOMY_MANAGER_TREE_PAGE_SIZE;
944 $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);
945 }
946 else {
947 $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));
948 }
949
950 while ($term = db_fetch_object($result)) {
951 $tree[] = $term;
952 }
953 }
954 else {
955 $tree = taxonomy_get_tree($vid, $parent);
956 }
957
958 return $tree;
959 }
960
961 /**
962 * recursive function for building nested form array
963 * with checkboxes and weight forms for each term
964 *
965 * nested form array are allways appended to parent-form['children']
966 *
967 * @param $index current index in tree, start with 0
968 * @param $tree of terms (generated by taxonomy_get_tree)
969 * @return a form array
970 */
971 function taxonomy_manager_tree_build_form(&$index, $tree, &$form, $root_form, $parents = array(), $build_subtrees = TRUE, $page = 0) {
972
973 $current_depth = $tree[$index]->depth;
974 while ($index < count($tree) && $tree[$index]->depth >= $current_depth) {
975 $term = $tree[$index];
976
977 $attributes = array();
978
979 $this_parents = $parents;
980 $this_parents[] = $term->tid;
981
982 $form[$term->tid]['checkbox'] = array('#type' => 'checkbox', '#title' => $term->name, '#return_value' => 1);
983 $form[$term->tid]['weight'] = array('#type' => 'hidden', '#value' => $term->weight, '#attributes' => array('class' => 'weight-form'));
984 $form[$term->tid]['tid'] = array('#type' => 'hidden', '#value' => $term->tid, '#attributes' => array('class' => 'term-id'));
985
986 if (is_array($root_form['#operations'])) {
987 $form[$term->tid]['operations'] = $root_form['#operations'];
988 }
989
990 if ($page) {
991 if ($index == (TAXONOMY_MANAGER_TREE_PAGE_SIZE - 1)) {
992 $module_path = drupal_get_path('module', 'taxonomy_manager') .'/';
993 $form[$term->tid]['has-more-siblings'] = array(
994 '#type' => 'markup',
995 '#value' => theme("image", $module_path ."images/2downarrow.png", "more", NULL, array('class' => 'load-siblings')),
996 );
997 $form[$term->tid]['page'] = array(
998 '#type' => 'hidden',
999 '#value' => $page,
1000 '#attributes' => array('class' => 'page'),
1001 );
1002 $next_count = _taxonomy_manager_tree_get_next_siblings_count($term->vid, $page, $root_form['#parent']);
1003 $form[$term->tid]['next_count'] = array('#value' => $next_count);
1004 $form[$term->tid]['#attributes']['class'] .= 'has-more-siblings ';
1005 }
1006 }
1007
1008 _taxonomy_manager_tree_element_set_params($this_parents, $form[$term->tid]);
1009
1010 $index++;
1011
1012 if ($build_subtrees) {
1013 if ($tree[$index]->depth > $current_depth) {
1014 $form[$term->tid]['#attributes']['class'] .= _taxonomy_manager_tree_term_get_class($index-1, $tree);
1015 taxonomy_manager_tree_build_form($index, $tree, $form[$term->tid]['children'], $root_form, array_merge($this_parents, array('children')));
1016 }
1017 else {
1018 if ((count($tree)-1 == $index-1) || ($tree[$index]->depth < $current_depth)) {
1019 $form[$term->tid]['#attributes']['class'] .= 'last ';
1020 }
1021 }
1022 }
1023 else {
1024 if (_taxonomy_manager_tree_term_has_children($term->tid)) {
1025 $form[$term->tid]['#attributes']['class'] .= 'has-children ';
1026 if ($index-1 == count($tree)-1) {
1027 $form[$term->tid]['#attributes']['class'] .= 'lastExpandable ';
1028 }
1029 else {
1030 $form[$term->tid]['#attributes']['class'] .= 'expandable ';
1031 }
1032 }
1033 else {
1034 if ($index-1 == count($tree)-1) {
1035 $form[$term->tid]['#attributes']['class'] .= 'last ';
1036 }
1037 }
1038 }
1039 }
1040 }
1041
1042 /**
1043 * adds #id and #name to all form elements
1044 *
1045 * @param $parents
1046 * @param $form
1047 */
1048 function _taxonomy_manager_tree_element_set_params($parents, &$form) {
1049 foreach (element_children($form) as $field_name) {
1050 $field_parents = array_merge($parents, array($field_name));
1051 $form[$field_name]['#tree'] = TRUE;
1052 $form[$field_name]['#post'] = array();
1053 $form[$field_name]['#parents'] = $field_parents;
1054 $form[$field_name]['#id'] = form_clean_id('edit-'. implode('-', $field_parents));
1055 $form[$field_name]['#name'] = array_shift($field_parents) .'['. implode('][', $field_parents) .']';
1056 }
1057 }
1058
1059 /**
1060 * calculates class type (expandable, lastExpandable) for current element
1061 *
1062 * @param $current_index in tree array
1063 * @param $tree array with terms
1064 * @return expandable or lastExpandable
1065 */
1066 function _taxonomy_manager_tree_term_get_class($current_index, $tree) {
1067 $class = '';
1068 $term = $tree[$current_index];
1069 $next = $tree[++$current_index];
1070 $children = false;
1071 while ($next->depth > $term->depth) {
1072 $children = true;
1073 $next = $tree[++$current_index];
1074 }
1075
1076 if ($next->depth == $term->depth) {
1077 $class = 'expandable ';
1078 }
1079 else {
1080 $class = 'lastExpandable ';
1081 }
1082 return $class;
1083 }
1084
1085 /**
1086 * checks if a term has children
1087 *
1088 * @param $tid
1089 * @return true, if term has children, else false
1090 */
1091 function _taxonomy_manager_tree_term_has_children($tid) {
1092 $count = db_result(db_query("SELECT COUNT(tid) FROM {term_hierarchy} WHERE parent = %d", $tid));
1093 if ($count == 0) {
1094 return false;
1095 }
1096 return true;
1097 }
1098
1099 /**
1100 * calculates number of next siblings if using paging
1101 *
1102 * @param $vid
1103 * @param $page
1104 * @param $parent
1105 * @return next page size
1106 */
1107 function _taxonomy_manager_tree_get_next_siblings_count($vid, $page, $parent = 0) {
1108 $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));
1109 $current_count = TAXONOMY_MANAGER_TREE_PAGE_SIZE * $page;
1110 $diff = $count - $current_count;
1111
1112 if ($diff > TAXONOMY_MANAGER_TREE_PAGE_SIZE) {
1113 $diff = TAXONOMY_MANAGER_TREE_PAGE_SIZE;
1114 }
1115
1116 return $diff;
1117 }
1118
1119 /**
1120 * callback for generating and rendering nested child forms (AHAH)
1121 *
1122 * @param $tree_id
1123 * @param $parent term id of parent, that is expanded and of which children have to be loaded
1124 */
1125 function taxonomy_manager_tree_build_child_form($tree_id, $parent) {
1126 $vid = db_result(db_query("SELECT vid FROM {term_data} WHERE tid = %d", $parent));
1127
1128 $child_form = array(
1129 '#type' => 'taxonomy_manager_tree',
1130 '#vid' => $vid,
1131 '#parent' => $parent,
1132 '#pager' => TRUE,
1133 '#siblings_page' => 1,
1134 );
1135
1136 $opertions_callback = str_replace('-', '_', $tree_id) .'_operations';
1137 if (function_exists($opertions_callback)) {
1138 $child_form['#operations'] = $opertions_callback();
1139 }
1140
1141 _taxonomy_manager_tree_sub_forms_set_parents($tree_id, $parent, $child_form);
1142
1143 $child_form = form_builder('taxonomy_manager_form', $child_form);
1144
1145 print drupal_render($child_form);
1146
1147 exit();
1148 }
1149
1150 /**
1151 * callback for generating and rendering next siblings terms form (AHAH)
1152 *
1153 * @param $tree_id
1154 * @param $page current page
1155 * @param $prev_tid last sibling, that appears
1156 * @param $parent if in hierarchies, parent id
1157 */
1158 function taxonomy_manager_tree_build_siblings_form($tree_id, $page, $prev_tid, $parent = 0) {
1159 $vid = db_result(db_query("SELECT vid FROM {term_data} WHERE tid = %d", $prev_tid));
1160
1161 $siblings_form = array(
1162 '#type' => 'taxonomy_manager_tree',
1163 '#vid' => $vid,
1164 '#parent' => $parent,
1165 '#pager' => TRUE,
1166 '#siblings_page' => $page+1,
1167 );
1168
1169 $opertions_callback = str_replace('-', '_', $tree_id) .'_operations';
1170 if (function_exists($opertions_callback)) {
1171 $child_form['#operations'] = $opertions_callback();
1172 }
1173
1174 _taxonomy_manager_tree_sub_forms_set_parents($tree_id, $parent, $siblings_form);
1175