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