You are here

API.txt in Hierarchical Select 5.3

Same filename and directory in other branches
  1. 5 API.txt
  2. 5.2 API.txt
  3. 6.3 API.txt
  4. 7.3 API.txt
Terminology
-----------
- item: an item in the hierarchy. A hierarchy can also be seen as a tree. In
        that case, an item can be either a parent or a child. However, if
        "multiple parents" are supported (i.e. a child can have multiple
        parents), then it's actually not a tree but a directed acyclic graph
        (see http://en.wikipedia.org/wiki/Directed_acyclic_graph), in which
        each case technically is a "node".
        An example: in the case of taxonomy, this is the term id (tid).
- label: the label associated with an item in the hierarchy. You may now it
         as "title" or something else similar.
         An example: in the case of taxonomy, this is the actual term.
- item type: a per-level, human-readable name that describes what kind of
             items that level contains.
- entity: an item is often associated with an entity. E.g. a term is usually
          associated with a node.
- form element: a form element allows the developer to assign a new value to
                a #type property in a form item. Examples of form elements
                supported by Drupal core are: select, checkboxes, textfield.
- form item: an instance of a form element, with various other properties
             defined, such as #title, #default_value and #description. These
             are used to define a form in Drupal.
- Hierarchical Select: this is the name of the module.
- hierarchical_select: this is the internal name of the Hierarchical Select
                       form element.
- hierarchical select: (note the difference in case) this is the part of the
                       widget with the multiple selects.
- dropbox: this is the part of the widget where the selections are stored when
           multiple selections are allowed.
           

Form API usage
--------------
You have to make sure your form item is using the "hierarchical_select" form
element type:

  $form['select_some_term'] = array(
    '#type' => 'hierarchical_select',
    '#title' => t('Select the tag you wish to use.'),
    '#size' => 1,
    '#config' => array(
      'module' => 'hs_taxonomy',
      'params' => array(
        'vid' => $vid,
      ),
      'save_lineage'    => 0,
      'enforce_deepest' => 0,
      'entity_count'    => 0,
      'require_entity'  => 0,
      'resizable'       => 1,
      'level_labels' => array(
        'status' => 0,
        'labels' => array(
          0 => t('Main category'),
          1 => t('Subcategory'),
          2 => t('Third level category'),
        ),
      ),
      'dropbox' => array(
        'status'   => 0,
        'title'    => t('All selections'),
        'limit'    => 0,
        'reset_hs' => 1,
      ),
      'editability' => array(
        'status'           => 0,
        'item_types'       => array(),
        'allowed_levels'   => array(
          0 => 0,
          1 => 0,
          2 => 1,
        ),
        'allow_new_levels' => 0,
        'max_levels'       => 3,
      ),
      // These settings cannot be configured through the UI: they can only be
      // overridden through code.
      'animation_delay'    => 400,
      'exclusive_lineages' => array(),
      'render_flat_select' => 0,
    ),
    '#default_value' => '83',
  ); 

Now, let's explain what we see here:
1) We've set the #type property to "hierarchical_select" instead of "select".
2) The #size property is inherited by the selects of the hierarchical select.
   You can use it to change a vertical size of the select (i.e. change how many
   items are displayed in the select, similar to a form select multiple).
3) There's a new property: #config. This must be an
array. These are the items it can contain:
 - module (required)
   This will be passed through in the AJAX requests, to let Hierarchical
   Select know which module's hooks should be used.

 - params (optional, may be necessary for some implementations)
   An array of parameters that will also be passed through in every AJAX
   request.
   e.g. In the case of taxonomy, this is the vocabulary id (vid). In case of
   content_taxonomy, there's three parameters: vid, tid and depth (tid allows
   one to define a new root, depth allows one to limit the depth of the
   displayed hierarchy).

 - save_lineage (optional, defaults to 0)
   Triggers the lineage saving functionality. If enabled, the selection can
   consist of multiple values.

 - enforce_deepest (optional, defaults to 0)
   Triggers the enforcing of a selection in the deepest level. If enabled, the
   selection will always be a single value.

 - entity_count (optional, defaults to 0)
   Enables the display of entity counts, between parentheses, for each item in
   the hierarchy.

 - require_entity (optional, defaults to 0)
   Whether an item should only be displayed if it has at least one associated
   entity.
 
 - level_labels['status'] (optional, defaults to 0)
   Whether level labels should be enabled or not. When save_lineage is
   enabled, this will result in *empty* level labels.

 - level_labels['labels'] (optional)
   An array of labels, one per level. The label for the first level should be
   the value of key 0.
   When enforce_deepest is set to:
   - 0, then you can provide n level labels, with n the number of levels
   - 1, then you can provide only one level label.   

 - dropbox['status'] (optional, defaults to 0)
   Whether the dropbox is enabled or not (the dropbox allows the user to make
   multiple selections).

 - dropbox['title'] (optional, defaults to "All selections:")
   The title of the dropbox. The dropbox is the area where all selections are
   displayed when the dropbox is enabled.

 - dropbox['limit'] (optional, defaults to 0, which means "no limit")
   Limit the number of selection that can be added to the dropbox. So this
   allows you the restrict the number of items that can be selected when
   the dropbox has been enabled.
   
 - dropbox['reset_hs'] (optional, defaults to 1, which means "do reset")
   Determines what will happen to the hierarchical select when the user has
   added a selection to the dropbox.

 - editability['status] (optional, defaults to 0)
   Allow the user to create new items in the hierarchy.

 - editability['item_types'] (optional, defaults to the empty array)
   Only meaningful when editable is set to TRUE.
   Set the item type for each level. E.g.: "country" for the first level,
   "region" for the second and "city" for the third. When the user then wants
   to create a new item, the default label for the new item will be of the
   form "new <item type>", e.g. "new region".

 - editability['allowed_levels'] (optional, defaults to 1 for each level)
   Only meaningful when editable is set to TRUE.
   Specify in which levels the user is allowed to create new items. In the
   example, the user is only allowed to create new items in the third level.
   When a setting for a level is ommitted, it defaults to 1 (i.e. allowed for
   that level). This means you only have to specify in which levels the user
   is not allowed to create new items.
   This only applies to *existing* levels: it does not affect the
   allow_new_levels setting (the next setting).

 - editability['allow_new_levels'] (optional, defaults to 0)
   Only meaningful when editable is set to TRUE.
   Allow the user to create new levels, i.e. when a certain item does not yet
   have children, the user can create a first child for it (thus thereby
   creating a new level).

 - editability['max_levels'] (optional, defaults to 3)
   Only meaningful when editable_settings['allow_new_levels'] is set to TRUE.
   Limits the maximum number of levels. Don't set this too high or you'll end
   up with very deep hierarchies. This only affects how deep new levels can be
   created, it will not affect the existing hierarchy.

 - animation_delay (optional, defaults to 400)
   The delay of each animation (the drop in left and right animations), in ms.

 - exclusive_lineages (optional, defaults to the empty array)
   Sometimes, it's desirable to have exclusive lineages. When one of these
   lineages is selected, the user should not be able to select anything else.
   e.g. the **ALL** option in Views exposed filters.

 - render_flat_select (optional, defaults to 0)
   Because the hierarchical_select form element consists of multiple form
   items, it doesn't work well in GET forms. By enabling this setting, a flat
   select will also be rendered, that contains only the selected lineages.
   Combine that with Drupal.HierarchicalSelect.prepareGETSubmit in the JS code
   (or, alternatively, the 'prepare-GET-submit' event  that can be triggered,
   see the JavaScript events section for details) and you have a work-around
   (which, admittedly, only works when JS is enabled).

 - resizable (optional, defaults to 1)
   Makes the hierarchical select resizable.
3) We *don't* specify a list of options: Hierarchical Select automatically
generates the options for us, thanks to the 'module' and 'params' settings.


Concepts
--------
- Item Unicity: each item in the hierarchy must be *unique*. It doesn't have
                to be numerical, it can also be a string.
                If your hierarchy does not have unique items by nature or by
                design (your items may be unique per level instead), that's
                not a problem. You can simply prepend the item's ancestors to
                get a unique item.
                e.g. you have an item "foobar" at the first, second and third
                levels. By prepending the ancestors using the dash as the
                separator, you'd get an item "foobar-foobar-foobar" at the
                third level.
                Also see the "Reserved item values" section.
- #options: it's gone, because it was the inherent cause for scalability
            problems: if a hierarchy consists of 10,000 or even 100,000 items,
            this results in huge HTML being generated. Huge HTML means more
            processing power necessary, and more bandwidth necessary. So where
            does Hierarchical Select get its "options"? It uses the hooks that
            every implementation has to implement to only get what it needs.
- The General Concept: you should think of Hierarchical Select as an abstract
                       widget that can represent *any* hierarchy. To be able
                       to display any hierarchy, you obviously  need some
                       universal way to "browse" a hierarchy.
                       If you are familiar with C++ or Java iterators, this
                       should come natural: the hooks you have to implement
                       is what allows Hierarchical Select to iterate over your
                       hierarchy. Then the heart of the iterator would be the
                       root_level() and children() hooks. params() allows you
                       to define which information is necessary before you can
                       determine *which* hierarchy or which *part* of the
                       hierarchy is being browsed. lineage() must return the
                       lineage, i.e. the item itself and all its ancestors,
                       this allows a hierarchy to be generated from just one
                       (selected) item.


Reserved item values
--------------------
- Ensure that your items don't have a "none", "all", "create_new_item" nor
  "label_\d+" values (the latter means "label_" followed by one or more
  digits). Your values should also not contain a pipe ("|"), since pipes are
  used to separate the selection of values that are sent back to the server
  in the callbacks.
- Valid 'empty' selections (i.e. if you want to set the #default_value
  property of your form item), are -1 and the empty array. The empty string is
  also considered valid, because Drupal core's Taxonomy module uses this as
  the empty selection.


Developer mode
--------------
When you are writing your implementation of the Hierarchical Select API, you
will often wonder what Hierarchical Select is doing internally with the data
you're feeding it. That's why there's a developer mode: it will show you this
data, even the data generated in AJAX callbacks. It'll also show you the time
it took to generate the lineage, to fill up the levels and to calculate the
child info, to track down badly performing code.
Also, when you're just creating a new HS config and it doesn't quite work
right, it can be helpful to enable the developer mode. It will perform some
basic diagnostics that might help you track down the cause.
To use this, you must have a browser with console.log() support. Install 
Firebug Lite (http://getfirebug.com/lite.html) if your browser does not
suport this. Next, go to Hierarchical Select's .module file and set the define
for the HS_DEVELOPER_MODE constant to TRUE.
When you now open Firebug (Firefox) or the Web Inspector (Safari), you'll see
the debug output. New output is added after each callback to the server.


Hierarchical Select implementations: gotcha's
---------------------------------------------
- "warning: Missing argument 1 for drupal_retrieve_form() …"
  This implies that your implementation's module weight is heavier than
  hierarchical_select.module. In that case, Hierarchical Select will not be
  able to detect hierarchical_select form items, preventing it from applying
  some magic, and AJAX updates won't work.


Hierarchical Select API Tutorial
--------------------------------
Written by Stephen Barker of Digital Frontiers Media
(http://drupal.org/user/106070) and reviewed by Wim Leers:
  http://drupal.org/node/532724


Hooks
-----
1) hook_hierarchical_select_params();
   Returns an array with the names of all parameters that are necessary for
   this implementation to work.

2) hook_hierarchical_select_root_level($params, $dropbox = FALSE);
   Returns the root level of the hierarchy: an array of (item, label) pairs.
   The $dropbox parameter can is optional and can even ommitted, as it's only
   necessary if you need the dropbox to influence your hierarchy.

3) hook_hierarchical_select_children($parent, $params, $dropbox = FALSE);
   Gets the children of $parent ($parent is an item in the hierarchy) and
   returns them: an array of (item, label) pairs, or the empty array if the
   given $parent has no children.
   The $dropbox parameter can is optional and can even ommitted, as it's only
   necessary if you need the dropbox to influence your hierarchy.

4) hook_hierarchical_select_lineage($item, $params);
   Calculates the lineage of $item (array of items, with $item the last) and
   returns it. Necessary when the "enforce_deepest" option is enabled.

5) hook_hierarchical_select_valid_item($item, $params);
   Validates an item, returns TRUE if valid, FALSE if invalid.

6) hook_hierarchical_select_item_get_label($item, $params);
   Given a valid item, returns the label. Is only used for rendering the
   selections in the dropbox.

7) hook_hierarchical_select_create_item($label, $parent, $params);
   Given a parent item and the label of a new item, create a new item as a
   child of the parent item. When $parent == 0, this means a new item is being
   created at the root level.
   Optional hook. When this hook is not implemented, this functionality will
   never be used, even when you configure it that way in code.

8) hook_hierarchical_select_entity_count($item, $params);
   Given a item, get the number of entities (most of the time the entity type
   is 'node') that are related to the given item. Used for the entity_count
   and require_entity settings.
   Optional hook. When this hook is not implemented, this functionality will
   never be used, even when you configure it that way (i.e. when you enable
   the entity_count and require_entity settings).

9) hook_hierarchical_select_implementation_info();
   Return metadata about this implementation.
   This information is used to generate the implementations overview at
   admin/settings/hierarchical_select/implementations. The expected format is:

      array(
        'hierarchy type' => t('Taxonomy'),
        'entity type'    => t('Node'),
        'entity'         => t('Story'),
        'context type'   => t('Node form'),
        'context'        => '',
      );
    
    another example:

      array(
        'hierarchy type' => t('Taxonomy'),
        'entity type'    => t('Node'),
        'entity'         => '',
        'context type'   => t('Views exposed filter'),
        'context'        => t('some view'),
      );

10) hook_hierarchical_select_config_info();
    Return metadata about each available user-editable configuration for this
    implementation.
    Optional hook. This information is used to generate the configurations
    overview at admin/settings/hierarchical_select/configs. The expected
    format is:

      $config_info[$config_id] = array(
        'config_id'      => $config_id,
        'hierarchy type' => t('Taxonomy'),
        'hierarchy'      => t($vocabulary->name),
        'entity type'    => t('Node'),
        'entity'         => implode(', ', array_map('t', $entities)),
        'edit link'      => "admin/content/taxonomy/edit/vocabulary/$vid",
      );


Standardized configuration form
-------------------------------
Hierarchical Select 3 comes with a standardized configuration form: 
hierarchical_select_common_config_form(). This function accepts a lot of
parameters, which allows you to use names typical to your module's hierarchy
(e.g. 'leaf' instead of 'term' and 'tree' instead of 'vocabulary'). A submit
handler is also provided, of course.
An example:

  // I'm not configuring all parameters here. For an example of that, see one
  // of the included modules.
  $form['foobar_hierarchical_select_config'] = hierarchical_select_common_config_form($module, $params, $config_id, $defaults, $strings, $max_hierarchy_depth, $preview_is_required);

  // Add the the submit handler for the Hierarchical Select config form.
  $parents = array('foobar_hierarchical_select_config');
  $form['#submit']['hierarchical_select_common_config_form_submit'] = array($parents);


Configuration management
------------------------
It's now possible to export Hierarchical Select configurations, and there is a
function to set the configuration of a certain Hierarchical Select. Combine
the two and you can manage your Hierarchical Select configurations in code!
An example:

  // The exported configuration.
  $config = array( … );
  $config_id = $config['config_id];

  // Apply the configuration.
  require_once(drupal_get_path('module', 'hierarchical_select') .'/includes/common.inc');
  hierarchical_select_common_config_set($config_id, $config);


JavaScript events
-----------------
The Hierarchical Select module's JavaScript code triggers several events, to
allow for advanced interactions.

You can find all hierarchical_select form items using this selector:

  $('.hierarchical-select-wrapper');

You can find a *specific* hierarchical_select form item using this selector:

  $('#hierarchical-select-x-wrapper');

where x is a number, or more accurately: a hsid (hierarchical select id).
Retrieving all hsids in the current document can be done like this:

  for (var hsid in Drupal.settings.HierarchicalSelect.settings) {
    // …
  }

The following events are triggered:
  - change-hierarchical-select
  - update-hierarchical-select
  - create-new-item
  - cancel-new-item
  - add-to-dropbox
  - remove-from-dropbox
  - enforced-update
  - prepared-GET-submit
All events are triggered *after* the animations have completed.

However, it's often useful to do something *before* an event (especially
because all of the above events perform an AJAX request to the server). So,
the equivalent "before" events exist as well:
  - before-update-hierarchical-select
  - before-create-new-item
  - before-cancel-new-item
  - before-add-to-dropbox
  - before-remove-from-dropbox
  - before-enforced-update
There is one exception: when the cache is enabled, the "before update
hierarchical select" event will not be triggered. This makes sense, because
updates from the cache are instantaneous.

An example of binding a function to the 'create-new-item' event of the second
(hsid == 1) hierarchical_select form item on the page:

  $('#hierarchical-select-1-wrapper')
  .bind('create-new-item', function() {
    // …
  });

And finally, you can trigger a special event to enforce an update (this can be
useful when you have changed a hierarchy through another form item, or for
live previews, or …). You can then also pass additional information that will
be POSTed. You can even disable normal updates, to manage that completely
yourself via enforced updates. This allows you to write a Hierarchical Select
implementation that gets some of its information ($params) from another form
item!
Suppose you'd like to enforce an update of the first (hsid == 0)
hierarchical_select form item on the page:

  $('#hierarchical-select-0-wrapper')
  .trigger('enforce-update');

Now let's move on to a more advanced example, in which we will disable normal
updates and let another form item (here a select) provide a part of the
information that will be used to render the Hierarchical Select. Effectively,
this other form item will *influence* the hierarchy that will be presented by
Hierarchical Select!

  $(document).ready(function() {
    Drupal.settings.specialfilter = {};

    // .specialfilter-first: a select form item
    // .specialfilter-second: a hierarchical_select form item

    update = function() {
      var selection = Drupal.settings.specialfilter.currentSelection;

      // Send an extra parameter via POST: dynamicParameter. This is the stored
      // selection.
      $('.specialfilter-second')
      .trigger('enforce-update',
        [
          { name : 'dynamicParameter', value : selection }
        ]
      );
    };

    attachHSBindings = function() {
      // When a user navigates the hierarchical_select form item, we still want to
      // POST the the extra dynamicParameter, or otherwise we will no longer have
      // a hierarchy in the hierarchical_select form item that really depends on
      // the select.
      $('.specialfilter-second .hierarchical-select > select')
      .change(function() { update(); });

      $('.specialfilter-second')
      .unbind('enforced-update').bind('enforced-update', function() { return attachHSBindings(); });
    };

    // Initialize after 25 ms, because otherwise the event binding of HS will
    // not yet be ready, and hence this won't have any effect
    setTimeout(function() {
      // Get the initial selection (before the user has changed anything).
      Drupal.settings.specialfilter.currentSelection = $('.specialfilter-first').attr('value');

      // When the select form item changes, we want to *store* that selection, and
      // update the hierarchical_select form item.
      $('.specialfilter-first')
      .change(function() {
        // Store the current selection.
        Drupal.settings.specialfilter.currentSelection = $(this).attr('value');
    
        update();
      });

      $('.specialfilter-second')
      .trigger('disable-updates');

      attachHSBindings();
    }, 25);
  });

The 'enforced-update' (notice the past tense!) event is triggered upon
completion.
An even more rarely used special event can be triggered to prepare the
hierarchical_select form element for a get submit: the 'prepare GET submit'
event. To use this event, the 'render_flat_select' setting should be enabled
in the config.

File

API.txt
View source
  1. Terminology
  2. -----------
  3. - item: an item in the hierarchy. A hierarchy can also be seen as a tree. In
  4. that case, an item can be either a parent or a child. However, if
  5. "multiple parents" are supported (i.e. a child can have multiple
  6. parents), then it's actually not a tree but a directed acyclic graph
  7. (see http://en.wikipedia.org/wiki/Directed_acyclic_graph), in which
  8. each case technically is a "node".
  9. An example: in the case of taxonomy, this is the term id (tid).
  10. - label: the label associated with an item in the hierarchy. You may now it
  11. as "title" or something else similar.
  12. An example: in the case of taxonomy, this is the actual term.
  13. - item type: a per-level, human-readable name that describes what kind of
  14. items that level contains.
  15. - entity: an item is often associated with an entity. E.g. a term is usually
  16. associated with a node.
  17. - form element: a form element allows the developer to assign a new value to
  18. a #type property in a form item. Examples of form elements
  19. supported by Drupal core are: select, checkboxes, textfield.
  20. - form item: an instance of a form element, with various other properties
  21. defined, such as #title, #default_value and #description. These
  22. are used to define a form in Drupal.
  23. - Hierarchical Select: this is the name of the module.
  24. - hierarchical_select: this is the internal name of the Hierarchical Select
  25. form element.
  26. - hierarchical select: (note the difference in case) this is the part of the
  27. widget with the multiple selects.
  28. - dropbox: this is the part of the widget where the selections are stored when
  29. multiple selections are allowed.
  30. Form API usage
  31. --------------
  32. You have to make sure your form item is using the "hierarchical_select" form
  33. element type:
  34. $form['select_some_term'] = array(
  35. '#type' => 'hierarchical_select',
  36. '#title' => t('Select the tag you wish to use.'),
  37. '#size' => 1,
  38. '#config' => array(
  39. 'module' => 'hs_taxonomy',
  40. 'params' => array(
  41. 'vid' => $vid,
  42. ),
  43. 'save_lineage' => 0,
  44. 'enforce_deepest' => 0,
  45. 'entity_count' => 0,
  46. 'require_entity' => 0,
  47. 'resizable' => 1,
  48. 'level_labels' => array(
  49. 'status' => 0,
  50. 'labels' => array(
  51. 0 => t('Main category'),
  52. 1 => t('Subcategory'),
  53. 2 => t('Third level category'),
  54. ),
  55. ),
  56. 'dropbox' => array(
  57. 'status' => 0,
  58. 'title' => t('All selections'),
  59. 'limit' => 0,
  60. 'reset_hs' => 1,
  61. ),
  62. 'editability' => array(
  63. 'status' => 0,
  64. 'item_types' => array(),
  65. 'allowed_levels' => array(
  66. 0 => 0,
  67. 1 => 0,
  68. 2 => 1,
  69. ),
  70. 'allow_new_levels' => 0,
  71. 'max_levels' => 3,
  72. ),
  73. // These settings cannot be configured through the UI: they can only be
  74. // overridden through code.
  75. 'animation_delay' => 400,
  76. 'exclusive_lineages' => array(),
  77. 'render_flat_select' => 0,
  78. ),
  79. '#default_value' => '83',
  80. );
  81. Now, let's explain what we see here:
  82. 1) We've set the #type property to "hierarchical_select" instead of "select".
  83. 2) The #size property is inherited by the selects of the hierarchical select.
  84. You can use it to change a vertical size of the select (i.e. change how many
  85. items are displayed in the select, similar to a form select multiple).
  86. 3) There's a new property: #config. This must be an
  87. array. These are the items it can contain:
  88. - module (required)
  89. This will be passed through in the AJAX requests, to let Hierarchical
  90. Select know which module's hooks should be used.
  91. - params (optional, may be necessary for some implementations)
  92. An array of parameters that will also be passed through in every AJAX
  93. request.
  94. e.g. In the case of taxonomy, this is the vocabulary id (vid). In case of
  95. content_taxonomy, there's three parameters: vid, tid and depth (tid allows
  96. one to define a new root, depth allows one to limit the depth of the
  97. displayed hierarchy).
  98. - save_lineage (optional, defaults to 0)
  99. Triggers the lineage saving functionality. If enabled, the selection can
  100. consist of multiple values.
  101. - enforce_deepest (optional, defaults to 0)
  102. Triggers the enforcing of a selection in the deepest level. If enabled, the
  103. selection will always be a single value.
  104. - entity_count (optional, defaults to 0)
  105. Enables the display of entity counts, between parentheses, for each item in
  106. the hierarchy.
  107. - require_entity (optional, defaults to 0)
  108. Whether an item should only be displayed if it has at least one associated
  109. entity.
  110. - level_labels['status'] (optional, defaults to 0)
  111. Whether level labels should be enabled or not. When save_lineage is
  112. enabled, this will result in *empty* level labels.
  113. - level_labels['labels'] (optional)
  114. An array of labels, one per level. The label for the first level should be
  115. the value of key 0.
  116. When enforce_deepest is set to:
  117. - 0, then you can provide n level labels, with n the number of levels
  118. - 1, then you can provide only one level label.
  119. - dropbox['status'] (optional, defaults to 0)
  120. Whether the dropbox is enabled or not (the dropbox allows the user to make
  121. multiple selections).
  122. - dropbox['title'] (optional, defaults to "All selections:")
  123. The title of the dropbox. The dropbox is the area where all selections are
  124. displayed when the dropbox is enabled.
  125. - dropbox['limit'] (optional, defaults to 0, which means "no limit")
  126. Limit the number of selection that can be added to the dropbox. So this
  127. allows you the restrict the number of items that can be selected when
  128. the dropbox has been enabled.
  129. - dropbox['reset_hs'] (optional, defaults to 1, which means "do reset")
  130. Determines what will happen to the hierarchical select when the user has
  131. added a selection to the dropbox.
  132. - editability['status] (optional, defaults to 0)
  133. Allow the user to create new items in the hierarchy.
  134. - editability['item_types'] (optional, defaults to the empty array)
  135. Only meaningful when editable is set to TRUE.
  136. Set the item type for each level. E.g.: "country" for the first level,
  137. "region" for the second and "city" for the third. When the user then wants
  138. to create a new item, the default label for the new item will be of the
  139. form "new ", e.g. "new region".
  140. - editability['allowed_levels'] (optional, defaults to 1 for each level)
  141. Only meaningful when editable is set to TRUE.
  142. Specify in which levels the user is allowed to create new items. In the
  143. example, the user is only allowed to create new items in the third level.
  144. When a setting for a level is ommitted, it defaults to 1 (i.e. allowed for
  145. that level). This means you only have to specify in which levels the user
  146. is not allowed to create new items.
  147. This only applies to *existing* levels: it does not affect the
  148. allow_new_levels setting (the next setting).
  149. - editability['allow_new_levels'] (optional, defaults to 0)
  150. Only meaningful when editable is set to TRUE.
  151. Allow the user to create new levels, i.e. when a certain item does not yet
  152. have children, the user can create a first child for it (thus thereby
  153. creating a new level).
  154. - editability['max_levels'] (optional, defaults to 3)
  155. Only meaningful when editable_settings['allow_new_levels'] is set to TRUE.
  156. Limits the maximum number of levels. Don't set this too high or you'll end
  157. up with very deep hierarchies. This only affects how deep new levels can be
  158. created, it will not affect the existing hierarchy.
  159. - animation_delay (optional, defaults to 400)
  160. The delay of each animation (the drop in left and right animations), in ms.
  161. - exclusive_lineages (optional, defaults to the empty array)
  162. Sometimes, it's desirable to have exclusive lineages. When one of these
  163. lineages is selected, the user should not be able to select anything else.
  164. e.g. the **ALL** option in Views exposed filters.
  165. - render_flat_select (optional, defaults to 0)
  166. Because the hierarchical_select form element consists of multiple form
  167. items, it doesn't work well in GET forms. By enabling this setting, a flat
  168. select will also be rendered, that contains only the selected lineages.
  169. Combine that with Drupal.HierarchicalSelect.prepareGETSubmit in the JS code
  170. (or, alternatively, the 'prepare-GET-submit' event that can be triggered,
  171. see the JavaScript events section for details) and you have a work-around
  172. (which, admittedly, only works when JS is enabled).
  173. - resizable (optional, defaults to 1)
  174. Makes the hierarchical select resizable.
  175. 3) We *don't* specify a list of options: Hierarchical Select automatically
  176. generates the options for us, thanks to the 'module' and 'params' settings.
  177. Concepts
  178. --------
  179. - Item Unicity: each item in the hierarchy must be *unique*. It doesn't have
  180. to be numerical, it can also be a string.
  181. If your hierarchy does not have unique items by nature or by
  182. design (your items may be unique per level instead), that's
  183. not a problem. You can simply prepend the item's ancestors to
  184. get a unique item.
  185. e.g. you have an item "foobar" at the first, second and third
  186. levels. By prepending the ancestors using the dash as the
  187. separator, you'd get an item "foobar-foobar-foobar" at the
  188. third level.
  189. Also see the "Reserved item values" section.
  190. - #options: it's gone, because it was the inherent cause for scalability
  191. problems: if a hierarchy consists of 10,000 or even 100,000 items,
  192. this results in huge HTML being generated. Huge HTML means more
  193. processing power necessary, and more bandwidth necessary. So where
  194. does Hierarchical Select get its "options"? It uses the hooks that
  195. every implementation has to implement to only get what it needs.
  196. - The General Concept: you should think of Hierarchical Select as an abstract
  197. widget that can represent *any* hierarchy. To be able
  198. to display any hierarchy, you obviously need some
  199. universal way to "browse" a hierarchy.
  200. If you are familiar with C++ or Java iterators, this
  201. should come natural: the hooks you have to implement
  202. is what allows Hierarchical Select to iterate over your
  203. hierarchy. Then the heart of the iterator would be the
  204. root_level() and children() hooks. params() allows you
  205. to define which information is necessary before you can
  206. determine *which* hierarchy or which *part* of the
  207. hierarchy is being browsed. lineage() must return the
  208. lineage, i.e. the item itself and all its ancestors,
  209. this allows a hierarchy to be generated from just one
  210. (selected) item.
  211. Reserved item values
  212. --------------------
  213. - Ensure that your items don't have a "none", "all", "create_new_item" nor
  214. "label_\d+" values (the latter means "label_" followed by one or more
  215. digits). Your values should also not contain a pipe ("|"), since pipes are
  216. used to separate the selection of values that are sent back to the server
  217. in the callbacks.
  218. - Valid 'empty' selections (i.e. if you want to set the #default_value
  219. property of your form item), are -1 and the empty array. The empty string is
  220. also considered valid, because Drupal core's Taxonomy module uses this as
  221. the empty selection.
  222. Developer mode
  223. --------------
  224. When you are writing your implementation of the Hierarchical Select API, you
  225. will often wonder what Hierarchical Select is doing internally with the data
  226. you're feeding it. That's why there's a developer mode: it will show you this
  227. data, even the data generated in AJAX callbacks. It'll also show you the time
  228. it took to generate the lineage, to fill up the levels and to calculate the
  229. child info, to track down badly performing code.
  230. Also, when you're just creating a new HS config and it doesn't quite work
  231. right, it can be helpful to enable the developer mode. It will perform some
  232. basic diagnostics that might help you track down the cause.
  233. To use this, you must have a browser with console.log() support. Install
  234. Firebug Lite (http://getfirebug.com/lite.html) if your browser does not
  235. suport this. Next, go to Hierarchical Select's .module file and set the define
  236. for the HS_DEVELOPER_MODE constant to TRUE.
  237. When you now open Firebug (Firefox) or the Web Inspector (Safari), you'll see
  238. the debug output. New output is added after each callback to the server.
  239. Hierarchical Select implementations: gotcha's
  240. ---------------------------------------------
  241. - "warning: Missing argument 1 for drupal_retrieve_form() …"
  242. This implies that your implementation's module weight is heavier than
  243. hierarchical_select.module. In that case, Hierarchical Select will not be
  244. able to detect hierarchical_select form items, preventing it from applying
  245. some magic, and AJAX updates won't work.
  246. Hierarchical Select API Tutorial
  247. --------------------------------
  248. Written by Stephen Barker of Digital Frontiers Media
  249. (http://drupal.org/user/106070) and reviewed by Wim Leers:
  250. http://drupal.org/node/532724
  251. Hooks
  252. -----
  253. 1) hook_hierarchical_select_params();
  254. Returns an array with the names of all parameters that are necessary for
  255. this implementation to work.
  256. 2) hook_hierarchical_select_root_level($params, $dropbox = FALSE);
  257. Returns the root level of the hierarchy: an array of (item, label) pairs.
  258. The $dropbox parameter can is optional and can even ommitted, as it's only
  259. necessary if you need the dropbox to influence your hierarchy.
  260. 3) hook_hierarchical_select_children($parent, $params, $dropbox = FALSE);
  261. Gets the children of $parent ($parent is an item in the hierarchy) and
  262. returns them: an array of (item, label) pairs, or the empty array if the
  263. given $parent has no children.
  264. The $dropbox parameter can is optional and can even ommitted, as it's only
  265. necessary if you need the dropbox to influence your hierarchy.
  266. 4) hook_hierarchical_select_lineage($item, $params);
  267. Calculates the lineage of $item (array of items, with $item the last) and
  268. returns it. Necessary when the "enforce_deepest" option is enabled.
  269. 5) hook_hierarchical_select_valid_item($item, $params);
  270. Validates an item, returns TRUE if valid, FALSE if invalid.
  271. 6) hook_hierarchical_select_item_get_label($item, $params);
  272. Given a valid item, returns the label. Is only used for rendering the
  273. selections in the dropbox.
  274. 7) hook_hierarchical_select_create_item($label, $parent, $params);
  275. Given a parent item and the label of a new item, create a new item as a
  276. child of the parent item. When $parent == 0, this means a new item is being
  277. created at the root level.
  278. Optional hook. When this hook is not implemented, this functionality will
  279. never be used, even when you configure it that way in code.
  280. 8) hook_hierarchical_select_entity_count($item, $params);
  281. Given a item, get the number of entities (most of the time the entity type
  282. is 'node') that are related to the given item. Used for the entity_count
  283. and require_entity settings.
  284. Optional hook. When this hook is not implemented, this functionality will
  285. never be used, even when you configure it that way (i.e. when you enable
  286. the entity_count and require_entity settings).
  287. 9) hook_hierarchical_select_implementation_info();
  288. Return metadata about this implementation.
  289. This information is used to generate the implementations overview at
  290. admin/settings/hierarchical_select/implementations. The expected format is:
  291. array(
  292. 'hierarchy type' => t('Taxonomy'),
  293. 'entity type' => t('Node'),
  294. 'entity' => t('Story'),
  295. 'context type' => t('Node form'),
  296. 'context' => '',
  297. );
  298. another example:
  299. array(
  300. 'hierarchy type' => t('Taxonomy'),
  301. 'entity type' => t('Node'),
  302. 'entity' => '',
  303. 'context type' => t('Views exposed filter'),
  304. 'context' => t('some view'),
  305. );
  306. 10) hook_hierarchical_select_config_info();
  307. Return metadata about each available user-editable configuration for this
  308. implementation.
  309. Optional hook. This information is used to generate the configurations
  310. overview at admin/settings/hierarchical_select/configs. The expected
  311. format is:
  312. $config_info[$config_id] = array(
  313. 'config_id' => $config_id,
  314. 'hierarchy type' => t('Taxonomy'),
  315. 'hierarchy' => t($vocabulary->name),
  316. 'entity type' => t('Node'),
  317. 'entity' => implode(', ', array_map('t', $entities)),
  318. 'edit link' => "admin/content/taxonomy/edit/vocabulary/$vid",
  319. );
  320. Standardized configuration form
  321. -------------------------------
  322. Hierarchical Select 3 comes with a standardized configuration form:
  323. hierarchical_select_common_config_form(). This function accepts a lot of
  324. parameters, which allows you to use names typical to your module's hierarchy
  325. (e.g. 'leaf' instead of 'term' and 'tree' instead of 'vocabulary'). A submit
  326. handler is also provided, of course.
  327. An example:
  328. // I'm not configuring all parameters here. For an example of that, see one
  329. // of the included modules.
  330. $form['foobar_hierarchical_select_config'] = hierarchical_select_common_config_form($module, $params, $config_id, $defaults, $strings, $max_hierarchy_depth, $preview_is_required);
  331. // Add the the submit handler for the Hierarchical Select config form.
  332. $parents = array('foobar_hierarchical_select_config');
  333. $form['#submit']['hierarchical_select_common_config_form_submit'] = array($parents);
  334. Configuration management
  335. ------------------------
  336. It's now possible to export Hierarchical Select configurations, and there is a
  337. function to set the configuration of a certain Hierarchical Select. Combine
  338. the two and you can manage your Hierarchical Select configurations in code!
  339. An example:
  340. // The exported configuration.
  341. $config = array( … );
  342. $config_id = $config['config_id];
  343. // Apply the configuration.
  344. require_once(drupal_get_path('module', 'hierarchical_select') .'/includes/common.inc');
  345. hierarchical_select_common_config_set($config_id, $config);
  346. JavaScript events
  347. -----------------
  348. The Hierarchical Select module's JavaScript code triggers several events, to
  349. allow for advanced interactions.
  350. You can find all hierarchical_select form items using this selector:
  351. $('.hierarchical-select-wrapper');
  352. You can find a *specific* hierarchical_select form item using this selector:
  353. $('#hierarchical-select-x-wrapper');
  354. where x is a number, or more accurately: a hsid (hierarchical select id).
  355. Retrieving all hsids in the current document can be done like this:
  356. for (var hsid in Drupal.settings.HierarchicalSelect.settings) {
  357. // …
  358. }
  359. The following events are triggered:
  360. - change-hierarchical-select
  361. - update-hierarchical-select
  362. - create-new-item
  363. - cancel-new-item
  364. - add-to-dropbox
  365. - remove-from-dropbox
  366. - enforced-update
  367. - prepared-GET-submit
  368. All events are triggered *after* the animations have completed.
  369. However, it's often useful to do something *before* an event (especially
  370. because all of the above events perform an AJAX request to the server). So,
  371. the equivalent "before" events exist as well:
  372. - before-update-hierarchical-select
  373. - before-create-new-item
  374. - before-cancel-new-item
  375. - before-add-to-dropbox
  376. - before-remove-from-dropbox
  377. - before-enforced-update
  378. There is one exception: when the cache is enabled, the "before update
  379. hierarchical select" event will not be triggered. This makes sense, because
  380. updates from the cache are instantaneous.
  381. An example of binding a function to the 'create-new-item' event of the second
  382. (hsid == 1) hierarchical_select form item on the page:
  383. $('#hierarchical-select-1-wrapper')
  384. .bind('create-new-item', function() {
  385. // …
  386. });
  387. And finally, you can trigger a special event to enforce an update (this can be
  388. useful when you have changed a hierarchy through another form item, or for
  389. live previews, or …). You can then also pass additional information that will
  390. be POSTed. You can even disable normal updates, to manage that completely
  391. yourself via enforced updates. This allows you to write a Hierarchical Select
  392. implementation that gets some of its information ($params) from another form
  393. item!
  394. Suppose you'd like to enforce an update of the first (hsid == 0)
  395. hierarchical_select form item on the page:
  396. $('#hierarchical-select-0-wrapper')
  397. .trigger('enforce-update');
  398. Now let's move on to a more advanced example, in which we will disable normal
  399. updates and let another form item (here a select) provide a part of the
  400. information that will be used to render the Hierarchical Select. Effectively,
  401. this other form item will *influence* the hierarchy that will be presented by
  402. Hierarchical Select!
  403. $(document).ready(function() {
  404. Drupal.settings.specialfilter = {};
  405. // .specialfilter-first: a select form item
  406. // .specialfilter-second: a hierarchical_select form item
  407. update = function() {
  408. var selection = Drupal.settings.specialfilter.currentSelection;
  409. // Send an extra parameter via POST: dynamicParameter. This is the stored
  410. // selection.
  411. $('.specialfilter-second')
  412. .trigger('enforce-update',
  413. [
  414. { name : 'dynamicParameter', value : selection }
  415. ]
  416. );
  417. };
  418. attachHSBindings = function() {
  419. // When a user navigates the hierarchical_select form item, we still want to
  420. // POST the the extra dynamicParameter, or otherwise we will no longer have
  421. // a hierarchy in the hierarchical_select form item that really depends on
  422. // the select.
  423. $('.specialfilter-second .hierarchical-select > select')
  424. .change(function() { update(); });
  425. $('.specialfilter-second')
  426. .unbind('enforced-update').bind('enforced-update', function() { return attachHSBindings(); });
  427. };
  428. // Initialize after 25 ms, because otherwise the event binding of HS will
  429. // not yet be ready, and hence this won't have any effect
  430. setTimeout(function() {
  431. // Get the initial selection (before the user has changed anything).
  432. Drupal.settings.specialfilter.currentSelection = $('.specialfilter-first').attr('value');
  433. // When the select form item changes, we want to *store* that selection, and
  434. // update the hierarchical_select form item.
  435. $('.specialfilter-first')
  436. .change(function() {
  437. // Store the current selection.
  438. Drupal.settings.specialfilter.currentSelection = $(this).attr('value');
  439. update();
  440. });
  441. $('.specialfilter-second')
  442. .trigger('disable-updates');
  443. attachHSBindings();
  444. }, 25);
  445. });
  446. The 'enforced-update' (notice the past tense!) event is triggered upon
  447. completion.
  448. An even more rarely used special event can be triggered to prepare the
  449. hierarchical_select form element for a get submit: the 'prepare GET submit'
  450. event. To use this event, the 'render_flat_select' setting should be enabled
  451. in the config.