'admin/content/types/'. $content_type['url_str'] .'/fields/'. $field_name .'/hs_config', 'title' => t('Hierarchical Select configuration for !field', array('!field' => $content_type['fields'][$field_name]['widget']['label'])), 'callback' => 'drupal_get_form', 'callback arguments' => array('hs_content_taxonomy_config_form', $content_type['type'], $field_name), 'access' => user_access('administer content types'), 'type' => MENU_CALLBACK, ); } } return $items; } //---------------------------------------------------------------------------- // Forms API callbacks. /** * Form definition; configuration form for Hierarchical Select as the widget * for a content_taxonomy field. * * @param $content_type_name * Name of a content type. Provides necessary context. * @param $field_name * Name of a field. Provides necessary context. */ function hs_content_taxonomy_config_form($content_type_name, $field_name) { drupal_add_css(drupal_get_path('module', 'hs_content_taxonomy') .'/hs_content_taxonomy.css'); $content_type = content_types($content_type_name); $field = $content_type['fields'][$field_name]; // Extract the necessary context from the $field array. $vid = $field['vid']; $depth = (empty($field['depth'])) ? 0 : $field['depth']; $dropbox_status = $field['multiple']; // Add the Hierarchical Select config form. $module = 'hs_content_taxonomy'; $params = array('vid' => $vid, 'depth' => $depth); $config_id = "content-taxonomy-$field_name"; $vocabulary = taxonomy_get_vocabulary($vid); $defaults = array( // Enable the save_lineage setting by default if the multiple parents // vocabulary option is enabled. 'save_lineage' => (int) ($vocabulary->hierarchy == 2), 'dropbox' => array( 'status' => $dropbox_status, ), 'editability' => array( 'max_levels' => min($depth, _taxonomy_hierarchical_select_get_depth($vid)), ), ); $strings = array( 'hierarchy' => t('vocabulary'), 'hierarchies' => t('vocabularies'), 'item' => t('term'), 'items' => t('terms'), 'item_type' => t('term type'), ); $max_hierarchy_depth = min($depth, _taxonomy_hierarchical_select_get_depth($vid)); $form['hierarchical_select_config'] = hierarchical_select_common_config_form($module, $params, $config_id, $defaults, $strings, $max_hierarchy_depth); $form['link'] = array( '#value' => l('Back to the field configuration', 'admin/content/types/'. $content_type['url_str'] .'/fields/'. $field_name), '#prefix' => '', '#weight' => -5, ); $form['save'] = array( '#type' => 'submit', '#value' => t('Save'), ); // Add the the submit handler for the Hierarchical Select config form. $parents = array('hierarchical_select_config'); $form['#submit']['hierarchical_select_common_config_form_submit'] = array($parents); return $form; } //---------------------------------------------------------------------------- // CCK hooks. /** * Implementation of hook_widget_info(). */ function hs_content_taxonomy_widget_info() { return array( 'content_taxonomy_hs' => array( // 'content_taxonomy_hs' instead of 'content_taxonomy_hierarchical_select' due to CCK limitations. 'label' => 'Hierarchical Select', 'field types' => array('content_taxonomy'), ), ); } /** * Implementation of hook_widget_settings(). */ function hs_content_taxonomy_widget_settings($op, $widget) { switch ($op) { case 'form': drupal_add_css(drupal_get_path('module', 'hs_content_taxonomy') .'/hs_content_taxonomy.css'); $context = _hs_content_taxonomy_parse_context_from_url(); list($content_type_name, $field_name) = $context; $content_type = content_types($content_type_name); $url = 'admin/content/types/'. $content_type['url_str'] .'/fields/'. $field_name .'/hs_config'; $items[] = t( "Due to limitations of CCK's widget settings system, there is a separate form to configure this widget'ssettings.", array('!url' => url($url)) ); $items[] = t( 'You have to configure the %save_options to either %save_as_tag or %both, to allow Hierarchical Select to work.', array( '%save_options' => t('Save options'), '%save_as_tag' => t('Save as tag'), '%both' => t('Both'), ) ); $items[] = t( 'Please make sure that the %multiple_values setting on this page and the %enable_the_dropbox setting on the separate form match!', array( '%multiple_values' => t('Multiple values'), '%enable_the_dropbox' => t('Enable the dropbox')) ); $form['hs_config'] = array( '#type' => 'fieldset', '#title' => t('Hierarchical Select configuration'), '#description' => '

'. 'Important!'. '

'. theme('item_list', $items), '#collapsible' => FALSE, ); return $form; case 'callbacks': return array( 'default value' => CONTENT_CALLBACK_NONE, ); } } /** * Implementation of hook_widget(). */ function hs_content_taxonomy_widget($op, &$node, $field, &$node_field) { if ($field['widget']['type'] == 'content_taxonomy_hs') { $field_name = $field['field_name']; $vid = $field['vid']; $tid = $field['tid']; $depth = (empty($field['depth'])) ? 0 : $field['depth']; switch ($op) { case 'form': $form[$field_name]['#tree'] = TRUE; $form[$field_name]['tids'] = array( '#type' => 'hierarchical_select', '#config' => array( 'module' => 'hs_content_taxonomy', 'params' => array( 'vid' => $vid, 'depth' => $depth, ), ), '#required' => $field['required'], '#description' => t($field['widget']['description']), '#default_value' => (is_array($node->taxonomy)) ? array_keys($node->taxonomy) : array(), ); hierarchical_select_common_config_apply($form[$field_name]['tids'], "content-taxonomy-$field_name"); // Enforce the dropbox when this content_taxonomy field is configured // to use multiple values. $form[$field_name]['tids']['#config']['dropbox']['status'] = $field['multiple']; return $form; case 'submit': case 'process form values': // TRICKY: getting the content_taxonomy formatter to work. I failed. // I gave up after a while because it just doesn't make *any* sense at // all. Even after adding debug output to the content_taxonomy module // and cck itself, I failed to get this to work. // I gave up because in the end, I excpect *very* few users will use // the CCK formatter, most will go with the normal taxonomy // presentation (i.e. as links). break; } } } //---------------------------------------------------------------------------- // Hierarchical Select hooks. /** * Implementation of hook_hierarchical_select_params(). */ function hs_content_taxonomy_hierarchical_select_params() { $params = array( 'vid', 'depth', ); return $params; } /** * Implementation of hook_hierarchical_select_root_level(). */ function hs_content_taxonomy_hierarchical_select_root_level($params) { return taxonomy_hierarchical_select_root_level($params); } /** * Implementation of hook_hierarchical_select_children(). */ function hs_content_taxonomy_hierarchical_select_children($parent, $params) { static $tree; // Keep a static cache of the entire tree, this allows us to quickly look up // if a term is not to deep – because if it's too deep, we don't want to // return any children. if (!isset($tree)) { $tree = taxonomy_get_tree($params['vid'], 0); } $terms = ($tree[$parent]->depth >= $params['depth']) ? array() : taxonomy_get_tree($params['vid'], $parent, -1, 1); return _taxonomy_hierarchical_select_terms_to_options($terms); } /** * Implementation of hook_hierarchical_select_lineage(). */ function hs_content_taxonomy_hierarchical_select_lineage($item, $params) { return taxonomy_hierarchical_select_lineage($item, $params); } /** * Implementation of hook_hierarchical_select_valid_item(). */ function hs_content_taxonomy_hierarchical_select_valid_item($item, $params) { if (!is_numeric($item) || $item < 1) { return FALSE; } $term = taxonomy_get_term($item); return ($term->vid == $params['vid'] && $term->depth < $params['depth']); } /** * Implementation of hook_hierarchical_select_item_get_label(). */ function hs_content_taxonomy_hierarchical_select_item_get_label($item, $params) { return taxonomy_hierarchical_select_item_get_label($item, $params); } /** * Implementation of hook_hierarchical_select_create_item(). */ function hs_content_taxonomy_hierarchical_select_create_item($label, $parent, $params) { // TRICKY: no depth check is necessary because HS's internal validation // prevents an invalid parent. return taxonomy_hierarchical_select_create_item($label, $parent, $params); } /** * Implementation of hook_hierarchical_select_node_count(). */ function hs_content_taxonomy_hierarchical_select_node_count($item, $params) { return taxonomy_hierarchical_select_node_count($item, $params); } /** * Implementation of hook_hierarchical_select_config_info(). */ function hs_content_taxonomy_hierarchical_select_config_info() { static $config_info; if (!isset($config_info)) { $config_info = array(); $content_types = content_types(); $fields = content_fields(); foreach ($fields as $field_name => $field) { if ($field['type'] == 'content_taxonomy') { foreach ($content_types as $content_type_name => $content_type) { if (isset($content_type['fields'][$field_name]) && $content_type['fields'][$field_name]['widget']['type'] == 'content_taxonomy_hs') { $vocabulary = taxonomy_get_vocabulary($field['vid']); $config_id = "content-taxonomy-$field_name"; $config_info["$config_id|$content_type_name"] = array( 'config_id' => $config_id, 'hierarchy type' => t('Content Taxonomy'), 'hierarchy' => t($vocabulary->name) ." ($field_name)", 'entity type' => t('node'), 'entity' => t($content_type['name']), 'edit link' => "admin/content/types/$content_type_name/fields/$field_name/hs_config", ); } } } } } return $config_info; } //---------------------------------------------------------------------------- // Private functions. /** * Parse the context (the content type and the field name) from the URL. * * @return * - FALSE if no context could be found * - array($content_type_name, $field_name) otherwise */ function _hs_content_taxonomy_parse_context_from_url() { if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'types') { $content_type = content_types(arg(3)); $type = node_get_types('types', $content_type['type']); $field_name = arg(5); if (arg(4) == 'fields' && !empty($field_name) && isset($content_type['fields'][$field_name])) { if ($content_type['fields'][$field_name]['type'] == 'content_taxonomy' && $content_type['fields'][$field_name]['widget']['type'] == 'content_taxonomy_hs') { return array($content_type['type'], $field_name); } } } return FALSE; }