/[drupal]/contributions/modules/hierarchical_select/API.txt
ViewVC logotype

Contents of /contributions/modules/hierarchical_select/API.txt

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


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

  ViewVC Help
Powered by ViewVC 1.1.2