hs_taxonomy.module

Tracking 5.x-3.x branch
  1. drupal
    1. 5 contributions/hierarchical_select/modules/hs_taxonomy.module
    2. 6 contributions/hierarchical_select/modules/hs_taxonomy.module

Implementation of the Hierarchical Select API for the Taxonomy module.

Functions & methods

NameDescription
hierarchical_select_taxonomy_form_vocabulary_submitAdditional submit callback for the taxonomy_form_vocabulary form.
hierarchical_select_taxonomy_form_vocabulary_validateAdditional validate callback for the taxonomy_form_vocabulary form.
hs_taxonomy_form_alterImplementation of hook_form_alter().
hs_taxonomy_get_parents_allAlternative version of taxonomy_get_parents_all(): instead of using all parents of a term (i.e. when multiple parents are being used), only the first is kept.
hs_taxonomy_hierarchical_select_childrenImplementation of hook_hierarchical_select_children().
hs_taxonomy_hierarchical_select_config_infoImplementation of hook_hierarchical_select_config_info().
hs_taxonomy_hierarchical_select_create_itemImplementation of hook_hierarchical_select_create_item().
hs_taxonomy_hierarchical_select_entity_countImplementation of hook_hierarchical_select_entity_count().
hs_taxonomy_hierarchical_select_implementation_infoImplementation of hook_hierarchical_select_implementation_info().
hs_taxonomy_hierarchical_select_item_get_labelImplementation of hook_hierarchical_select_item_get_label().
hs_taxonomy_hierarchical_select_lineageImplementation of hook_hierarchical_select_lineage().
hs_taxonomy_hierarchical_select_paramsImplementation of hook_hierarchical_select_params().
hs_taxonomy_hierarchical_select_root_levelImplementation of hook_hierarchical_select_root_level().
hs_taxonomy_hierarchical_select_update_form_itemUpdate a taxonomy select to become a hierarchical_select type form item.
hs_taxonomy_hierarchical_select_valid_itemImplementation of hook_hierarchical_select_valid_item().
hs_taxonomy_term_count_nodesDrupal core's taxonomy_term_count_nodes() is buggy. See http://drupal.org/node/144969#comment-843000.
hs_taxonomy_token_listImplementation of hook_token_list().
hs_taxonomy_token_valuesImplementation of hook_token_values().
_hs_taxonomy_hierarchical_select_get_depthGet the depth of a vocabulary's tree.
_hs_taxonomy_hierarchical_select_get_treeDrupal core's taxonomy_get_tree() doesn't allow us to reset the cached trees, which obviously causes problems when you create new items between two calls to it.
_hs_taxonomy_hierarchical_select_terms_to_optionsTransform an array of terms into an associative array of options, for use in a select form item.
_hs_taxonomy_token_termpath_for_vidHelper function for hs_taxonomy_token_values().

File

View source
  1. <?php
  2. /**
  3. * @file
  4. * Implementation of the Hierarchical Select API for the Taxonomy module.
  5. */
  6. //----------------------------------------------------------------------------
  7. // Drupal core hooks.
  8. /**
  9. * Implementation of hook_form_alter().
  10. */
  11. function hs_taxonomy_form_alter($form_id, &$form) {
  12. // Add per-vocabulary settings for Hierarchical Select.
  13. if ($form_id == 'taxonomy_form_vocabulary') {
  14. require_once(drupal_get_path('module', 'hierarchical_select') .'/includes/common.inc');
  15. $vid = (isset($form['vid'])) ? $form['vid']['#value'] : NULL;
  16. // Don't add the per-vocabulary settings for Hierarchical Select when the
  17. // vocabulary still needs to be created.
  18. if ($vid == NULL) {
  19. return;
  20. }
  21. if (variable_get("taxonomy_hierarchical_select_$vid", 0)) {
  22. $form['tags']['#attributes']['disabled'] = TRUE;
  23. $form['tags']['#description'] = t(
  24. "This setting is irrelevant when you're using Hierarchical Select.
  25. <br />Use Hierarchical Select's %editability-settings instead.",
  26. array('%editability-settings' => t('Editability settings'))
  27. );
  28. $form['multiple']['#attributes']['disabled'] = TRUE;
  29. $form['multiple']['#description'] = t(
  30. "This setting is managed by the Hierarchical Select configuration, by
  31. the %enable-dropbox setting.",
  32. array('%enable-dropbox' => t('Enable the dropbox'))
  33. );
  34. }
  35. $split = array_search('weight', array_keys($form)) + 1;
  36. $first_part = array_slice($form, 0, $split);
  37. $second_part = array_slice($form, $split);
  38. $form = $first_part;
  39. $form['hierarchical_select_status'] = array(
  40. '#type' => 'checkbox',
  41. '#title' => '<strong>'. t('Use the Hierarchical Select form element for this vocabulary.') .'</strong>',
  42. '#default_value' => variable_get("taxonomy_hierarchical_select_$vid", 0),
  43. '#description' => t(
  44. 'When checked, the %free_tagging and %multiple_values settings will
  45. be managed by the Hierarchical Select configuration.',
  46. array(
  47. '%free_tagging' => t('Free tagging'),
  48. '%multiple_values' => t('Multiple values'),
  49. )
  50. ),
  51. );
  52. // Add the Hierarchical Select config form.
  53. $module = 'hs_taxonomy';
  54. $params = array(
  55. 'vid' => $vid,
  56. 'exclude_tid' => NULL,
  57. 'root_term' => NULL,
  58. );
  59. $config_id = "taxonomy-$vid";
  60. $vocabulary = taxonomy_get_vocabulary($vid);
  61. $defaults = array(
  62. // Enable the save_lineage setting by default if the multiple parents
  63. // vocabulary option is enabled.
  64. 'save_lineage' => (int) ($vocabulary->hierarchy == 2),
  65. 'editability' => array(
  66. 'max_levels' => _hs_taxonomy_hierarchical_select_get_depth($vid),
  67. ),
  68. );
  69. $strings = array(
  70. 'hierarchy' => t('vocabulary'),
  71. 'hierarchies' => t('vocabularies'),
  72. 'item' => t('term'),
  73. 'items' => t('terms'),
  74. 'item_type' => t('term type'),
  75. 'entity' => t('node'),
  76. 'entities' => t('nodes'),
  77. );
  78. $max_hierarchy_depth = _hs_taxonomy_hierarchical_select_get_depth($vid);
  79. $preview_is_required = $vocabulary->required;
  80. $form['hierarchical_select'] = hierarchical_select_common_config_form($module, $params, $config_id, $defaults, $strings, $max_hierarchy_depth, $preview_is_required);
  81. // The forum selection requires that only the deepest term is saved!
  82. // See http://drupal.org/node/241766#comment-808464.
  83. if ($vid == variable_get('forum_nav_vocabulary', -1)) {
  84. $form['hierarchical_select']['save_lineage']['#value'] = 0;
  85. $form['hierarchical_select']['save_lineage']['#attributes'] = array('disabled' => 'disabled');
  86. $form['hierarchical_select']['save_lineage']['#description'] .= '<br />'. t(
  87. 'This is the vocabulary that will be used for forum navigation and it
  88. <strong>always</strong> requires the %dont_save_lineage setting to be
  89. set!',
  90. array('%dont_save_lineage' => t('Save only the deepest term'))
  91. );
  92. }
  93. // Add the the submit handler for the Hierarchical Select config form.
  94. $parents = array('hierarchical_select');
  95. $form['#submit']['hierarchical_select_common_config_form_submit'] = array($parents);
  96. // Add a validate callback to override the freetagging and multiple select
  97. // settings if necessary.
  98. $form['#validate']['hierarchical_select_taxonomy_form_vocabulary_validate'] = array();
  99. $form['#submit']['hierarchical_select_taxonomy_form_vocabulary_submit'] = array();
  100. // The original #submit callback(s) has/have to be executed afterwards.
  101. $form['#submit'] = array_merge($form['#submit'], $second_part['#submit']);
  102. $form += $second_part;
  103. }
  104. // Change the term selection of nodes. Only affects hierarchical
  105. // vocabularies.
  106. else if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id && is_array($form['taxonomy'])) {
  107. foreach ($form['taxonomy'] as $vid => $form_item) {
  108. // Only apply Hierarchical Select if it's enabled for this vocabulary.
  109. if (is_numeric($vid) && variable_get("taxonomy_hierarchical_select_$vid", 0)) {
  110. require_once(drupal_get_path('module', 'hierarchical_select') .'/includes/common.inc');
  111. // #size is set as soon as save_lineage or the dropbox is enabled,
  112. // because then "multiple select" is enabled. Unset #size.
  113. unset($form['taxonomy'][$vid]['#size']);
  114. $form['taxonomy'][$vid]['#type'] = 'hierarchical_select';
  115. $form['taxonomy'][$vid]['#config'] = array(
  116. 'module' => 'hs_taxonomy',
  117. 'params' => array(
  118. 'vid' => $vid,
  119. 'exclude_tid' => NULL,
  120. 'root_term' => NULL,
  121. ),
  122. );
  123. hs_taxonomy_hierarchical_select_update_form_item($form['taxonomy'][$vid], $vid);
  124. }
  125. }
  126. }
  127. // The taxonomy term form.
  128. else if ($form_id == 'taxonomy_form_term') {
  129. unset($form['parent']['#options']);
  130. unset($form['parent']['#theme']);
  131. $form['parent']['#type'] = 'hierarchical_select';
  132. $form['parent']['#config'] = array(
  133. 'module' => 'hs_taxonomy',
  134. 'enforce_deepest' => 0,
  135. 'save_lineage' => 0,
  136. 'params' => array(
  137. 'vid' => $form['vid']['#value'],
  138. 'exclude_tid' => $form['tid']['#value'],
  139. 'root_term' => TRUE,
  140. ),
  141. );
  142. // When 'multiple parents' are enabled, we should support that as well!
  143. if ($form['parent']['#multiple']) {
  144. unset($form['parent']['#size']);
  145. $form['parent']['#config']['dropbox']['status'] = 1;
  146. }
  147. }
  148. // The forum 'container' and 'forum' forms.
  149. else if ($form_id == 'forum_form_forum' || $form_id == 'forum_form_container') {
  150. unset($form['parent'][0]['#options']);
  151. unset($form['parent'][0]['#theme']);
  152. unset($form['parent'][0]['#required']);
  153. $form['parent'][0]['#type'] = 'hierarchical_select';
  154. $form['parent'][0]['#config'] = array(
  155. 'module' => 'hs_taxonomy',
  156. 'enforce_deepest' => 0,
  157. 'save_lineage' => 0,
  158. 'params' => array(
  159. 'vid' => $form['vid']['#value'],
  160. 'exclude_tid' => $form['tid']['#value'],
  161. 'root_term' => TRUE,
  162. ),
  163. );
  164. }
  165. }
  166. //----------------------------------------------------------------------------
  167. // Hierarchical Select hooks.
  168. /**
  169. * Implementation of hook_hierarchical_select_params().
  170. */
  171. function hs_taxonomy_hierarchical_select_params() {
  172. $params = array(
  173. 'vid',
  174. 'exclude_tid', // Allows a term to be excluded (necessary for the taxonomy_form_term form).
  175. 'root_term', // Displays a fake "<root>" term in the root level (necessary for the taxonomy_form-term form).
  176. );
  177. return $params;
  178. }
  179. /**
  180. * Implementation of hook_hierarchical_select_root_level().
  181. */
  182. function hs_taxonomy_hierarchical_select_root_level($params) {
  183. $terms = _hs_taxonomy_hierarchical_select_get_tree($params['vid'], 0, -1, 1);
  184. // If the root_term parameter is enabled, then prepend a fake "<root>" term.
  185. if ($params['root_term'] === TRUE) {
  186. $root_term = new StdClass();
  187. $root_term->tid = 0;
  188. $root_term->name = '<'. t('root') .'>';
  189. $terms = array_merge(array($root_term), $terms);
  190. }
  191. // Unset the term that's being excluded, if it is among the terms.
  192. if (isset($params['exclude_tid'])) {
  193. foreach ($terms as $key => $term) {
  194. if ($term->tid == $params['exclude_tid']) {
  195. unset($terms[$key]);
  196. }
  197. }
  198. }
  199. return _hs_taxonomy_hierarchical_select_terms_to_options($terms);
  200. }
  201. /**
  202. * Implementation of hook_hierarchical_select_children().
  203. */
  204. function hs_taxonomy_hierarchical_select_children($parent, $params) {
  205. if ($params['root_term'] && $parent == 0) {
  206. return array();
  207. }
  208. $terms = taxonomy_get_children($parent, $params['vid']);
  209. // Unset the term that's being excluded, if it is among the children.
  210. unset($terms[$params['exclude_tid']]);
  211. return _hs_taxonomy_hierarchical_select_terms_to_options($terms);
  212. }
  213. /**
  214. * Implementation of hook_hierarchical_select_lineage().
  215. */
  216. function hs_taxonomy_hierarchical_select_lineage($item, $params) {
  217. $lineage = array();
  218. if ($params['root_term'] && $item == 0) {
  219. return array(0);
  220. }
  221. $terms = array_reverse(hs_taxonomy_get_parents_all($item));
  222. foreach ($terms as $term) {
  223. $lineage[] = $term->tid;
  224. }
  225. return $lineage;
  226. }
  227. /**
  228. * Alternative version of taxonomy_get_parents_all(): instead of using all
  229. * parents of a term (i.e. when multiple parents are being used), only the
  230. * first is kept.
  231. */
  232. function hs_taxonomy_get_parents_all($tid) {
  233. $parents = array();
  234. if ($tid) {
  235. $parents[] = taxonomy_get_term($tid);
  236. $n = 0;
  237. while ($parent = taxonomy_get_parents($parents[$n]->tid)) {
  238. $parents = array_merge($parents, array(reset($parent)));
  239. $n++;
  240. }
  241. }
  242. return $parents;
  243. }
  244. /**
  245. * Implementation of hook_hierarchical_select_valid_item().
  246. */
  247. function hs_taxonomy_hierarchical_select_valid_item($item, $params) {
  248. if ($params['root_term'] && $item == 0) {
  249. return TRUE;
  250. }
  251. else if (!is_numeric($item) || $item < 1 || $item == $params['exclude_tid']) {
  252. return FALSE;
  253. }
  254. $term = taxonomy_get_term($item);
  255. return ($term->vid == $params['vid']);
  256. }
  257. /**
  258. * Implementation of hook_hierarchical_select_item_get_label().
  259. */
  260. function hs_taxonomy_hierarchical_select_item_get_label($item, $params) {
  261. static $labels = array();
  262. if (!isset($labels[$item])) {
  263. $term = taxonomy_get_term($item);
  264. // Use the translated term when available!
  265. $labels[$item] = t($term->name);
  266. }
  267. return $labels[$item];
  268. }
  269. /**
  270. * Implementation of hook_hierarchical_select_create_item().
  271. */
  272. function hs_taxonomy_hierarchical_select_create_item($label, $parent, $params) {
  273. $form_values = array(
  274. 'name' => html_entity_decode($label, ENT_QUOTES),
  275. 'parent' => $parent,
  276. 'vid' => $params['vid'],
  277. );
  278. $status = taxonomy_save_term($form_values);
  279. if ($status == SAVED_NEW) {
  280. // Reset the cached tree.
  281. _hs_taxonomy_hierarchical_select_get_tree($params['vid'], 0, -1, 1, TRUE);
  282. // Retrieve the tid.
  283. $children = taxonomy_get_children($parent, $params['vid']);
  284. foreach ($children as $tid => $term) {
  285. if ($term->name == $label) {
  286. return $tid;
  287. }
  288. }
  289. }
  290. else {
  291. return FALSE;
  292. }
  293. }
  294. /**
  295. * Implementation of hook_hierarchical_select_entity_count().
  296. */
  297. function hs_taxonomy_hierarchical_select_entity_count($item, $params) {
  298. return hs_taxonomy_term_count_nodes($item);
  299. }
  300. /**
  301. * Implementation of hook_hierarchical_select_implementation_info().
  302. */
  303. function hs_taxonomy_hierarchical_select_implementation_info() {
  304. return array(
  305. 'hierarchy type' => t('Taxonomy'),
  306. 'entity type' => t('Node'),
  307. );
  308. }
  309. /**
  310. * Implementation of hook_hierarchical_select_config_info().
  311. */
  312. function hs_taxonomy_hierarchical_select_config_info() {
  313. static $config_info;
  314. if (!isset($config_info)) {
  315. $config_info = array();
  316. $content_types = node_get_types();
  317. $vocabularies = taxonomy_get_vocabularies();
  318. foreach ($vocabularies as $vid => $vocabulary) {
  319. if (variable_get("taxonomy_hierarchical_select_$vid", 0)) {
  320. // Collect the human-readable names of each content type for which this
  321. // vocabulary is used.
  322. $entities = array();
  323. foreach ($vocabulary->nodes as $content_type) {
  324. $entities[] = $content_types[$content_type]->name;
  325. }
  326. $config_id = "taxonomy-$vid";
  327. $config_info[$config_id] = array(
  328. 'config_id' => $config_id,
  329. 'hierarchy type' => t('Taxonomy'),
  330. 'hierarchy' => t($vocabulary->name),
  331. 'entity type' => t('Node'),
  332. 'entity' => implode(', ', array_map('t', $entities)),
  333. 'context type' => t('Node form'),
  334. 'context' => '',
  335. 'edit link' => "admin/content/taxonomy/edit/vocabulary/$vid",
  336. );
  337. }
  338. }
  339. }
  340. return $config_info;
  341. }
  342. //----------------------------------------------------------------------------
  343. // Token hooks.
  344. /**
  345. * Implementation of hook_token_values().
  346. */
  347. function hs_taxonomy_token_values($type, $object = NULL, $options = array()) {
  348. static $hs_vids;
  349. static $all_vids;
  350. $separator = variable_get('hs_taxonomy_separator', variable_get('pathauto_separator', '-'));
  351. $values = array();
  352. switch ($type) {
  353. case 'node':
  354. $node = $object;
  355. // Default values.
  356. $values['save-lineage-termpath'] = $values['save-lineage-termpath-raw'] = FALSE;
  357. // If $node->taxonomy doesn't exist, these tokens cannot be created!
  358. if (!is_object($node) || !isset($node->taxonomy) || !is_array($node->taxonomy)) {
  359. return $values;
  360. }
  361. // Find out which vocabularies are using Hierarchical Select.
  362. if (!isset($hs_vids)) {
  363. $hs_vids = array();
  364. $result = db_query("SELECT SUBSTRING(name, 30, 3) AS vid FROM {variable} WHERE name LIKE 'taxonomy_hierarchical_select_%' AND value LIKE 'i:1\;';");
  365. while ($o = db_fetch_object($result)) {
  366. $hs_vids[] = $o->vid;
  367. }
  368. }
  369. // Get a list of all existent vids, so we can generate an empty token
  370. // when a token is requested for a vocabulary that's not associated with
  371. // the current content type.
  372. if (!isset($all_vids)) {
  373. $all_vids = array();
  374. $result = db_query("SELECT vid FROM {vocabulary}");
  375. while ($row = db_fetch_object($result)) {
  376. $all_vids[] = $row->vid;
  377. }
  378. }
  379. // Generate the per-vid "save-lineage-termpath" tokens.
  380. foreach ($all_vids as $vid) {
  381. $terms = array();
  382. if (in_array($vid, $hs_vids) && isset($node->taxonomy[$vid])) {
  383. $selection = $node->taxonomy[$vid];
  384. $terms = _hs_taxonomy_token_termpath_for_vid($selection, $vid);
  385. }
  386. $values["save-lineage-termpath:$vid"] = implode($separator, array_map('check_plain', $terms));
  387. $values["save-lineage-termpath-raw:$vid"] = implode($separator, $terms);
  388. }
  389. // We use the terms of the first vocabulary that uses Hierarchical
  390. // Select for the default "save-lineage-termpath" tokens.
  391. $vids = array_intersect(array_keys($node->taxonomy), $hs_vids);
  392. if (!empty($vids)) {
  393. $vid = $vids[0];
  394. $values['save-lineage-termpath'] = implode($separator, array_map('check_plain', $terms));
  395. $values['save-lineage-termpath-raw'] = implode($separator, $terms);
  396. }
  397. break;
  398. }
  399. return $values;
  400. }
  401. /**
  402. * Implementation of hook_token_list().
  403. */
  404. function hs_taxonomy_token_list($type = 'all') {
  405. if ($type == 'node' || $type == 'all') {
  406. $tokens['node']['save-lineage-termpath'] = t('Only use when you have enabled the "save lineage" setting of Hierarchical Select. Will show the term\'s parent terms separated by /.');
  407. $tokens['node']['save-lineage-termpath-raw'] = t('As [save-linage-termpath]. WARNING - raw user input.');
  408. $tokens['node']['save-lineage-termpath:vid'] = t('Only has output when terms are present for the vocabulary with the specified vid. Only use when you have enabled the "save lineage" setting of Hierarchical Select. Will show the term\'s parent terms separated by /.');
  409. $tokens['node']['save-lineage-termpath-raw:vid'] = t('Only has output when terms are present for the vocabulary with the specified vid. As [save-linage-termpath]. WARNING - raw user input.');
  410. return $tokens;
  411. }
  412. }
  413. /**
  414. * Helper function for hs_taxonomy_token_values().
  415. */
  416. function _hs_taxonomy_token_termpath_for_vid($selection, $vid) {
  417. $terms = array();
  418. $selection = (is_array($selection)) ? $selection : array($selection);
  419. // Generate the part we'll need of the Hierarchical Select configuration.
  420. $config = array(
  421. 'module' => 'hs_taxonomy',
  422. 'save_lineage' => 1,
  423. 'params' => array(
  424. 'vid' => $vid,
  425. 'exclude_tid' => NULL,
  426. 'root_term' => NULL,
  427. ),
  428. );
  429. // Validate all items in the selection, if any.
  430. if (!empty($selection)) {
  431. foreach ($selection as $key => $item) {
  432. $valid = module_invoke($config['module'], 'hierarchical_select_valid_item', $selection[$key], $config['params']);
  433. if (!$valid) {
  434. unset($selection[$key]);
  435. }
  436. }
  437. }
  438. // Generate a dropbox out of the selection. This will automatically
  439. // calculate all lineages for us.
  440. // If the selection is empty, then the tokens will be as well.
  441. if (!empty($selection)) {
  442. $dropbox = _hierarchical_select_dropbox_generate($config, $selection);
  443. // If no lineages could be generated, these tokens cannot be created!
  444. if (empty($dropbox->lineages)) {
  445. return $terms;
  446. }
  447. // We pick the first lineage.
  448. $lineage = $dropbox->lineages[0];
  449. // Finally, we build the tokens.
  450. foreach ($lineage as $item) {
  451. $terms[] = $item['label'];
  452. }
  453. }
  454. return $terms;
  455. }
  456. //----------------------------------------------------------------------------
  457. // FAPI callbacks.
  458. /**
  459. * Additional validate callback for the taxonomy_form_vocabulary form.
  460. */
  461. function hierarchical_select_taxonomy_form_vocabulary_validate($form_id, $form_values, $form) {
  462. // If Hierarchical Select is enabled, the "multiple select" setting must be
  463. // managed by Hierarchical Select.
  464. // TRICKY: the "multiple select" setting is absent for the forum vocabulary!
  465. if ($form_values['hierarchical_select_status'] && isset($form['multiple'])) {
  466. // Enable Taxonomy's "multiple select" setting when:
  467. // - Hierarchical Select's "multiple select" setting is enabled, or:
  468. // - Hierarchical Select's "save term lineage" setting is enabled
  469. $multiple_select_enabled = ($form_values['hierarchical_select']['dropbox']['status'] || $form_values['hierarchical_select']['save_lineage']);
  470. form_set_value($form['multiple'], (int) $multiple_select_enabled);
  471. }
  472. // If Hierarchical Select is enabled, disable freetagging.
  473. if ($form_values['hierarchical_select_status']) {
  474. form_set_value($form['tags'], 0);
  475. }
  476. }
  477. /**
  478. * Additional submit callback for the taxonomy_form_vocabulary form.
  479. */
  480. function hierarchical_select_taxonomy_form_vocabulary_submit($form_id, $form_values) {
  481. $vid = $form_values['vid'];
  482. variable_set("taxonomy_hierarchical_select_$vid", $form_values['hierarchical_select_status']);
  483. }
  484. //----------------------------------------------------------------------------
  485. // Private functions.
  486. /**
  487. * Drupal core's taxonomy_get_tree() doesn't allow us to reset the cached
  488. * trees, which obviously causes problems when you create new items between
  489. * two calls to it.
  490. */
  491. function _hs_taxonomy_hierarchical_select_get_tree($vid, $parent = 0, $depth = -1, $max_depth = NULL, $reset = FALSE) {
  492. static $children, $parents, $terms;
  493. if ($reset) {
  494. $children = $parents = $terms = array();
  495. }
  496. $depth++;
  497. // We cache trees, so it's not CPU-intensive to call get_tree() on a term
  498. // and its children, too.
  499. if (!isset($children[$vid])) {
  500. $children[$vid] = array();
  501. $result = db_query(db_rewrite_sql('SELECT t.tid, t.*, parent FROM {term_data} t INNER JOIN {term_hierarchy} h ON t.tid = h.tid WHERE t.vid = %d ORDER BY weight, name', 't', 'tid'), $vid);
  502. while ($term = db_fetch_object($result)) {
  503. $children[$vid][$term->parent][] = $term->tid;
  504. $parents[$vid][$term->tid][] = $term->parent;
  505. $terms[$vid][$term->tid] = $term;
  506. }
  507. }
  508. $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth;
  509. if ($children[$vid][$parent]) {
  510. foreach ($children[$vid][$parent] as $child) {
  511. if ($max_depth > $depth) {
  512. $term = drupal_clone($terms[$vid][$child]);
  513. $term->depth = $depth;
  514. // The "parent" attribute is not useful, as it would show one parent only.
  515. unset($term->parent);
  516. $term->parents = $parents[$vid][$child];
  517. $tree[] = $term;
  518. if ($children[$vid][$child]) {
  519. $tree = array_merge($tree, _hs_taxonomy_hierarchical_select_get_tree($vid, $child, $depth, $max_depth));
  520. }
  521. }
  522. }
  523. }
  524. return $tree ? $tree : array();
  525. }
  526. /**
  527. * Drupal core's taxonomy_term_count_nodes() is buggy. See
  528. * http://drupal.org/node/144969#comment-843000.
  529. */
  530. function hs_taxonomy_term_count_nodes($tid, $type = 0) {
  531. static $count;
  532. $term = taxonomy_get_term($tid);
  533. $tree = _hs_taxonomy_hierarchical_select_get_tree($term->vid, $tid);
  534. $tids = array($tid);
  535. foreach ($tree as $descendant) {
  536. $tids[] = $descendant->tid;
  537. }
  538. if (!isset($count[$type][$tid])) {
  539. if (is_numeric($type)) {
  540. $count[$type][$tid] = db_result(db_query(db_rewrite_sql("SELECT COUNT(DISTINCT(n.nid)) AS count FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE n.status = 1 AND t.tid IN (%s)"), implode(',', $tids)));
  541. }
  542. else {
  543. $count[$type][$tid] = db_result(db_query(db_rewrite_sql("SELECT COUNT(DISTINCT(n.nid)) AS count FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE n.status = 1 AND n.type = '%s' AND t.tid IN (%s)"), $type, implode(',', $tids)));
  544. }
  545. }
  546. return $count[$type][$tid];
  547. }
  548. /**
  549. * Transform an array of terms into an associative array of options, for use
  550. * in a select form item.
  551. *
  552. * @param $terms
  553. * An array of term objects.
  554. * @return
  555. * An associative array of options, keys are tids, values are term names.
  556. */
  557. function _hs_taxonomy_hierarchical_select_terms_to_options($terms) {
  558. $options = array();
  559. foreach ($terms as $key => $term) {
  560. // Use the translated term when available!
  561. $options[$term->tid] = t($term->name);
  562. }
  563. return $options;
  564. }
  565. /**
  566. * Get the depth of a vocabulary's tree.
  567. *
  568. * @param $vid
  569. * A vocabulary id.
  570. * @return
  571. * The depth of the vocabulary's tree.
  572. */
  573. function _hs_taxonomy_hierarchical_select_get_depth($vid) {
  574. $tree = _hs_taxonomy_hierarchical_select_get_tree($vid);
  575. foreach ($tree as $term) {
  576. if ($term->depth > $depth) {
  577. $depth = $term->depth;
  578. }
  579. }
  580. return $depth;
  581. }
  582. /**
  583. * Update a taxonomy select to become a hierarchical_select type form item.
  584. *
  585. * @param $form_item
  586. * The form item to update.
  587. * @param $vid
  588. * The id of the vocabulary of which the configuration should be applied.
  589. */
  590. function hs_taxonomy_hierarchical_select_update_form_item(&$form_item, $vid) {
  591. unset($form_item['#options']); // Unset to prevent passing around of possibly huge HTML.
  592. unset($form_item['#theme']); // Unset to prevent theme_taxonomy_term_select() from running.
  593. hierarchical_select_common_config_apply($form_item, "taxonomy-$vid");
  594. }