1 | <?php |
---|
2 | |
---|
3 | /** |
---|
4 | * @file |
---|
5 | * This module defines the "hierarchical_select" form element, which is a |
---|
6 | * greatly enhanced way for letting the user select items in a hierarchy. |
---|
7 | */ |
---|
8 | |
---|
9 | |
---|
10 | // Make sure that the devel module is installed when you enable developer mode! |
---|
11 | define('HS_DEVELOPER_MODE', 0); |
---|
12 | |
---|
13 | // 6 hours cache life time for forms should be plenty. |
---|
14 | define('HS_CACHE_LIFETIME_DEFAULT', 21600); |
---|
15 | |
---|
16 | |
---|
17 | //---------------------------------------------------------------------------- |
---|
18 | // Drupal core hooks. |
---|
19 | |
---|
20 | /** |
---|
21 | * Implementation of hook_menu(). |
---|
22 | */ |
---|
23 | function hierarchical_select_menu() { |
---|
24 | $items['hierarchical_select_json'] = array( |
---|
25 | 'page callback' => 'hierarchical_select_json', |
---|
26 | 'type' => MENU_CALLBACK, |
---|
27 | // TODO: Needs improvements. Ideally, this would inherit the permissions |
---|
28 | // of the form the Hierarchical Select was in. |
---|
29 | 'access callback' => TRUE, |
---|
30 | ); |
---|
31 | $items['admin/settings/hierarchical_select'] = array( |
---|
32 | 'title' => 'Hierarchical Select', |
---|
33 | 'description' => 'Configure site-wide settings for the Hierarchical Select form element.', |
---|
34 | 'access arguments' => array('administer site configuration'), |
---|
35 | 'page callback' => 'drupal_get_form', |
---|
36 | 'page arguments' => array('hierarchical_select_admin_settings'), |
---|
37 | 'type' => MENU_NORMAL_ITEM, |
---|
38 | 'file' => 'hierarchical_select.admin.inc', |
---|
39 | ); |
---|
40 | $items['admin/settings/hierarchical_select/settings'] = array( |
---|
41 | 'title' => 'Site-wide settings', |
---|
42 | 'access arguments' => array('administer site configuration'), |
---|
43 | 'weight' => -10, |
---|
44 | 'type' => MENU_DEFAULT_LOCAL_TASK, |
---|
45 | 'file' => 'hierarchical_select.admin.inc', |
---|
46 | ); |
---|
47 | $items['admin/settings/hierarchical_select/configs'] = array( |
---|
48 | 'title' => 'Configurations', |
---|
49 | 'description' => 'All available Hierarchical Select configurations.', |
---|
50 | 'access arguments' => array('administer site configuration'), |
---|
51 | 'page callback' => 'hierarchical_select_admin_configs', |
---|
52 | 'type' => MENU_LOCAL_TASK, |
---|
53 | 'file' => 'hierarchical_select.admin.inc', |
---|
54 | ); |
---|
55 | $items['admin/settings/hierarchical_select/implementations'] = array( |
---|
56 | 'title' => 'Implementations', |
---|
57 | 'description' => 'Features of each Hierarchical Select implementation.', |
---|
58 | 'access arguments' => array('administer site configuration'), |
---|
59 | 'page callback' => 'hierarchical_select_admin_implementations', |
---|
60 | 'type' => MENU_LOCAL_TASK, |
---|
61 | 'file' => 'hierarchical_select.admin.inc', |
---|
62 | ); |
---|
63 | $items['admin/settings/hierarchical_select/export/%hierarchical_select_config_id'] = array( |
---|
64 | 'title' => 'Export', |
---|
65 | 'access arguments' => array('administer site configuration'), |
---|
66 | 'page callback' => 'drupal_get_form', |
---|
67 | 'page arguments' => array('hierarchical_select_admin_export', 4), |
---|
68 | 'type' => MENU_LOCAL_TASK, |
---|
69 | 'file' => 'hierarchical_select.admin.inc', |
---|
70 | ); |
---|
71 | $items['admin/settings/hierarchical_select/import/%hierarchical_select_config_id'] = array( |
---|
72 | 'title' => 'Import', |
---|
73 | 'access arguments' => array('administer site configuration'), |
---|
74 | 'page callback' => 'drupal_get_form', |
---|
75 | 'page arguments' => array('hierarchical_select_admin_import', 4), |
---|
76 | 'type' => MENU_LOCAL_TASK, |
---|
77 | 'file' => 'hierarchical_select.admin.inc', |
---|
78 | ); |
---|
79 | |
---|
80 | return $items; |
---|
81 | } |
---|
82 | |
---|
83 | /** |
---|
84 | * Implementation of hook_flush_caches(). |
---|
85 | */ |
---|
86 | function hierarchical_select_flush_caches() { |
---|
87 | return array('cache_hierarchical_select'); |
---|
88 | } |
---|
89 | |
---|
90 | /** |
---|
91 | * Implementation of hook_form_alter(). |
---|
92 | */ |
---|
93 | function hierarchical_select_form_alter(&$form, $form_state, $form_id) { |
---|
94 | if (_hierarchical_select_form_has_hierarchical_select($form)) { |
---|
95 | $form['#after_build'][] = 'hierarchical_select_after_build'; |
---|
96 | } |
---|
97 | } |
---|
98 | |
---|
99 | /** |
---|
100 | * Implementation of hook_elements(). |
---|
101 | */ |
---|
102 | function hierarchical_select_elements() { |
---|
103 | $type['hierarchical_select'] = array( |
---|
104 | '#input' => TRUE, |
---|
105 | '#process' => array('hierarchical_select_process'), |
---|
106 | '#config' => array( |
---|
107 | 'module' => 'some_module', |
---|
108 | 'params' => array(), |
---|
109 | 'save_lineage' => 0, |
---|
110 | 'enforce_deepest' => 0, |
---|
111 | 'entity_count' => 0, |
---|
112 | 'require_entity' => 0, |
---|
113 | 'resizable' => 1, |
---|
114 | 'level_labels' => array( |
---|
115 | 'status' => 0, |
---|
116 | 'labels' => array(), |
---|
117 | ), |
---|
118 | 'dropbox' => array( |
---|
119 | 'status' => 0, |
---|
120 | 'title' => t('All selections'), |
---|
121 | 'limit' => 0, |
---|
122 | 'reset_hs' => 1, |
---|
123 | ), |
---|
124 | 'editability' => array( |
---|
125 | 'status' => 0, |
---|
126 | 'item_types' => array(), |
---|
127 | 'allowed_levels' => array(), |
---|
128 | 'allow_new_levels' => 0, |
---|
129 | 'max_levels' => 3, |
---|
130 | ), |
---|
131 | 'animation_delay' => variable_get('hierarchical_select_animation_delay', 400), |
---|
132 | 'special_items' => array(), |
---|
133 | 'render_flat_select' => 0, |
---|
134 | 'path' => 'hierarchical_select_json', |
---|
135 | ), |
---|
136 | '#default_value' => -1, |
---|
137 | ); |
---|
138 | return $type; |
---|
139 | } |
---|
140 | |
---|
141 | /** |
---|
142 | * Implementation of hook_requirements(). |
---|
143 | */ |
---|
144 | function hierarchical_select_requirements($phase) { |
---|
145 | $requirements = array(); |
---|
146 | |
---|
147 | if ($phase == 'runtime') { |
---|
148 | // Check if all hook_update_n() hooks have been executed. |
---|
149 | require_once('includes/install.inc'); |
---|
150 | drupal_load_updates(); |
---|
151 | $updates = drupal_get_schema_versions('hierarchical_select'); |
---|
152 | $current = drupal_get_installed_schema_version('hierarchical_select'); |
---|
153 | |
---|
154 | $up_to_date = (end($updates) == $current); |
---|
155 | |
---|
156 | $hierarchical_select_weight = db_result(db_query("SELECT weight FROM {system} WHERE type = 'module' AND name = 'hierarchical_select'")); |
---|
157 | $core_overriding_modules = array('hs_book', 'hs_menu', 'hs_taxonomy'); |
---|
158 | $path_errors = array(); |
---|
159 | foreach ($core_overriding_modules as $module) { |
---|
160 | $filename = db_result(db_query("SELECT filename FROM {system} WHERE type = 'module' AND name = '%s'", $module)); |
---|
161 | if (strpos($filename, 'modules/') === 0) { |
---|
162 | $module_info = drupal_parse_info_file(dirname($filename) ."/$module.info"); |
---|
163 | $path_errors[] = t('!module', array('!module' => $module_info['name'])); |
---|
164 | } |
---|
165 | } |
---|
166 | $weight_errors = array(); |
---|
167 | foreach (module_implements('hierarchical_select_root_level') as $module) { |
---|
168 | $weight = db_result(db_query("SELECT weight FROM {system} WHERE name = '%s'", $module)); |
---|
169 | if (!($hierarchical_select_weight > $weight)) { |
---|
170 | $filename = db_result(db_query("SELECT filename FROM {system} WHERE type = 'module' AND name = '%s'", $module)); |
---|
171 | $module_info = drupal_parse_info_file(dirname($filename) ."/$module.info"); |
---|
172 | $weight_errors[] = t('!module (!weight)', array('!module' => $module_info['name'], '!weight' => $weight)); |
---|
173 | } |
---|
174 | } |
---|
175 | |
---|
176 | if ($up_to_date && !count($path_errors) && !count($weight_errors)) { |
---|
177 | $value = t('All updates installed. Implementation modules are installed correctly.'); |
---|
178 | $description = ''; |
---|
179 | $severity = REQUIREMENT_OK; |
---|
180 | } |
---|
181 | elseif ($path_errors) { |
---|
182 | $value = t('Modules incorrectly installed!'); |
---|
183 | $description = t( |
---|
184 | "The following modules implement Hierarchical Select module for Drupal |
---|
185 | core modules, but are installed in the wrong location. They're |
---|
186 | installed in core's <code>modules</code> directory, but should be |
---|
187 | installed in either the <code>sites/all/modules</code> directory or a |
---|
188 | <code>sites/yoursite.com/modules</code> directory" |
---|
189 | ) .':'. theme('item_list', $path_errors); |
---|
190 | $severity = REQUIREMENT_ERROR; |
---|
191 | } |
---|
192 | elseif ($weight_errors) { |
---|
193 | $value = t('Module weight incorrectly configured!'); |
---|
194 | $description = t( |
---|
195 | 'The weight of the Hierarchical Select module (!weight) is not |
---|
196 | strictly higher than the weight of the following modules', |
---|
197 | array('!weight' => $hierarchical_select_weight) |
---|
198 | ) .':'. theme('item_list', $weight_errors); |
---|
199 | $severity = REQUIREMENT_ERROR; |
---|
200 | } |
---|
201 | else { |
---|
202 | $value = t('Not all updates installed!'); |
---|
203 | $description = t('Please run update.php to install the latest updates! |
---|
204 | You have installed update !installed_update, but the latest update is |
---|
205 | !latest_update!', |
---|
206 | array( |
---|
207 | '!installed_update' => $current, |
---|
208 | '!latest_update' => end($updates), |
---|
209 | ) |
---|
210 | ); |
---|
211 | $severity = REQUIREMENT_ERROR; |
---|
212 | } |
---|
213 | |
---|
214 | $requirements['hierarchical_select'] = array( |
---|
215 | 'title' => t('Hierarchical Select'), |
---|
216 | 'value' => $value, |
---|
217 | 'description' => $description, |
---|
218 | 'severity' => $severity, |
---|
219 | ); |
---|
220 | } |
---|
221 | |
---|
222 | return $requirements; |
---|
223 | } |
---|
224 | |
---|
225 | /** |
---|
226 | * Implementation of hook_theme(). |
---|
227 | */ |
---|
228 | function hierarchical_select_theme() { |
---|
229 | return array( |
---|
230 | 'hierarchical_select_form_element' => array( |
---|
231 | 'file' => 'includes/theme.inc', |
---|
232 | 'arguments' => array('element' => NULL, 'value' => NULL), |
---|
233 | ), |
---|
234 | 'hierarchical_select' => array( |
---|
235 | 'file' => 'includes/theme.inc', |
---|
236 | 'arguments' => array('element' => NULL), |
---|
237 | ), |
---|
238 | 'hierarchical_select_selects_container' => array( |
---|
239 | 'file' => 'includes/theme.inc', |
---|
240 | 'arguments' => array('element' => NULL), |
---|
241 | ), |
---|
242 | 'hierarchical_select_select' => array( |
---|
243 | 'file' => 'includes/theme.inc', |
---|
244 | 'arguments' => array('element' => NULL), |
---|
245 | ), |
---|
246 | 'hierarchical_select_special_option' => array( |
---|
247 | 'file' => 'includes/theme.inc', |
---|
248 | 'arguments' => array('option' => NULL), |
---|
249 | ), |
---|
250 | 'hierarchical_select_textfield' => array( |
---|
251 | 'file' => 'includes/theme.inc', |
---|
252 | 'arguments' => array('element' => NULL), |
---|
253 | ), |
---|
254 | 'hierarchical_select_dropbox_table' => array( |
---|
255 | 'file' => 'includes/theme.inc', |
---|
256 | 'arguments' => array('element' => NULL), |
---|
257 | ), |
---|
258 | 'hierarchical_select_common_config_form_level_labels' => array( |
---|
259 | 'file' => 'includes/theme.inc', |
---|
260 | 'arguments' => array('form' => NULL), |
---|
261 | ), |
---|
262 | 'hierarchical_select_common_config_form_editability' => array( |
---|
263 | 'file' => 'includes/theme.inc', |
---|
264 | 'arguments' => array('form' => NULL), |
---|
265 | ), |
---|
266 | 'hierarchical_select_selection_as_lineages' => array( |
---|
267 | 'file' => 'includes/theme.inc', |
---|
268 | 'arguments' => array( |
---|
269 | 'selection' => NULL, |
---|
270 | 'config' => NULL, |
---|
271 | ), |
---|
272 | ), |
---|
273 | ); |
---|
274 | } |
---|
275 | |
---|
276 | /** |
---|
277 | * Implementation of hook_simpletest(). |
---|
278 | */ |
---|
279 | function hierarchical_select_simpletest() { |
---|
280 | $dir = drupal_get_path('module', 'hierarchical_select') .'/tests'; |
---|
281 | $tests = file_scan_directory($dir, '\.test$'); |
---|
282 | return array_keys($tests); |
---|
283 | } |
---|
284 | |
---|
285 | /** |
---|
286 | * Implementation of hook_features_api(). |
---|
287 | */ |
---|
288 | function hierarchical_select_features_api() { |
---|
289 | return array( |
---|
290 | 'hierarchical_select' => array( |
---|
291 | 'name' => t('Hierarchical select configs'), |
---|
292 | 'feature_source' => TRUE, |
---|
293 | 'default_hook' => 'hierarchical_select_default_configs', |
---|
294 | 'default_file' => FEATURES_DEFAULTS_INCLUDED, |
---|
295 | 'file' => drupal_get_path('module', 'hierarchical_select') .'/hierarchical_select.features.inc', |
---|
296 | ), |
---|
297 | ); |
---|
298 | } |
---|
299 | |
---|
300 | //---------------------------------------------------------------------------- |
---|
301 | // Menu system callbacks. |
---|
302 | |
---|
303 | /** |
---|
304 | * Wildcard loader for Hierarchical Select config ID's. |
---|
305 | */ |
---|
306 | function hierarchical_select_config_id_load($config_id) { |
---|
307 | $config = variable_get('hs_config_'. $config_id, FALSE); |
---|
308 | return ($config !== FALSE) ? $config['config_id'] : FALSE; |
---|
309 | } |
---|
310 | |
---|
311 | /** |
---|
312 | * Menu callback; format=text/json; generates and outputs the appropriate HTML. |
---|
313 | */ |
---|
314 | function hierarchical_select_json() { |
---|
315 | // We are returning Javascript, so tell the browser. Ripped from Drupal 6's |
---|
316 | // drupal_json() function. |
---|
317 | drupal_set_header('Content-Type: text/javascript; charset=utf-8'); |
---|
318 | |
---|
319 | $hs_form_build_id = $_POST['hs_form_build_id']; |
---|
320 | |
---|
321 | // In this context, we're in a node selection mode. |
---|
322 | // See http://drupal.org/node/683574#comment-3117926. |
---|
323 | if (module_exists('i18n') && isset($_POST['language'])) { |
---|
324 | i18n_selection_mode('node', $_POST['language']); |
---|
325 | } |
---|
326 | |
---|
327 | // Collect all necessary variables. |
---|
328 | $cached = cache_get($hs_form_build_id, 'cache_hierarchical_select'); |
---|
329 | $storage = $cached->data; |
---|
330 | |
---|
331 | // Ensure that the form id in the POST array is the same as the one of the |
---|
332 | // stored parameters of the original form. For 99% of the forms, this step |
---|
333 | // is not necessary, but when a hierarchical_select form item is inside a |
---|
334 | // form in a subform_element in a form, then it is necessary. |
---|
335 | $form_id = $_POST['form_id'] = $storage['parameters'][0]; |
---|
336 | |
---|
337 | if (HS_DEVELOPER_MODE) { |
---|
338 | _hierarchical_select_log("form_id: $form_id"); |
---|
339 | _hierarchical_select_log("hs_form_build_id: $hs_form_build_id"); |
---|
340 | } |
---|
341 | |
---|
342 | $form_state = &$storage['parameters'][1]; |
---|
343 | |
---|
344 | // Include the file in which the form definition function lives. |
---|
345 | if (!empty($storage['file'])) { |
---|
346 | require_once($storage['file']); |
---|
347 | } |
---|
348 | |
---|
349 | // Also include files set in $form_state['form_load_files']. Set by CTools |
---|
350 | // Delegator, which is used by Panels (i.e. this is necessary for Panels |
---|
351 | // compatibility). |
---|
352 | if (isset($form_state['form_load_files'])) { |
---|
353 | foreach ($form_state['form_load_files'] as $file) { |
---|
354 | require_once './' . $file; |
---|
355 | } |
---|
356 | } |
---|
357 | |
---|
358 | // Retrieve and process the form. |
---|
359 | $form = call_user_func_array('drupal_retrieve_form', $storage['parameters']); |
---|
360 | drupal_prepare_form($form_id, $form, $form_state); |
---|
361 | $form['#post'] = $_POST; |
---|
362 | $form = form_builder($form_id, $form, $form_state); |
---|
363 | |
---|
364 | // Render only the relevant part of the form (i.e. the hierarchical_select |
---|
365 | // form item that has triggered this AJAX callback). |
---|
366 | $hsid = $_POST['hsid']; |
---|
367 | $name = $storage['#names'][$hsid]; |
---|
368 | $part_of_form = _hierarchical_select_get_form_item($form, $name); |
---|
369 | $output = drupal_render($part_of_form); |
---|
370 | |
---|
371 | // If the user's browser supports the active cache system, then send the |
---|
372 | // currently requested hierarchy in an easy-to-manage form. |
---|
373 | $cache = array(); |
---|
374 | if (isset($_POST['client_supports_caching'])) { |
---|
375 | if ($_POST['client_supports_caching'] == 'true') { |
---|
376 | $cache = _hierarchical_select_json_convert_hierarchy_to_cache($part_of_form['hierarchy']['#value']); |
---|
377 | } |
---|
378 | else if ($_POST['client_supports_caching'] == 'false') { |
---|
379 | // This indicates that a client-side cache is installed, but not working |
---|
380 | // properly. |
---|
381 | // TODO: figure out a clean way to notify the administrator. |
---|
382 | } |
---|
383 | } |
---|
384 | |
---|
385 | print drupal_to_js(array( |
---|
386 | 'cache' => $cache, |
---|
387 | 'output' => $output, |
---|
388 | 'log' => (isset($part_of_form['log']['#value'])) ? $part_of_form['log']['#value'] : NULL, |
---|
389 | )); |
---|
390 | exit; |
---|
391 | } |
---|
392 | |
---|
393 | |
---|
394 | //---------------------------------------------------------------------------- |
---|
395 | // Forms API callbacks. |
---|
396 | |
---|
397 | /** |
---|
398 | * Hierarchical select form element type #process callback. |
---|
399 | */ |
---|
400 | function hierarchical_select_process($element, $edit, &$form_state, $form) { |
---|
401 | if (!is_array($element['#value']) || !isset($element['#value']['hsid'])) { |
---|
402 | // The HSID is stored in the session, to allow for multiple Hierarchical |
---|
403 | // Select form items on the same page of which at least one is added through |
---|
404 | // AHAH. A normal static variable won't do in this case, because then at |
---|
405 | // least two Hierarchical Select form items will have HSID 0, because they |
---|
406 | // are generated in different requests, both of which will have a first HSID |
---|
407 | // of 0. This will then cause problems on the page. |
---|
408 | if (!isset($_SESSION['hsid'])) { |
---|
409 | $_SESSION['hsid'] = 0; |
---|
410 | } |
---|
411 | else { |
---|
412 | // Let the HSID go from 0 to 99, then start over. Larger numbers are |
---|
413 | // pointless: who's going to use more than a hundred Hierarchical Select |
---|
414 | // form items on the same page? |
---|
415 | $_SESSION['hsid'] = ($_SESSION['hsid'] + 1) % 100; |
---|
416 | } |
---|
417 | $hsid = $_SESSION['hsid']; |
---|
418 | } |
---|
419 | else { |
---|
420 | $hsid = check_plain($element['#value']['hsid']); |
---|
421 | } |
---|
422 | $element['hsid'] = array('#type' => 'hidden', '#value' => $hsid); |
---|
423 | // A hierarchical_select form element expands to multiple items. For example |
---|
424 | // $element['hsid'] got set just above. If #value is not an array, then |
---|
425 | // form_set_value(), which is called by form_builder() will fail, because it |
---|
426 | // assumes that #value is an array, because we are trying to set a child of |
---|
427 | // it. |
---|
428 | if (!is_array($element['#value'])) { |
---|
429 | $element['#value'] = array($element['#value']); |
---|
430 | } |
---|
431 | |
---|
432 | // Store the #name property of each hierarchical_select form item, this is |
---|
433 | // necessary to find this form item back in an AJAX callback. |
---|
434 | _hierarchical_select_store_name($element, $hsid); |
---|
435 | |
---|
436 | // Get the config and convert the 'special_items' setting to a more easily |
---|
437 | // accessible format. |
---|
438 | $config = $element['#config']; |
---|
439 | if (isset($config['special_items'])) { |
---|
440 | $special_items['exclusive'] = array_keys(array_filter($config['special_items'], '_hierarchical_select_special_item_exclusive')); |
---|
441 | $special_items['none'] = array_keys(array_filter($config['special_items'], '_hierarchical_select_special_item_none')); |
---|
442 | } |
---|
443 | |
---|
444 | // Set up Javascript and add settings specifically for the current |
---|
445 | // hierarchical select. |
---|
446 | $config = _hierarchical_select_inherit_default_config($element['#config']); |
---|
447 | _hierarchical_select_setup_js(); |
---|
448 | _hierarchical_select_setup_js($form_state); |
---|
449 | $settings = array( |
---|
450 | 'HierarchicalSelect' => array( |
---|
451 | 'settings' => array( |
---|
452 | $hsid => array( |
---|
453 | 'animationDelay' => ($config['animation_delay'] == 0) ? (int) variable_get('hierarchical_select_animation_delay', 400) : $config['animation_delay'], |
---|
454 | 'cacheId' => $config['module'] .'_'. implode('_', (is_array($config['params'])) ? $config['params'] : array()), |
---|
455 | 'renderFlatSelect' => (isset($config['render_flat_select'])) ? (int) $config['render_flat_select'] : 0, |
---|
456 | 'createNewItems' => (isset($config['editability']['status'])) ? (int) $config['editability']['status'] : 0, |
---|
457 | 'createNewLevels' => (isset($config['editability']['allow_new_levels'])) ? (int) $config['editability']['allow_new_levels'] : 0, |
---|
458 | 'resizable' => (isset($config['resizable'])) ? (int) $config['resizable'] : 0, |
---|
459 | 'path' => $config['path'], |
---|
460 | ), |
---|
461 | ), |
---|
462 | ) |
---|
463 | ); |
---|
464 | _hierarchical_select_add_js_settings($settings, $form_state); |
---|
465 | |
---|
466 | // Basic config validation and diagnostics. |
---|
467 | if (HS_DEVELOPER_MODE) { |
---|
468 | $diagnostics = array(); |
---|
469 | if (!isset($config['module']) || empty($config['module'])) { |
---|
470 | $diagnostics[] = t("'module is not set!"); |
---|
471 | } |
---|
472 | elseif (!module_exists($config['module'])) { |
---|
473 | $diagnostics[] = t('the module that should be used (module) is not installed!', array('%module' => $config['module'])); |
---|
474 | } |
---|
475 | else { |
---|
476 | $required_params = module_invoke($config['module'], 'hierarchical_select_params'); |
---|
477 | $missing_params = array_diff($required_params, array_keys($config['params'])); |
---|
478 | if (!empty($missing_params)) { |
---|
479 | $diagnostics[] = t("'params' is missing values for: ") . implode(', ', $missing_params) .'.'; |
---|
480 | } |
---|
481 | } |
---|
482 | $config_id = (isset($config['config_id']) && is_string($config['config_id'])) ? $config['config_id'] : 'none'; |
---|
483 | if (empty($diagnostics)) { |
---|
484 | _hierarchical_select_log("Config diagnostics (config id: $config_id): no problems found!"); |
---|
485 | } |
---|
486 | else { |
---|
487 | $diagnostics_string = print_r($diagnostics, TRUE); |
---|
488 | $message = "Config diagnostics (config id: $config_id): $diagnostics_string"; |
---|
489 | _hierarchical_select_log($message); |
---|
490 | $element['#type']= 'item'; |
---|
491 | $element['#value'] = '<p><span style="color:red;">Fix the indicated errors in the #config property first!</span><br />'. nl2br($message) .'</p>'; |
---|
492 | return $element; |
---|
493 | } |
---|
494 | } |
---|
495 | |
---|
496 | // Calculate the selections in both the hierarchical select and the dropbox, |
---|
497 | // we need these before we can render anything. |
---|
498 | list($hs_selection, $db_selection) = _hierarchical_select_process_calculate_selections($element); |
---|
499 | |
---|
500 | if (HS_DEVELOPER_MODE) { |
---|
501 | _hierarchical_select_log("Calculated hierarchical select selection:"); |
---|
502 | _hierarchical_select_log($hs_selection); |
---|
503 | |
---|
504 | if ($config['dropbox']['status']) { |
---|
505 | _hierarchical_select_log("Calculated dropbox selection:"); |
---|
506 | _hierarchical_select_log($db_selection); |
---|
507 | } |
---|
508 | } |
---|
509 | |
---|
510 | // If: |
---|
511 | // - the special_items setting has been configured |
---|
512 | // - at least one special item has the 'exclusive' property |
---|
513 | // - the dropbox is enabled |
---|
514 | // then do the necessary processing to make exclusive lineages possible. |
---|
515 | if (isset($special_items) && count($special_items['exclusive']) && $config['dropbox']['status']) { |
---|
516 | // When the form is first loaded, $db_selection will contain the selection |
---|
517 | // that we should check, but in updates, $hs_selection will. |
---|
518 | $selection = (!empty($hs_selection)) ? $hs_selection : $db_selection; |
---|
519 | |
---|
520 | // If the current selection of the hierarchical select matches one of the |
---|
521 | // configured exclusive items, then disable the dropbox (to ensure an |
---|
522 | // exclusive selection). |
---|
523 | $exclusive_item = array_intersect($selection, $special_items['exclusive']); |
---|
524 | if (count($exclusive_item)) { |
---|
525 | // By also updating the configuration stored in $element, we ensure that |
---|
526 | // the validation step, which extracts the configuration again, also gets |
---|
527 | // the updated config. |
---|
528 | $element['#config']['dropbox']['status'] = 0; |
---|
529 | $config = _hierarchical_select_inherit_default_config($element['#config']); |
---|
530 | |
---|
531 | // Set the hierarchical select to the exclusive item and make the |
---|
532 | // dropbox empty. |
---|
533 | $hs_selection = array(0 => reset($exclusive_item)); |
---|
534 | $db_selection = array(); |
---|
535 | } |
---|
536 | } |
---|
537 | |
---|
538 | // Generate the $hierarchy and $dropbox objects using the selections that |
---|
539 | // were just calculated. |
---|
540 | $dropbox = (!$config['dropbox']['status']) ? FALSE : _hierarchical_select_dropbox_generate($config, $db_selection); |
---|
541 | $hierarchy = _hierarchical_select_hierarchy_generate($config, $hs_selection, $element['#required'], $dropbox); |
---|
542 | |
---|
543 | if (HS_DEVELOPER_MODE) { |
---|
544 | _hierarchical_select_log('Generated hierarchy in '. $hierarchy->build_time['total'] .' ms:'); |
---|
545 | _hierarchical_select_log($hierarchy); |
---|
546 | |
---|
547 | if ($config['dropbox']['status']) { |
---|
548 | _hierarchical_select_log('Generated dropbox in '. $dropbox->build_time .' ms: '); |
---|
549 | _hierarchical_select_log($dropbox); |
---|
550 | } |
---|
551 | } |
---|
552 | |
---|
553 | // Store the hierarchy object in the element, we'll need this if the user's |
---|
554 | // browser supports the active cache system. |
---|
555 | $element['hierarchy'] = array('#type' => 'value', '#value' => $hierarchy); |
---|
556 | |
---|
557 | |
---|
558 | // Ensure that #tree is enabled! |
---|
559 | $element['#tree'] = TRUE; |
---|
560 | |
---|
561 | // If render_flat_select is enabled, render a flat select. |
---|
562 | if ($config['render_flat_select']) { |
---|
563 | $element['flat_select'] = _hierarchical_select_process_render_flat_select($hierarchy, $dropbox, $config); |
---|
564 | } |
---|
565 | |
---|
566 | // Render the hierarchical select. |
---|
567 | $element['hierarchical_select'] = array( |
---|
568 | '#theme' => 'hierarchical_select_selects_container', |
---|
569 | ); |
---|
570 | $element['hierarchical_select']['selects'] = _hierarchical_select_process_render_hs_selects($hsid, $hierarchy); |
---|
571 | |
---|
572 | // The selects in the hierarchical select should inherit the #size property. |
---|
573 | foreach (element_children($element['hierarchical_select']['selects']) as $depth) { |
---|
574 | $element['hierarchical_select']['selects'][$depth]['#size'] = isset($element['#size']) ? $element['#size'] : 0; |
---|
575 | } |
---|
576 | |
---|
577 | // Check if a new item is being created. |
---|
578 | $creating_new_item = FALSE; |
---|
579 | if (isset($element['#value']['hierarchical_select']['selects'])) { |
---|
580 | foreach ($element['#value']['hierarchical_select']['selects'] as $depth => $value) { |
---|
581 | if ($value == 'create_new_item' && _hierarchical_select_create_new_item_is_allowed($config, $depth)) { |
---|
582 | $creating_new_item = TRUE; |
---|
583 | |
---|
584 | // We want to override the select in which the "create_new_item" |
---|
585 | // option was selected and hide all selects after that, if they exist. |
---|
586 | for ($i = $depth; $i < count($hierarchy->lineage); $i++) { |
---|
587 | unset($element['hierarchical_select']['selects'][$i]); |
---|
588 | } |
---|
589 | |
---|
590 | $element['hierarchical_select']['create_new_item'] = array( |
---|
591 | '#prefix' => '<div class="'. str_replace('_', '-', $value) .'">', |
---|
592 | '#suffix' => '</div>', |
---|
593 | ); |
---|
594 | |
---|
595 | $item_type_depth = ($value == 'create_new_item') ? $depth : $depth + 1; |
---|
596 | $item_type = (!empty($config['editability']['item_types'][$item_type_depth])) ? t($config['editability']['item_types'][$item_type_depth]) : t('item'); |
---|
597 | |
---|
598 | $element['hierarchical_select']['create_new_item']['input'] = array( |
---|
599 | '#type' => 'textfield', |
---|
600 | '#size' => 20, |
---|
601 | '#maxlength' => 255, |
---|
602 | '#default_value' => t('new @item', array('@item' => $item_type)), |
---|
603 | '#attributes' => array( |
---|
604 | 'title' => t('new @item', array('@item' => $item_type)), |
---|
605 | 'class' => 'create-new-item-input' |
---|
606 | ), |
---|
607 | // Use a #theme callback to prevent the textfield from being wrapped |
---|
608 | // in a div. This simplifies the CSS and JS code. |
---|
609 | '#theme' => 'hierarchical_select_textfield', |
---|
610 | ); |
---|
611 | |
---|
612 | $element['hierarchical_select']['create_new_item']['create'] = array( |
---|
613 | '#type' => 'button', |
---|
614 | '#value' => t('Create'), |
---|
615 | '#attributes' => array('class' => 'create-new-item-create'), |
---|
616 | ); |
---|
617 | |
---|
618 | $element['hierarchical_select']['create_new_item']['cancel'] = array( |
---|
619 | '#type' => 'button', |
---|
620 | '#value' => t('Cancel'), |
---|
621 | '#attributes' => array('class' => 'create-new-item-cancel'), |
---|
622 | ); |
---|
623 | } |
---|
624 | } |
---|
625 | } |
---|
626 | |
---|
627 | |
---|
628 | if ($config['dropbox']['status']) { |
---|
629 | if (!$creating_new_item) { |
---|
630 | // Append an "Add" button to the selects. |
---|
631 | $element['hierarchical_select']['dropbox_add'] = array( |
---|
632 | '#type' => 'button', |
---|
633 | '#value' => t('Add'), |
---|
634 | '#attributes' => array('class' => 'add-to-dropbox'), |
---|
635 | ); |
---|
636 | } |
---|
637 | |
---|
638 | if ($config['dropbox']['limit'] > 0) { // Zero as dropbox limit means no limit. |
---|
639 | if (count($dropbox->lineages) == $config['dropbox']['limit']) { |
---|
640 | $element['dropbox_limit_warning'] = array( |
---|
641 | '#value' => t("You've reached the maximal number of items you can select."), |
---|
642 | '#prefix' => '<p class="hierarchical-select-dropbox-limit-warning">', |
---|
643 | '#suffix' => '</p>', |
---|
644 | ); |
---|
645 | |
---|
646 | // Disable all child form elements of $element['hierarchical_select]. |
---|
647 | _hierarchical_select_mark_as_disabled($element['hierarchical_select']); |
---|
648 | } |
---|
649 | } |
---|
650 | |
---|
651 | // Add the hidden part of the dropbox. This will be used to store the |
---|
652 | // currently selected lineages. |
---|
653 | $element['dropbox']['hidden'] = array( |
---|
654 | '#prefix' => '<div class="dropbox-hidden">', |
---|
655 | '#suffix' => '</div>', |
---|
656 | ); |
---|
657 | $element['dropbox']['hidden'] = _hierarchical_select_process_render_db_hidden($hsid, $dropbox); |
---|
658 | |
---|
659 | // Add the dropbox-as-a-table that will be visible to the user. |
---|
660 | $element['dropbox']['visible'] = _hierarchical_select_process_render_db_visible($hsid, $dropbox); |
---|
661 | } |
---|
662 | |
---|
663 | // This button and accompanying help text will be hidden when Javascript is |
---|
664 | // enabled. |
---|
665 | $element['nojs'] = array( |
---|
666 | '#prefix' => '<div class="nojs">', |
---|
667 | '#suffix' => '</div>', |
---|
668 | ); |
---|
669 | $element['nojs']['update_button'] = array( |
---|
670 | '#type' => 'button', |
---|
671 | '#value' => t('Update'), |
---|
672 | '#attributes' => array('class' => 'update-button'), |
---|
673 | ); |
---|
674 | $element['nojs']['update_button_help_text'] = array( |
---|
675 | '#value' => _hierarchical_select_nojs_helptext($config['dropbox']['status']), |
---|
676 | '#prefix' => '<div class="help-text">', |
---|
677 | '#suffix' => '</div>', |
---|
678 | ); |
---|
679 | |
---|
680 | |
---|
681 | // Ensure the render order is correct. |
---|
682 | $element['hierarchical_select']['#weight'] = 0; |
---|
683 | $element['dropbox_limit_warning']['#weight'] = 1; |
---|
684 | $element['dropbox']['#weight'] = 2; |
---|
685 | $element['nojs']['#weight'] = 3; |
---|
686 | |
---|
687 | // This prevents values from in $element['#post'] to be used instead of the |
---|
688 | // generated default values (#default_value). |
---|
689 | // For example: $element['hierarchical_select']['selects']['0']['#default_value'] |
---|
690 | // is set to 'label_0' after an "Add" operation. When $element['#post'] is |
---|
691 | // NOT unset, the corresponding value in $element['#post'] will be used |
---|
692 | // instead of the default value that was set. This is undesired behavior. |
---|
693 | if (isset($element['#post'])) { |
---|
694 | unset($element['#post']); |
---|
695 | } |
---|
696 | |
---|
697 | // Finally, calculate the return value of this hierarchical_select form |
---|
698 | // element. This will be set in _hierarchical_select_validate(). (If we'd |
---|
699 | // set it now, it would be overridden again.) |
---|
700 | $element['#return_value'] = _hierarchical_select_process_calculate_return_value($hierarchy, ($config['dropbox']['status']) ? $dropbox : FALSE, $config['module'], $config['params'], $config['save_lineage']); |
---|
701 | |
---|
702 | // Add a validate callback, which will: |
---|
703 | // - validate that the dropbox limit was not exceeded. |
---|
704 | // - set the return value of this form element. |
---|
705 | // Also make sure it is the *first* validate callback. |
---|
706 | $element['#element_validate'] = (isset($element['#element_validate'])) ? $element['#element_validate'] : array(); |
---|
707 | $element['#element_validate'] = array_merge(array('_hierarchical_select_validate'), $element['#element_validate']); |
---|
708 | |
---|
709 | if (HS_DEVELOPER_MODE) { |
---|
710 | $element['log'] = array('#type' => 'value', '#value' => _hierarchical_select_log(NULL, TRUE)); |
---|
711 | $settings = array( |
---|
712 | 'HierarchicalSelect' => array( |
---|
713 | 'initialLog' => array( |
---|
714 | $hsid => $element['log']['#value'], |
---|
715 | ), |
---|
716 | ), |
---|
717 | ); |
---|
718 | _hierarchical_select_add_js_settings($settings, $form_state); |
---|
719 | } |
---|
720 | |
---|
721 | // If the form item is marked as disabled, disable all child form items as |
---|
722 | // well. |
---|
723 | if (isset($element['#disabled']) && $element['#disabled']) { |
---|
724 | _hierarchical_select_mark_as_disabled($element); |
---|
725 | } |
---|
726 | |
---|
727 | // Remove #options in case it's been added. |
---|
728 | if (isset($element['#options'])) { |
---|
729 | unset($element['#options']); |
---|
730 | } |
---|
731 | |
---|
732 | return $element; |
---|
733 | } |
---|
734 | |
---|
735 | /** |
---|
736 | * Hierarchical select form element type #after_build callback. |
---|
737 | */ |
---|
738 | function hierarchical_select_after_build($form, &$form_state) { |
---|
739 | // TRICKY: Pageroute compatibility: avoid that the body of this #after_build |
---|
740 | // callback is executed twice. |
---|
741 | if (isset($form['hs_form_build_id'])) { |
---|
742 | return $form; |
---|
743 | } |
---|
744 | |
---|
745 | $names = _hierarchical_select_store_name(NULL, NULL, TRUE); |
---|
746 | |
---|
747 | if (!isset($_POST['hs_form_build_id']) && count($names)) { |
---|
748 | $parameters = (isset($form['#parameters'])) ? $form['#parameters'] : array(); |
---|
749 | $menu_item = menu_get_item(); |
---|
750 | |
---|
751 | // Collect information in this array, which will be used in dynamic form |
---|
752 | // updates, to ⊠|
---|
753 | $storage = array( |
---|
754 | // ⊠retrieve $form. |
---|
755 | 'parameters' => $parameters, |
---|
756 | // ⊠determine which part of $form should be rendered. |
---|
757 | '#names' => $names, |
---|
758 | // ⊠include the file in which the form function lives. |
---|
759 | 'file' => $menu_item['file'], |
---|
760 | ); |
---|
761 | |
---|
762 | // Store the information needed for dynamic form updates in the cache, so |
---|
763 | // we can retrieve this in our JSON callbacks (to be able to rebuild and |
---|
764 | // render part of the form). |
---|
765 | $hs_form_build_id = 'hs_form_'. md5(mt_rand()); |
---|
766 | $lifetime = variable_get('hierarchical_select_cache_lifetime', HS_CACHE_LIFETIME_DEFAULT); |
---|
767 | cache_set($hs_form_build_id, $storage, 'cache_hierarchical_select', time() + $lifetime); |
---|
768 | } |
---|
769 | elseif (isset($_POST['hs_form_build_id'])) { |
---|
770 | // Don't generate a new hs_form_build_id if this is a re-rendering of the |
---|
771 | // same form! |
---|
772 | $hs_form_build_id = $_POST['hs_form_build_id']; |
---|
773 | } |
---|
774 | |
---|
775 | // Store the hs_form_build_id in a hidden value, so that it gets POSTed. |
---|
776 | $form_element = array( |
---|
777 | '#type' => 'hidden', |
---|
778 | '#value' => $hs_form_build_id, |
---|
779 | // We have to set #parents manually because we want to send only |
---|
780 | // $form_element through form_builder(), not $form. If we set #parents, |
---|
781 | // form_builder() has all info it needs to generate #name and #id. |
---|
782 | '#parents' => array('hs_form_build_id'), |
---|
783 | ); |
---|
784 | $form['hs_form_build_id'] = form_builder($form['form_id']['#value'], $form_element, $form_state); |
---|
785 | |
---|
786 | return $form; |
---|
787 | } |
---|
788 | |
---|
789 | /** |
---|
790 | * Hierarchical select form element #element_validate callback. |
---|
791 | */ |
---|
792 | function _hierarchical_select_validate(&$element, &$form_state) { |
---|
793 | // If the dropbox is enabled and a dropbox limit is configured, check if |
---|
794 | // this limit is not exceeded. |
---|
795 | $config = _hierarchical_select_inherit_default_config($element['#config']); |
---|
796 | if ($config['dropbox']['status']) { |
---|
797 | if ($config['dropbox']['limit'] > 0) { // Zero as dropbox limit means no limit. |
---|
798 | // TRICKY: #element_validate is not called upon the initial rendering |
---|
799 | // (i.e. it is assumed that the default value is valid). However, |
---|
800 | // Hierarchical Select's config can influence the validity (i.e. how |
---|
801 | // many selections may be added to the dropbox). This means it's |
---|
802 | // possible the user has actually selected too many items without being |
---|
803 | // notified of this. |
---|
804 | $lineage_count = count($element['#value']['dropbox']['hidden']['lineages_selections']); |
---|
805 | if ($lineage_count > $config['dropbox']['limit']) { |
---|
806 | // TRICKY: this should propagate the error down to the children, but |
---|
807 | // this doesn't seem to happen, since for example the selects of the |
---|
808 | // hierarchical select don't get the error class set. Further |
---|
809 | // investigation needed. |
---|
810 | form_error( |
---|
811 | $element, |
---|
812 | t("You've selected %lineage-count items, but you're only allowed to select %dropbox-limit items.", |
---|
813 | array( |
---|
814 | '%lineage-count' => $lineage_count, |
---|
815 | '%dropbox-limit' => $config['dropbox']['limit'], |
---|
816 | ) |
---|
817 | ) |
---|
818 | ); |
---|
819 | _hierarchical_select_form_set_error_class($element); |
---|
820 | } |
---|
821 | } |
---|
822 | } |
---|
823 | |
---|
824 | // Set the proper return value. I.e. instead of returning all the values |
---|
825 | // that are used for making the hierarchical_select form element type work, |
---|
826 | // we pass a flat array of item ids. e.g. for the taxonomy module, this will |
---|
827 | // be an array of term ids. If a single item is selected, this will not be |
---|
828 | // an array. |
---|
829 | // If the form item is disabled, set the default value as the return value, |
---|
830 | // because otherwise nothing would be returned (disabled form items are not |
---|
831 | // submitted, as described in the HTML standard). |
---|
832 | if (isset($element['#disabled']) && $element['#disabled']) { |
---|
833 | $element['#return_value'] = $element['#default_value']; |
---|
834 | } |
---|
835 | |
---|
836 | $element['#value'] = $element['#return_value']; |
---|
837 | form_set_value($element, $element['#value'], $form_state); |
---|
838 | |
---|
839 | // We have to check again for errors. This line is taken litterally from |
---|
840 | // form.inc, so it works in an identical way. |
---|
841 | if ($element['#required'] && (!count($element['#value']) || (is_string($element['#value']) && strlen(trim($element['#value'])) == 0))) { |
---|
842 | form_error($element, t('!name field is required.', array('!name' => $element['#title']))); |
---|
843 | _hierarchical_select_form_set_error_class($element); |
---|
844 | } |
---|
845 | } |
---|
846 | |
---|
847 | |
---|
848 | //---------------------------------------------------------------------------- |
---|
849 | // Forms API #process callback: |
---|
850 | // Calculation of hierarchical select and dropbox selection. |
---|
851 | |
---|
852 | /** |
---|
853 | * Get the current (flat) selection of the hierarchical select. |
---|
854 | * |
---|
855 | * This selection is updatable by the user, because the values are retrieved |
---|
856 | * from the selects in $element['hierarchical_select']['selects']. |
---|
857 | * |
---|
858 | * @param $element |
---|
859 | * A hierarchical_select form element. |
---|
860 | * @return |
---|
861 | * An array (bag) containing the ids of the selected items in the |
---|
862 | * hierarchical select. |
---|
863 | */ |
---|
864 | function _hierarchical_select_process_get_hs_selection($element) { |
---|
865 | $hs_selection = array(); |
---|
866 | $config = _hierarchical_select_inherit_default_config($element['#config']); |
---|
867 | |
---|
868 | if (!empty($element['#value']['hierarchical_select']['selects'])) { |
---|
869 | if ($config['save_lineage']) { |
---|
870 | foreach ($element['#value']['hierarchical_select']['selects'] as $key => $value) { |
---|
871 | $hs_selection[] = $value; |
---|
872 | } |
---|
873 | } |
---|
874 | else { |
---|
875 | foreach ($element['#value']['hierarchical_select']['selects'] as $key => $value) { |
---|
876 | $hs_selection[] = $value; |
---|
877 | } |
---|
878 | $hs_selection = _hierarchical_select_hierarchy_validate($hs_selection, $config['module'], $config['params']); |
---|
879 | |
---|
880 | // Get the last valid value. (Only the deepest item gets saved). Make |
---|
881 | // sure $hs_selection is an array at all times. |
---|
882 | $hs_selection = ($hs_selection != -1) ? array(end($hs_selection)) : array(); |
---|
883 | } |
---|
884 | } |
---|
885 | |
---|
886 | return $hs_selection; |
---|
887 | } |
---|
888 | |
---|
889 | /** |
---|
890 | * Get the current (flat) selection of the dropbox. |
---|
891 | * |
---|
892 | * This selection is not updatable by the user, because the values are |
---|
893 | * retrieved from the hidden values in |
---|
894 | * $element['dropbox']['hidden']['lineages_selections']. This selection can |
---|
895 | * only be updated by the server, i.e. when the user clicks the "Add" button. |
---|
896 | * But this selection can still be reduced in size if the user has marked |
---|
897 | * dropbox entries (lineages) for removal. |
---|
898 | * |
---|
899 | * @param $element |
---|
900 | * A hierarchical_select form element. |
---|
901 | * @return |
---|
902 | * An array (bag) containing the ids of the selected items in the |
---|
903 | * dropbox. |
---|
904 | */ |
---|
905 | function _hierarchical_select_process_get_db_selection($element) { |
---|
906 | $db_selection = array(); |
---|
907 | |
---|
908 | if (!empty($element['#value']['dropbox']['hidden']['lineages_selections'])) { |
---|
909 | // This is only present in #value if at least one "Remove" checkbox was |
---|
910 | // checked, so ensure that we're doing something valid. |
---|
911 | $remove_from_db_selection = (!isset($element['#value']['dropbox']['visible']['lineages'])) ? array() : array_keys($element['#value']['dropbox']['visible']['lineages']); |
---|
912 | |
---|
913 | // Add all selections to the dropbox selection, except for the ones that |
---|
914 | // are scheduled for removal. |
---|
915 | foreach ($element['#value']['dropbox']['hidden']['lineages_selections'] as $x => $selection) { |
---|
916 | if (!in_array($x, $remove_from_db_selection)) { |
---|
917 | $db_selection = array_merge($db_selection, unserialize($selection)); |
---|
918 | } |
---|
919 | } |
---|
920 | |
---|
921 | // Ensure that the last item of each selection that was scheduled for |
---|
922 | // removal is completely absent from the dropbox selection. |
---|
923 | // In case of a tree with multiple parents, the same item can exist in |
---|
924 | // different entries, and thus it would stay in the selection. When the |
---|
925 | // server then reconstructs all lineages, the lineage we're removing, will |
---|
926 | // also be reconstructed: it will seem as if the removing didn't work! |
---|
927 | // This will not break removing dropbox entries for hierarchies without |
---|
928 | // multiple parents, since items at the deepest level are always unique to |
---|
929 | // that specific lineage. |
---|
930 | // Easier explanation at http://drupal.org/node/221210#comment-733715. |
---|
931 | foreach ($remove_from_db_selection as $key => $x) { |
---|
932 | $item = end(unserialize($element['#value']['dropbox']['hidden']['lineages_selections'][$x])); |
---|
933 | $position = array_search($item, $db_selection); |
---|
934 | if ($position) { |
---|
935 | unset($db_selection[$position]); |
---|
936 | } |
---|
937 | } |
---|
938 | $db_selection = array_unique($db_selection); |
---|
939 | } |
---|
940 | |
---|
941 | return $db_selection; |
---|
942 | } |
---|
943 | |
---|
944 | /** |
---|
945 | * Calculates the flat selections of both the hierarchical select and the |
---|
946 | * dropbox. |
---|
947 | * |
---|
948 | * @param $element |
---|
949 | * A hierarchical_select form element. |
---|
950 | * @return |
---|
951 | * An array of the following structure: |
---|
952 | * array( |
---|
953 | * $hierarchical_select_selection = array(), // Flat list of selected ids. |
---|
954 | * $dropbox_selection = array(), |
---|
955 | * ) |
---|
956 | * with both of the subarrays flat lists of selected ids. The |
---|
957 | * _hierarchical_select_hierarchy_generate() and |
---|
958 | * _hierarchical_select_dropbox_generate() functions should be applied on |
---|
959 | * these respective subarrays. |
---|
960 | * |
---|
961 | * @see _hierarchical_select_hierarchy_generate() |
---|
962 | * @see _hierarchical_select_dropbox_generate() |
---|
963 | */ |
---|
964 | function _hierarchical_select_process_calculate_selections(&$element) { |
---|
965 | $hs_selection = array(); // hierarchical select selection |
---|
966 | $db_selection = array(); // dropbox selection |
---|
967 | |
---|
968 | $config = _hierarchical_select_inherit_default_config($element['#config']); |
---|
969 | $dropbox = (bool) $config['dropbox']['status']; |
---|
970 | |
---|
971 | // When: |
---|
972 | // - no data was POSTed |
---|
973 | // - or #value is set directly and not by a Hierarchical Select POST (and |
---|
974 | // therefor set either manually or by another module), |
---|
975 | // then use the value of #default_value, or when available, of #value. |
---|
976 | if (!isset($element['#post']) || (!isset($element['#value']['hierarchical_select']) && !isset($element['#value']['dropbox']))) { |
---|
977 | $value = (isset($element['#value'])) ? $element['#value'] : $element['#default_value']; |
---|
978 | $value = (is_array($value)) ? $value : array($value); |
---|
979 | if ($dropbox) { |
---|
980 | $db_selection = $value; |
---|
981 | } |
---|
982 | else { |
---|
983 | $hs_selection = $value; |
---|
984 | } |
---|
985 | } |
---|
986 | else { |
---|
987 | $op = (isset($element['#post']['op'])) ? $element['#post']['op'] : NULL; |
---|
988 | if ($dropbox && $op == t('Add')) { |
---|
989 | $hs_selection = _hierarchical_select_process_get_hs_selection($element); |
---|
990 | $db_selection = _hierarchical_select_process_get_db_selection($element); |
---|
991 | |
---|
992 | // Add $hs_selection to $db_selection (automatically filters to keep |
---|
993 | // only the unique ones). |
---|
994 | $db_selection = array_merge($db_selection, $hs_selection); |
---|
995 | |
---|
996 | // Only reset $hs_selection if the user has configured it that way. |
---|
997 | if ($config['dropbox']['reset_hs']) { |
---|
998 | $hs_selection = array(); |
---|
999 | } |
---|
1000 | } |
---|
1001 | else if ($op == t('Create')) { |
---|
1002 | // This code handles both the creation of a new item in an existing |
---|
1003 | // level and the creation of an item that also creates a new level. |
---|
1004 | |
---|
1005 | // TODO: http://drupal.org/node/253868 |
---|
1006 | // TODO: http://drupal.org/node/253869 |
---|
1007 | |
---|
1008 | $label = trim($element['#value']['hierarchical_select']['create_new_item']['input']); |
---|
1009 | $selects = isset($element['#value']['hierarchical_select']['selects']) ? $element['#value']['hierarchical_select']['selects'] : array(); |
---|
1010 | $depth = count($selects); |
---|
1011 | $parent = ($depth > 0) ? end($selects) : 0; |
---|
1012 | |
---|
1013 | // Disallow items with empty labels; allow the user again to create a |
---|
1014 | // (proper) new item. |
---|
1015 | if (empty($label)) { |
---|
1016 | $element['#value']['hierarchical_select']['selects'][count($selects)] = 'create_new_item'; |
---|
1017 | } |
---|
1018 | // Ensure that this new item will not violate the max_levels and |
---|
1019 | // allowed_levels settings. |
---|
1020 | else if ( |
---|
1021 | (count(module_invoke($config['module'], 'hierarchical_select_children', $parent, $config['params'])) |
---|
1022 | || $config['editability']['max_levels'] == 0 |
---|
1023 | || $depth < $config['editability']['max_levels'] |
---|
1024 | ) |
---|
1025 | && |
---|
1026 | (_hierarchical_select_create_new_item_is_allowed($config, $depth)) |
---|
1027 | ) { |
---|
1028 | // Create the new item in the hierarchy and retrieve its value. |
---|
1029 | $value = module_invoke($config['module'], 'hierarchical_select_create_item', check_plain($label), $parent, $config['params']); |
---|
1030 | |
---|
1031 | // Ensure the newly created item will be selected after rendering. |
---|
1032 | if ($value) { |
---|
1033 | // Pretend there was a select where the "create new item" section |
---|
1034 | // was, and assign it the value of the item that was just created. |
---|
1035 | $element['#value']['hierarchical_select']['selects'][count($selects)] = $value; |
---|
1036 | } |
---|
1037 | } |
---|
1038 | |
---|
1039 | $hs_selection = _hierarchical_select_process_get_hs_selection($element); |
---|
1040 | if ($dropbox) { |
---|
1041 | $db_selection = _hierarchical_select_process_get_db_selection($element); |
---|
1042 | } |
---|
1043 | } |
---|
1044 | else { |
---|
1045 | // This handles the cases of: |
---|
1046 | // - $op == t('Update') |
---|
1047 | // - $op == t('Cancel') (used when creating a new item or a new level) |
---|
1048 | // - any other submit button, e.g. the "Preview" button |
---|
1049 | $hs_selection = _hierarchical_select_process_get_hs_selection($element); |
---|
1050 | if ($dropbox) { |
---|
1051 | $db_selection = _hierarchical_select_process_get_db_selection($element); |
---|
1052 | } |
---|
1053 | } |
---|
1054 | } |
---|
1055 | |
---|
1056 | // Prevent doubles in either array. |
---|
1057 | $hs_selection = array_unique($hs_selection); |
---|
1058 | $db_selection = array_unique($db_selection); |
---|
1059 | |
---|
1060 | return array($hs_selection, $db_selection); |
---|
1061 | } |
---|
1062 | |
---|
1063 | |
---|
1064 | //---------------------------------------------------------------------------- |
---|
1065 | // Forms API #process callback: |
---|
1066 | // Rendering (generation of FAPI code) of hierarchical select and dropbox. |
---|
1067 | |
---|
1068 | /** |
---|
1069 | * Render the selects in the hierarchical select. |
---|
1070 | * |
---|
1071 | * @param $hsid |
---|
1072 | * A hierarchical select id. |
---|
1073 | * @param $hierarchy |
---|
1074 | * A hierarchy object. |
---|
1075 | * @return |
---|
1076 | * A structured array for use in the Forms API. |
---|
1077 | */ |
---|
1078 | function _hierarchical_select_process_render_hs_selects($hsid, $hierarchy) { |
---|
1079 | $form['#tree'] = TRUE; |
---|
1080 | $form['#prefix'] = '<div class="selects">'; |
---|
1081 | $form['#suffix'] = '</div>'; |
---|
1082 | |
---|
1083 | foreach ($hierarchy->lineage as $depth => $selected_item) { |
---|
1084 | $form[$depth] = array( |
---|
1085 | '#type' => 'select', |
---|
1086 | '#options' => $hierarchy->levels[$depth], |
---|
1087 | '#default_value' => $selected_item, |
---|
1088 | // Use a #theme callback to prevent the select from being wrapped in a |
---|
1089 | // div. This simplifies the CSS and JS code. Also sets a special class |
---|
1090 | // on the level label option, if any, to make level label styles |
---|
1091 | // possible. |
---|
1092 | '#theme' => 'hierarchical_select_select', |
---|
1093 | // Add child information. When a child has no children, its |
---|
1094 | // corresponding "option" element will be marked as such. |
---|
1095 | '#childinfo' => (isset($hierarchy->childinfo[$depth])) ? $hierarchy->childinfo[$depth] : NULL, |
---|
1096 | ); |
---|
1097 | } |
---|
1098 | return $form; |
---|
1099 | } |
---|
1100 | |
---|
1101 | /** |
---|
1102 | * Render the hidden part of the dropbox. This part stores the lineages of all |
---|
1103 | * selections in the dropbox. |
---|
1104 | * |
---|
1105 | * @param $hsid |
---|
1106 | * A hierarchical select id. |
---|
1107 | * @param $dropbox |
---|
1108 | * A dropbox object. |
---|
1109 | * @return |
---|
1110 | * A structured array for use in the Forms API. |
---|
1111 | */ |
---|
1112 | function _hierarchical_select_process_render_db_hidden($hsid, $dropbox) { |
---|
1113 | $element['#tree'] = TRUE; |
---|
1114 | |
---|
1115 | foreach ($dropbox->lineages_selections as $x => $lineage_selection) { |
---|
1116 | $element['lineages_selections'][$x] = array('#type' => 'hidden', '#value' => serialize($lineage_selection)); |
---|
1117 | } |
---|
1118 | return $element; |
---|
1119 | } |
---|
1120 | |
---|
1121 | /** |
---|
1122 | * Render the visible part of the dropbox. |
---|
1123 | * |
---|
1124 | * @param $hsid |
---|
1125 | * A hierarchical select id. |
---|
1126 | * @param $dropbox |
---|
1127 | * A dropbox object. |
---|
1128 | * @return |
---|
1129 | * A structured array for use in the Forms API. |
---|
1130 | */ |
---|
1131 | function _hierarchical_select_process_render_db_visible($hsid, $dropbox) { |
---|
1132 | $element['#tree'] = TRUE; |
---|
1133 | $element['#theme'] = 'hierarchical_select_dropbox_table'; |
---|
1134 | |
---|
1135 | |
---|
1136 | // This information is necessary for the #theme callback. |
---|
1137 | $element['title'] = array('#type' => 'value', '#value' => t($dropbox->title)); |
---|
1138 | $element['separator'] = array('#type' => 'value', '#value' => 'âº'); |
---|
1139 | $element['is_empty'] = array('#type' => 'value', '#value' => empty($dropbox->lineages)); |
---|
1140 | |
---|
1141 | |
---|
1142 | if (!empty($dropbox->lineages)) { |
---|
1143 | foreach ($dropbox->lineages as $x => $lineage) { |
---|
1144 | |
---|
1145 | // Store position information for the lineage. This will be used in the |
---|
1146 | // #theme callback. |
---|
1147 | $element['lineages'][$x] = array( |
---|
1148 | '#zebra' => (($x + 1) % 2 == 0) ? 'even' : 'odd', |
---|
1149 | '#first' => ($x == 0) ? 'first' : '', |
---|
1150 | '#last' => ($x == count($dropbox->lineages) - 1) ? 'last' : '', |
---|
1151 | ); |
---|
1152 | |
---|
1153 | // Create a 'markup' element for each item in the lineage. |
---|
1154 | foreach ($lineage as $depth => $item) { |
---|
1155 | // The item is selected when save_lineage is enabled (i.e. each item |
---|
1156 | // will be selected), or when the item is the last item in the current |
---|
1157 | // lineage. |
---|
1158 | $is_selected = $dropbox->save_lineage || ($depth == count($lineage) - 1); |
---|
1159 | |
---|
1160 | $element['lineages'][$x][$depth] = array( |
---|
1161 | '#value' => $item['label'], |
---|
1162 | '#prefix' => '<span class="dropbox-item'. (($is_selected) ? ' dropbox-selected-item' : '') .'">', |
---|
1163 | '#suffix' => '</span>', |
---|
1164 | ); |
---|
1165 | } |
---|
1166 | |
---|
1167 | // Finally, create a "Remove" checkbox for the lineage. |
---|
1168 | $element['lineages'][$x]['remove'] = array( |
---|
1169 | '#type' => 'checkbox', |
---|
1170 | '#title' => t('Remove'), |
---|
1171 | ); |
---|
1172 | } |
---|
1173 | } |
---|
1174 | |
---|
1175 | return $element; |
---|
1176 | } |
---|
1177 | |
---|
1178 | /** |
---|
1179 | * Render a flat select version of a hierarchical_select form element. This is |
---|
1180 | * necessary for backwards compatibility (together with some Javascript code) |
---|
1181 | * in case of GET forms. |
---|
1182 | * |
---|
1183 | * @param $hierarchy |
---|
1184 | * A hierarchy object. |
---|
1185 | * @param $dropbox |
---|
1186 | * A dropbox object. |
---|
1187 | * @param $config |
---|
1188 | * A config array with at least the following settings: |
---|
1189 | * - module |
---|
1190 | * - params |
---|
1191 | * - dropbox |
---|
1192 | * - status |
---|
1193 | * @return |
---|
1194 | * A structured array for use in the Forms API. |
---|
1195 | */ |
---|
1196 | function _hierarchical_select_process_render_flat_select($hierarchy, $dropbox, $config) { |
---|
1197 | $selection = array(); |
---|
1198 | if ($config['dropbox']['status']) { |
---|
1199 | foreach ($dropbox->lineages_selections as $lineage_selection) { |
---|
1200 | $selection = array_merge($selection, $lineage_selection); |
---|
1201 | } |
---|
1202 | } |
---|
1203 | else { |
---|
1204 | $selection = $hierarchy->lineage; |
---|
1205 | } |
---|
1206 | |
---|
1207 | $options = array(); |
---|
1208 | foreach ($selection as $value) { |
---|
1209 | $is_valid = module_invoke($config['module'], 'hierarchical_select_valid_item', $value, $config['params']); |
---|
1210 | if ($is_valid) { |
---|
1211 | $options[$value] = $value; |
---|
1212 | } |
---|
1213 | } |
---|
1214 | |
---|
1215 | $element = array( |
---|
1216 | '#type' => 'select', |
---|
1217 | '#multiple' => ($config['save_lineage'] || $config['dropbox']['status']), |
---|
1218 | '#options' => $options, |
---|
1219 | '#value' => array_keys($options), |
---|
1220 | // Use a #theme callback to prevent the select from being wrapped in a |
---|
1221 | // div. This simplifies the CSS and JS code. |
---|
1222 | '#theme' => 'hierarchical_select_select', |
---|
1223 | '#attributes' => array('class' => 'flat-select'), |
---|
1224 | ); |
---|
1225 | |
---|
1226 | return $element; |
---|
1227 | } |
---|
1228 | |
---|
1229 | /** |
---|
1230 | * Calculate the return value of a hierarchical_select form element, based on |
---|
1231 | * the $hierarchy and $dropbox objects. We have to set a return value, because |
---|
1232 | * the values set and used by this form element ($element['#value]) are not |
---|
1233 | * easily usable in the Forms API; we want to return a flat list of item ids. |
---|
1234 | * |
---|
1235 | * @param $hierarchy |
---|
1236 | * A hierarchy object. |
---|
1237 | * @param $dropbox |
---|
1238 | * Optional. A dropbox object. |
---|
1239 | * @param $module |
---|
1240 | * The module that should be used for HS hooks. |
---|
1241 | * @param $params |
---|
1242 | * Optional. An array of parameters, which may be necessary for some |
---|
1243 | * implementations. |
---|
1244 | * @param $save_lineage |
---|
1245 | * Whether the save_lineage setting is enabled or not. |
---|
1246 | * @return |
---|
1247 | * A single item id or a flat array of item ids. |
---|
1248 | */ |
---|
1249 | function _hierarchical_select_process_calculate_return_value($hierarchy, $dropbox = FALSE, $module, $params, $save_lineage) { |
---|
1250 | if (!$dropbox) { |
---|
1251 | $return_value = _hierarchical_select_hierarchy_validate($hierarchy->lineage, $module, $params); |
---|
1252 | // If the save_lineage setting is disabled, keep only the deepest item. |
---|
1253 | if (!$save_lineage) { |
---|
1254 | $return_value = (is_array($return_value)) ? end($return_value) : NULL; |
---|
1255 | } |
---|
1256 | |
---|
1257 | // Prevent a return value of -1. -1 is used for HS' internal system and |
---|
1258 | // means "nothing selected", but to Drupal it *will* seam like a valid |
---|
1259 | // value. Therefore, we set it to NULL. |
---|
1260 | $return_value = ($return_value != -1) ? $return_value : NULL; |
---|
1261 | } |
---|
1262 | else { |
---|
1263 | $return_value = array(); |
---|
1264 | foreach ($dropbox->lineages_selections as $x => $selection) { |
---|
1265 | if (!$save_lineage) { |
---|
1266 | // An entry in the dropbox when the save_lineage setting is disabled |
---|
1267 | // is only the deepest item of the generated lineage. |
---|
1268 | $return_value[] = end($selection); |
---|
1269 | } |
---|
1270 | else { |
---|
1271 | // An entry in the dropbox when the save_lineage setting is enabled is |
---|
1272 | // the entire generated lineage, if it's valid (i.e. if the user has |
---|
1273 | // not tampered with it). |
---|
1274 | $lineage = _hierarchical_select_hierarchy_validate($selection, $module, $params); |
---|
1275 | $return_value = array_merge($return_value, $lineage); |
---|
1276 | } |
---|
1277 | } |
---|
1278 | $return_value = array_unique($return_value); |
---|
1279 | } |
---|
1280 | |
---|
1281 | return $return_value; |
---|
1282 | } |
---|
1283 | |
---|
1284 | |
---|
1285 | //---------------------------------------------------------------------------- |
---|
1286 | // Private functions. |
---|
1287 | |
---|
1288 | /** |
---|
1289 | * Inherit the default config from Hierarchical Selects' hook_elements(). |
---|
1290 | * |
---|
1291 | * @param $config |
---|
1292 | * A config array with at least the following settings: |
---|
1293 | * - module |
---|
1294 | * - params |
---|
1295 | * @return |
---|
1296 | * An updated config array. |
---|
1297 | */ |
---|
1298 | function _hierarchical_select_inherit_default_config($config, $defaults_override = array()) { |
---|
1299 | // Set defaults for unconfigured settings. Get the defaults from our |
---|
1300 | // hook_elements() implementation. Default properties from this hook are |
---|
1301 | // applied automatically, but properties inside properties, such as is the |
---|
1302 | // case for Hierarchical Select's #config property, aren't applied. |
---|
1303 | $type = hierarchical_select_elements(); |
---|
1304 | $defaults = $type['hierarchical_select']['#config']; |
---|
1305 | // Don't inherit the module and params settings. |
---|
1306 | unset($defaults['module']); |
---|
1307 | unset($defaults['params']); |
---|
1308 | |
---|
1309 | // Allow the defaults to be overridden. |
---|
1310 | $defaults = array_smart_merge($defaults, $defaults_override); |
---|
1311 | |
---|
1312 | // Apply the defaults to the config. |
---|
1313 | $config = array_smart_merge($defaults, $config); |
---|
1314 | |
---|
1315 | return $config; |
---|
1316 | } |
---|
1317 | |
---|
1318 | /** |
---|
1319 | * Helper function to add the required Javascript files and settings. |
---|
1320 | * |
---|
1321 | * @param $form_state |
---|
1322 | * A form state array. Necessary to set the callback URL for Hierarchical |
---|
1323 | * Select through _hierarchical_select_add_js_settings(). |
---|
1324 | */ |
---|
1325 | function _hierarchical_select_setup_js(&$form_state = NULL) { |
---|
1326 | global $language; |
---|
1327 | |
---|
1328 | static $ran_once; |
---|
1329 | static $js_settings_added; |
---|
1330 | |
---|
1331 | $jquery_ui_components = array( |
---|
1332 | 'effects.core', |
---|
1333 | 'effects.drop', |
---|
1334 | ); |
---|
1335 | |
---|
1336 | if (!$js_settings_added && isset($form_state)) { |
---|
1337 | $url = base_path(); |
---|
1338 | $url .= variable_get('clean_url', 0) ? '' : 'index.php?q='; |
---|
1339 | // Prefix URL with language path when i18n is enabled and when path-based |
---|
1340 | // negotiation is being used. |
---|
1341 | $negotiation = variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE); |
---|
1342 | if (module_exists('i18n') && ($language->prefix != '') && ($negotiation == LANGUAGE_NEGOTIATION_PATH_DEFAULT || $negotiation == LANGUAGE_NEGOTIATION_PATH)) { |
---|
1343 | $url .= $language->prefix . '/'; |
---|
1344 | } |
---|
1345 | if (module_exists('purl')) { |
---|
1346 | $options = array(); |
---|
1347 | purl_url_outbound_alter($url, $options, ''); |
---|
1348 | $url = str_replace('//', '/', '/' . $url); |
---|
1349 | } |
---|
1350 | |
---|
1351 | _hierarchical_select_add_js_settings(array('HierarchicalSelect' => array('basePath' => $url, 'getArguments' => drupal_query_string_encode($_GET, array('q')))), $form_state); |
---|
1352 | |
---|
1353 | $js_settings_added = TRUE; |
---|
1354 | } |
---|
1355 | |
---|
1356 | if (!$ran_once) { |
---|
1357 | $ran_once = TRUE; |
---|
1358 | |
---|
1359 | // Add the CSS and JS, set the URL that should be used by all hierarchical |
---|
1360 | // selects. |
---|
1361 | drupal_add_css(drupal_get_path('module', 'hierarchical_select') .'/hierarchical_select.css'); |
---|
1362 | drupal_add_js(drupal_get_path('module', 'hierarchical_select') .'/hierarchical_select.js'); |
---|
1363 | if (variable_get('hierarchical_select_js_cache_system', 0) == 1) { |
---|
1364 | drupal_add_js(drupal_get_path('module', 'hierarchical_select') .'/hierarchical_select_cache.js'); |
---|
1365 | } |
---|
1366 | if (!module_exists('jquery_form')) { |
---|
1367 | drupal_add_js(drupal_get_path('module', 'hierarchical_select') .'/hierarchical_select_formtoarray.js'); |
---|
1368 | } |
---|
1369 | else { |
---|
1370 | jquery_form_add(); |
---|
1371 | } |
---|
1372 | if (!module_exists('jquery_ui')) { |
---|
1373 | foreach ($jquery_ui_components as $component) { |
---|
1374 | drupal_add_js(drupal_get_path('module', 'hierarchical_select') ."/js/jquery.ui/$component.js"); |
---|
1375 | } |
---|
1376 | } |
---|
1377 | else { |
---|
1378 | jquery_ui_add($jquery_ui_components); |
---|
1379 | } |
---|
1380 | } |
---|
1381 | } |
---|
1382 | |
---|
1383 | /** |
---|
1384 | * Convert a hierarchy object into an array of arrays that can be used for |
---|
1385 | * caching an entire hierarchy in a client-side database. |
---|
1386 | * |
---|
1387 | * @param $hierarchy |
---|
1388 | * A hierarchy object. |
---|
1389 | * @return |
---|
1390 | * An array of arrays. |
---|
1391 | */ |
---|
1392 | function _hierarchical_select_json_convert_hierarchy_to_cache($hierarchy) { |
---|
1393 | // Convert the hierarchy object to an array of values like these: |
---|
1394 | // array('value' => $term_id, 'label => $term_name, 'parent' => $term_id) |
---|
1395 | $cache = array(); |
---|
1396 | foreach ($hierarchy->levels as $depth => $items) { |
---|
1397 | $weight = 0; |
---|
1398 | foreach ($items as $value => $label) { |
---|
1399 | $weight++; |
---|
1400 | $cache[] = array( |
---|
1401 | 'value' => $value, |
---|
1402 | 'label' => $label, |
---|
1403 | 'parent' => ($depth == 0) ? 0 : $hierarchy->lineage[$depth - 1], |
---|
1404 | 'weight' => $weight, |
---|
1405 | ); |
---|
1406 | } |
---|
1407 | } |
---|
1408 | |
---|
1409 | // The last item in the lineage never has any children. |
---|
1410 | $value = end($hierarchy->lineage); |
---|
1411 | $cache[] = array( |
---|
1412 | 'value' => $value .'-has-no-children', // Construct a pseudo-value (will never be actually used). |
---|
1413 | 'label' => '', |
---|
1414 | 'parent' => $value, |
---|
1415 | 'weight' => 0, |
---|
1416 | ); |
---|
1417 | |
---|
1418 | return $cache; |
---|
1419 | } |
---|
1420 | |
---|
1421 | /** |
---|
1422 | * Helper function that marks every element in the given element as disabled. |
---|
1423 | * |
---|
1424 | * @param &$element |
---|
1425 | * The element of which we want to mark all elements as disabled. |
---|
1426 | * @return |
---|
1427 | * A structured array for use in the Forms API. |
---|
1428 | */ |
---|
1429 | function _hierarchical_select_mark_as_disabled(&$element) { |
---|
1430 | $element['#disabled'] = TRUE; |
---|
1431 | |
---|
1432 | // Recurse through all children: |
---|
1433 | foreach (element_children($element) as $key) { |
---|
1434 | if (isset($element[$key]) && $element[$key]) { |
---|
1435 | _hierarchical_select_mark_as_disabled($element[$key]); |
---|
1436 | } |
---|
1437 | } |
---|
1438 | } |
---|
1439 | |
---|
1440 | /** |
---|
1441 | * Helper function to determine whether a given depth (i.e. the depth of a |
---|
1442 | * level) is allowed by the allowed_levels setting. |
---|
1443 | * |
---|
1444 | * @param $config |
---|
1445 | * A config array with at least the following settings: |
---|
1446 | * - editability |
---|
1447 | * - allowed_levels |
---|
1448 | * @param $depth |
---|
1449 | * A depth, starting from 0. |
---|
1450 | * @return |
---|
1451 | * 0 or 1 if it allowed_levels is set for the given depth, 1 otherwise. |
---|
1452 | */ |
---|
1453 | function _hierarchical_select_create_new_item_is_allowed($config, $depth) { |
---|
1454 | return (isset($config['editability']['allowed_levels'][$depth])) ? $config['editability']['allowed_levels'][$depth] : 1; |
---|
1455 | } |
---|
1456 | |
---|
1457 | /** |
---|
1458 | * Helper function that generates the help text is that is displayed to the |
---|
1459 | * user when Javascript is disabled. |
---|
1460 | * |
---|
1461 | * @param $dropbox_is_enabled |
---|
1462 | * Indicates if the dropbox is enabled or not, the help text will be |
---|
1463 | * adjusted depending on this value. |
---|
1464 | * @return |
---|
1465 | * The generated help text (in HTML). |
---|
1466 | */ |
---|
1467 | function _hierarchical_select_nojs_helptext($dropbox_is_enabled) { |
---|
1468 | $output = ''; |
---|
1469 | |
---|
1470 | // The options that will be used in the unordered list. |
---|
1471 | $items = array( |
---|
1472 | t('<span class="highlight">enable Javascript</span> in your browser and then refresh this page, for a much enhanced experience.'), |
---|
1473 | t('<span class="highlight">click the <em>Update</em> button</span> every time you want to update the selection'), |
---|
1474 | ); |
---|
1475 | $items[1] .= (!$dropbox_is_enabled) ? '.' : t(", or when you've checked some checkboxes for entries in the dropbox you'd like to remove."); |
---|
1476 | |
---|
1477 | $output .= '<span class="warning">'; |
---|
1478 | $output .= t("You don't have Javascript enabled."); |
---|
1479 | $output .= '</span> '; |
---|
1480 | $output .= '<span class="ask-to-hover">'; |
---|
1481 | $output .= t('Hover for more information!'); |
---|
1482 | $output .= '</span> '; |
---|
1483 | $output .= t("But don't worry: you can still use this web site! You have two options:"); |
---|
1484 | $output .= theme('item_list', $items, NULL, 'ul', array('class' => 'solutions')); |
---|
1485 | |
---|
1486 | return $output; |
---|
1487 | } |
---|
1488 | |
---|
1489 | /** |
---|
1490 | * Get the form item that has the the given #name property. |
---|
1491 | * |
---|
1492 | * @param $form |
---|
1493 | * A structured array for use in the Forms API. |
---|
1494 | * @param $name |
---|
1495 | * A #name value. |
---|
1496 | * @return |
---|
1497 | * A form item. |
---|
1498 | */ |
---|
1499 | function _hierarchical_select_get_form_item($form, $name) { |
---|
1500 | if (isset($form['#name']) && $form['#name'] == $name) { |
---|
1501 | return $form; |
---|
1502 | } |
---|
1503 | |
---|
1504 | // The current form item apparently is not the one we're looking for, so try |
---|
1505 | // to find it in the child form items. |
---|
1506 | foreach (element_children($form) as $child) { |
---|
1507 | $form_item = _hierarchical_select_get_form_item($form[$child], $name); |
---|
1508 | if ($form_item !== FALSE) { |
---|
1509 | return $form_item; |
---|
1510 | } |
---|
1511 | } |
---|
1512 | |
---|
1513 | // No match in the children either, so return FALSE. |
---|
1514 | return FALSE; |
---|
1515 | } |
---|
1516 | |
---|
1517 | /** |
---|
1518 | * Store the #name property of the given form item, so we can retrieve a list |
---|
1519 | * of #name properties of hierarchical_select form items present in this form |
---|
1520 | * later. |
---|
1521 | * |
---|
1522 | * @param $form_item |
---|
1523 | * Optional. A hierarchical_select form item. |
---|
1524 | * @param $hsid |
---|
1525 | * Optional. A hierarchical select ID. |
---|
1526 | * @param $reset |
---|
1527 | * Optional. Flag that marks if the stored #name properties should be reset. |
---|
1528 | * @return |
---|
1529 | * The stored #name properties per hierarchical_select form item. |
---|
1530 | */ |
---|
1531 | function _hierarchical_select_store_name($form_item = NULL, $hsid = NULL, $reset = FALSE) { |
---|
1532 | static $names; |
---|
1533 | |
---|
1534 | if ($reset) { |
---|
1535 | $ret = $names; |
---|
1536 | $names = array(); |
---|
1537 | return $ret; |
---|
1538 | } |
---|
1539 | |
---|
1540 | if (isset($form_item) && isset($hsid)) { |
---|
1541 | $names[$hsid] = $form_item['#name']; |
---|
1542 | } |
---|
1543 | |
---|
1544 | return $names; |
---|
1545 | } |
---|
1546 | |
---|
1547 | /** |
---|
1548 | * Detect whether a form has at least one hierarchical_select form element. |
---|
1549 | * |
---|
1550 | * @param $form |
---|
1551 | * A structured array for use in the Forms API. |
---|
1552 | * @return |
---|
1553 | * TRUE if the form contains a hierarchical_select form element, FALSE |
---|
1554 | * otherwise. |
---|
1555 | */ |
---|
1556 | function _hierarchical_select_form_has_hierarchical_select($form) { |
---|
1557 | if (isset($form['#type']) && $form['#type'] == 'hierarchical_select') { |
---|
1558 | return TRUE; |
---|
1559 | } |
---|
1560 | else { |
---|
1561 | $has_hierarchical_select = FALSE; |
---|
1562 | foreach (element_children($form) as $name) { |
---|
1563 | if (is_array($form[$name])) { |
---|
1564 | $has_hierarchical_select = _hierarchical_select_form_has_hierarchical_select($form[$name]); |
---|
1565 | if ($has_hierarchical_select) { |
---|
1566 | break; |
---|
1567 | } |
---|
1568 | } |
---|
1569 | } |
---|
1570 | return $has_hierarchical_select; |
---|
1571 | } |
---|
1572 | } |
---|
1573 | |
---|
1574 | /** |
---|
1575 | * Set the 'error' class on the appropriate part of Hierarchical Select, |
---|
1576 | * depending on its configuration. |
---|
1577 | * |
---|
1578 | * @param $element |
---|
1579 | * A Hierarchical Select form item. |
---|
1580 | */ |
---|
1581 | function _hierarchical_select_form_set_error_class(&$element) { |
---|
1582 | $config = _hierarchical_select_inherit_default_config($element['#config']); |
---|
1583 | |
---|
1584 | if ($config['dropbox']['status']) { |
---|
1585 | form_error($element['dropbox']['visible']); |
---|
1586 | } |
---|
1587 | else { |
---|
1588 | for ($i = 0; $i < count(element_children($element['hierarchical_select']['selects'])); $i++) { |
---|
1589 | form_error($element['hierarchical_select']['selects'][$i]); |
---|
1590 | } |
---|
1591 | } |
---|
1592 | } |
---|
1593 | |
---|
1594 | /** |
---|
1595 | * Append messages to Hierarchical Select's log. Used when in developer mode. |
---|
1596 | * |
---|
1597 | * @param $item |
---|
1598 | * Either a message (string) or an array. |
---|
1599 | * @param $reset |
---|
1600 | * Reset the stored log. |
---|
1601 | * @return |
---|
1602 | * Only when the log is being reset, the stored log is returned. |
---|
1603 | */ |
---|
1604 | function _hierarchical_select_log($item, $reset = FALSE) { |
---|
1605 | static $log; |
---|
1606 | |
---|
1607 | if ($reset) { |
---|
1608 | $copy_of_log = $log; |
---|
1609 | $log = array(); |
---|
1610 | return $copy_of_log; |
---|
1611 | } |
---|
1612 | |
---|
1613 | $log[] = $item; |
---|
1614 | } |
---|
1615 | |
---|
1616 | |
---|
1617 | //---------------------------------------------------------------------------- |
---|
1618 | // Hierarchy object generation functions. |
---|
1619 | |
---|
1620 | /** |
---|
1621 | * Generate the hierarchy object. |
---|
1622 | * |
---|
1623 | * @param $config |
---|
1624 | * A config array with at least the following settings: |
---|
1625 | * - module |
---|
1626 | * - params |
---|
1627 | * - enforce_deepest |
---|
1628 | * - save_lineage |
---|
1629 | * - level_labels |
---|
1630 | * - status |
---|
1631 | * - labels |
---|
1632 | * - editability |
---|
1633 | * - status |
---|
1634 | * - allow_new_levels |
---|
1635 | * - max_levels |
---|
1636 | * @param $selection |
---|
1637 | * The selection based on which a HS should be rendered. |
---|
1638 | * @param $required |
---|
1639 | * Whether the form element is required or not. (#required in Forms API) |
---|
1640 | * @param $dropbox |
---|
1641 | * A dropbox object, or FALSE. |
---|
1642 | * @return |
---|
1643 | * A hierarchy object. |
---|
1644 | */ |
---|
1645 | function _hierarchical_select_hierarchy_generate($config, $selection, $required, $dropbox = FALSE) { |
---|
1646 | $hierarchy = new stdClass(); |
---|
1647 | |
---|
1648 | // Convert the 'special_items' setting to a more easily accessible format. |
---|
1649 | if (isset($config['special_items'])) { |
---|
1650 | $special_items['exclusive'] = array_keys(array_filter($config['special_items'], '_hierarchical_select_special_item_exclusive')); |
---|
1651 | $special_items['none'] = array_keys(array_filter($config['special_items'], '_hierarchical_select_special_item_none')); |
---|
1652 | } |
---|
1653 | |
---|
1654 | |
---|
1655 | // |
---|
1656 | // Build the lineage. |
---|
1657 | // |
---|
1658 | |
---|
1659 | $start_lineage = microtime(); |
---|
1660 | |
---|
1661 | // If save_linage is enabled, reconstruct the lineage. This is necessary |
---|
1662 | // because e.g. the taxonomy module stores the terms by order of weight and |
---|
1663 | // lexicography, rather than by hierarchy. |
---|
1664 | if ($config['save_lineage'] && is_array($selection) && count($selection) >= 2) { |
---|
1665 | // Ensure the item in the root level is the first item in the selection. |
---|
1666 | $root_level = array_keys(module_invoke($config['module'], 'hierarchical_select_root_level', $config['params'])); |
---|
1667 | |
---|
1668 | for ($i = 0; $i < count($selection); $i++) { |
---|
1669 | if (in_array($selection[$i], $root_level)) { |
---|
1670 | if ($i != 0) { // Don't swap if it's already the first item. |
---|
1671 | list($selection[0], $selection[$i]) = array($selection[$i], $selection[0]); |
---|
1672 | } |
---|
1673 | break; |
---|
1674 | } |
---|
1675 | } |
---|
1676 | // Reconstruct all sublevels. |
---|
1677 | for ($i = 0; $i < count($selection); $i++) { |
---|
1678 | $children = array_keys(module_invoke($config['module'], 'hierarchical_select_children', $selection[$i], $config['params'])); |
---|
1679 | |
---|
1680 | // Ensure the next item in the selection is a child of the current item. |
---|
1681 | for ($j = $i + 1; $j < count($selection); $j++) { |
---|
1682 | if (in_array($selection[$j], $children)) { |
---|
1683 | list($selection[$j], $selection[$i + 1]) = array($selection[$i + 1], $selection[$j]); |
---|
1684 | } |
---|
1685 | } |
---|
1686 | } |
---|
1687 | } |
---|
1688 | |
---|
1689 | // Validate the hierarchy. |
---|
1690 | $selection = _hierarchical_select_hierarchy_validate($selection, $config['module'], $config['params']); |
---|
1691 | |
---|
1692 | // When nothing is currently selected, set the root level to: |
---|
1693 | // - "<none>" (or its equivalent special item) when: |
---|
1694 | // - enforce_deepest is enabled *and* level labels are enabled *and* |
---|
1695 | // no root level label is set (1), or |
---|
1696 | // - the dropbox is enabled *and* at least one selection has been added |
---|
1697 | // to the dropbox (2) |
---|
1698 | // - "label_0" (the root level label) in all other cases. |
---|
1699 | if ($selection == -1) { |
---|
1700 | $root_level = module_invoke($config['module'], 'hierarchical_select_root_level', $config['params']); |
---|
1701 | $first_case = $config['enforce_deepest'] && $config['level_labels']['status'] && !isset($config['level_labels']['labels'][0]); |
---|
1702 | $second_case = $dropbox && count($dropbox->lineages) > 0; |
---|
1703 | |
---|
1704 | // If |
---|
1705 | // - the special_items setting has been configured, and |
---|
1706 | // - one special item has the 'none' property |
---|
1707 | // then we'll use the special item instead of the normal "<none>" option. |
---|
1708 | $none_option = (count($special_items['none'])) ? $special_items['none'][0] : 'none'; |
---|
1709 | |
---|
1710 | // Set "<none>" option (or its equivalent special item), or "label_0". |
---|
1711 | $hierarchy->lineage[0] = ($first_case || $second_case) ? $none_option : 'label_0'; |
---|
1712 | } |
---|
1713 | else { |
---|
1714 | // If save_lineage setting is enabled, then the selection *is* a lineage. |
---|
1715 | // If it's disabled, we have to generate one ourselves based on the |
---|
1716 | // (deepest) selected item. |
---|
1717 | if ($config['save_lineage']) { |
---|
1718 | // When the form element is optional, the "<none>" setting can be |
---|
1719 | // selected, thus only the first level will be displayed. As a result, |
---|
1720 | // we won't receive an array as the selection, but only a single item. |
---|
1721 | // We convert this into an array. |
---|
1722 | $hierarchy->lineage = (is_array($selection)) ? $selection : array(0 => $selection); |
---|
1723 | } |
---|
1724 | else { |
---|
1725 | $selection = (is_array($selection)) ? $selection[0] : $selection; |
---|
1726 | if (module_invoke($config['module'], 'hierarchical_select_valid_item', $selection, $config['params'])) { |
---|
1727 | $hierarchy->lineage = module_invoke($config['module'], 'hierarchical_select_lineage', $selection, $config['params']); |
---|
1728 | } |
---|
1729 | else { |
---|
1730 | // If the selected item is invalid, then start with an empty lineage. |
---|
1731 | $hierarchy->lineage = array(); |
---|
1732 | } |
---|
1733 | } |
---|
1734 | } |
---|
1735 | |
---|
1736 | // If enforce_deepest is enabled, ensure that the lineage goes as deep as |
---|
1737 | // possible: append values of items that will be selected by default. |
---|
1738 | if ($config['enforce_deepest'] && !in_array($hierarchy->lineage[0], array('none', 'label_0'))) { |
---|
1739 | $hierarchy->lineage = _hierarchical_select_hierarchy_enforce_deepest($hierarchy->lineage, $config['module'], $config['params']); |
---|
1740 | } |
---|
1741 | |
---|
1742 | $end_lineage = microtime(); |
---|
1743 | |
---|
1744 | |
---|
1745 | // |
---|
1746 | // Build the levels. |
---|
1747 | // |
---|
1748 | |
---|
1749 | $start_levels = microtime(); |
---|
1750 | |
---|
1751 | // Start building the levels, initialize with the root level. |
---|
1752 | $hierarchy->levels[0] = module_invoke($config['module'], 'hierarchical_select_root_level', $config['params']); |
---|
1753 | $hierarchy->levels[0] = _hierarchical_select_apply_entity_settings($hierarchy->levels[0], $config); |
---|
1754 | |
---|
1755 | // Prepend a "<create new item>" option to the root level when: |
---|
1756 | // - the editability setting is enabled, and |
---|
1757 | // - the hook is implemented (this is an optional hook), and |
---|
1758 | // - the allowed_levels setting allows to create new items at this level. |
---|
1759 | if ($config['editability']['status'] |
---|
1760 | && module_hook($config['module'], 'hierarchical_select_create_item') |
---|
1761 | && _hierarchical_select_create_new_item_is_allowed($config, 0) |
---|
1762 | ) { |
---|
1763 | $item_type = isset($config['editability']['item_types'][0]) ? t($config['editability']['item_types'][0]) : ''; |
---|
1764 | $item_type = (!empty($item_type)) ? $item_type : t('item'); |
---|
1765 | $option = theme('hierarchical_select_special_option', t('create new !item_type', array('!item_type' => $item_type))); |
---|
1766 | $hierarchy->levels[0] = array('create_new_item' => $option) + $hierarchy->levels[0]; |
---|
1767 | } |
---|
1768 | |
---|
1769 | // Prepend a "<none>" option to the root level when: |
---|
1770 | // - the form element is optional (1), or |
---|
1771 | // - enforce_deepest is enabled (2), or |
---|
1772 | // - the dropbox is enabled *and* at least one selection has been added to |
---|
1773 | // the dropbox (3) |
---|
1774 | // except when: |
---|
1775 | // - the special_items setting has been configured, and |
---|
1776 | // - one special item has the 'none' property |
---|
1777 | $first_case = !$required; |
---|
1778 | $second_case = $config['enforce_deepest']; |
---|
1779 | $third_case = $dropbox && count($dropbox->lineages) > 0; |
---|
1780 | if (($first_case || $second_case || $third_case) && !count($special_items['none'])) { |
---|
1781 | $option = theme('hierarchical_select_special_option', t('none')); |
---|
1782 | $hierarchy->levels[0] = array('none' => $option) + $hierarchy->levels[0]; |
---|
1783 | } |
---|
1784 | |
---|
1785 | // Calculate the lineage's depth (starting from 0). |
---|
1786 | $max_depth = count($hierarchy->lineage) - 1; |
---|
1787 | |
---|
1788 | // Build all sublevels, based on the lineage. |
---|
1789 | for ($depth = 1; $depth <= $max_depth; $depth++) { |
---|
1790 | $hierarchy->levels[$depth] = module_invoke($config['module'], 'hierarchical_select_children', $hierarchy->lineage[$depth - 1], $config['params']); |
---|
1791 | $hierarchy->levels[$depth] = _hierarchical_select_apply_entity_settings($hierarchy->levels[$depth], $config); |
---|
1792 | } |
---|
1793 | |
---|
1794 | if ($config['enforce_deepest']) { |
---|
1795 | // Prepend a "<create new item>" option to each level below the root level |
---|
1796 | // when: |
---|
1797 | // - the editability setting is enabled, and |
---|
1798 | // - the hook is implemented (this is an optional hook), and |
---|
1799 | // - the allowed_levels setting allows to create new items at this level. |
---|
1800 | if ($config['editability']['status'] && module_hook($config['module'], 'hierarchical_select_create_item')) { |
---|
1801 | for ($depth = 1; $depth <= $max_depth; $depth++) { |
---|
1802 | $item_type = t($config['editability']['item_types'][$depth]); |
---|
1803 | $item_type = (!empty($item_type)) ? $item_type : t('item'); |
---|
1804 | $option = theme('hierarchical_select_special_option', t('create new !item_type', array('!item_type' => $item_type))); |
---|
1805 | if (_hierarchical_select_create_new_item_is_allowed($config, $depth)) { |
---|
1806 | $hierarchy->levels[$depth] = array('create_new_item' => $option) + $hierarchy->levels[$depth]; |
---|
1807 | } |
---|
1808 | } |
---|
1809 | } |
---|
1810 | |
---|
1811 | // If level labels are enabled and the root label is set, prepend it. |
---|
1812 | if ($config['level_labels']['status'] && isset($config['level_labels']['labels'][0])) { |
---|
1813 | $hierarchy->levels[0] = array('label_0' => t($config['level_labels']['labels'][0])) + $hierarchy->levels[0]; |
---|
1814 | } |
---|
1815 | } |
---|
1816 | else if (!$config['enforce_deepest']) { |
---|
1817 | // Prepend special options to every level. |
---|
1818 | for ($depth = 0; $depth <= $max_depth; $depth++) { |
---|
1819 | // Prepend a "<create new item>" option to the current level when: |
---|
1820 | // - this is not the root level (the root level already has this), and |
---|
1821 | // - the editability setting is enabled, and |
---|
1822 | // - the hook is implemented (this is an optional hook), and |
---|
1823 | // - the allowed_levels setting allows to create new items at this level. |
---|
1824 | if ($depth > 0 |
---|
1825 | && $config['editability']['status'] |
---|
1826 | && module_hook($config['module'], 'hierarchical_select_create_item') |
---|
1827 | && _hierarchical_select_create_new_item_is_allowed($config, $depth) |
---|
1828 | ) { |
---|
1829 | $item_type = t($config['editability']['item_types'][$depth]); |
---|
1830 | $item_type = (!empty($item_type)) ? $item_type : t('item'); |
---|
1831 | $option = theme('hierarchical_select_special_option', t('create new !item_type', array('!item_type' => $item_type))); |
---|
1832 | $hierarchy->levels[$depth] = array('create_new_item' => $option) + $hierarchy->levels[$depth]; |
---|
1833 | } |
---|
1834 | // Level label: set an empty level label if they've been disabled. |
---|
1835 | $label = ($config['level_labels']['status'] && isset($config['level_labels']['labels'][$depth])) ? t($config['level_labels']['labels'][$depth]) : ''; |
---|
1836 | $hierarchy->levels[$depth] = array('label_'. $depth => $label) + $hierarchy->levels[$depth]; |
---|
1837 | } |
---|
1838 | |
---|
1839 | // If the root level label is empty and the none option is present, remove |
---|
1840 | // the root level label because it's conceptually identical. |
---|
1841 | if ($hierarchy->levels[0]['label_0'] == '' && isset($hierarchy->levels[0]['none'])) { |
---|
1842 | unset($hierarchy->levels[0]['label_0']); |
---|
1843 | // Update the selected lineage when necessary to prevent an item that |
---|
1844 | // doesn't exist from being "selected" internally. |
---|
1845 | if ($hierarchy->lineage[0] == 'label_0') { |
---|
1846 | $hierarchy->lineage[0] = 'none'; |
---|
1847 | } |
---|
1848 | } |
---|
1849 | |
---|
1850 | // Add one more level if appropriate. |
---|
1851 | $parent = $hierarchy->lineage[$max_depth]; |
---|
1852 | if (module_invoke($config['module'], 'hierarchical_select_valid_item', $parent, $config['params'])) { |
---|
1853 | $children = module_invoke($config['module'], 'hierarchical_select_children', $parent, $config['params']); |
---|
1854 | if (count($children)) { |
---|
1855 | // We're good, let's add one level! |
---|
1856 | $depth = $max_depth + 1; |
---|
1857 | |
---|
1858 | $hierarchy->levels[$depth] = array(); |
---|
1859 | |
---|
1860 | // Prepend a "<create new item>" option to the current level when: |
---|
1861 | // - the editability setting is enabled, and |
---|
1862 | // - the hook is implemented (this is an optional hook), and |
---|
1863 | // - the allowed_levels setting allows to create new items at this level. |
---|
1864 | if ($config['editability']['status'] |
---|
1865 | && module_hook($config['module'], 'hierarchical_select_create_item') |
---|
1866 | && _hierarchical_select_create_new_item_is_allowed($config, $depth) |
---|
1867 | ) { |
---|
1868 | $item_type = t($config['editability']['item_types'][$depth]); |
---|
1869 | $item_type = (!empty($item_type)) ? $item_type : t('item'); |
---|
1870 | $option = theme('hierarchical_select_special_option', t('create new !item_type', array('!item_type' => $item_type))); |
---|
1871 | $hierarchy->levels[$depth] = array('create_new_item' => $option); |
---|
1872 | } |
---|
1873 | |
---|
1874 | // Level label: set an empty level label if they've been disabled. |
---|
1875 | $hierarchy->lineage[$depth] = 'label_'. $depth; |
---|
1876 | $label = ($config['level_labels']['status']) ? t($config['level_labels']['labels'][$depth]) : ''; |
---|
1877 | $hierarchy->levels[$depth] = array('label_'. $depth => $label) + $hierarchy->levels[$depth] + $children; |
---|
1878 | |
---|
1879 | $hierarchy->levels[$depth] = _hierarchical_select_apply_entity_settings($hierarchy->levels[$depth], $config); |
---|
1880 | } |
---|
1881 | } |
---|
1882 | } |
---|
1883 | |
---|
1884 | // Add an extra level with only a level label and a "<create new item>" |
---|
1885 | // option, if: |
---|
1886 | // - the editability setting is enabled |
---|
1887 | // - the allow_new_levels setting is enabled |
---|
1888 | // - an additional level is permitted by the max_levels setting |
---|
1889 | // - the deepest item of the lineage is a valid item |
---|
1890 | // NOTE: this uses an optional hook, so we also check if it's implemented. |
---|
1891 | if ($config['editability']['status'] |
---|
1892 | && $config['editability']['allow_new_levels'] |
---|
1893 | && ($config['editability']['max_levels'] == 0 || count($hierarchy->lineage) < $config['editability']['max_levels']) |
---|
1894 | && module_invoke($config['module'], 'hierarchical_select_valid_item', end($hierarchy->lineage), $config['params']) |
---|
1895 | && module_hook($config['module'], 'hierarchical_select_create_item') |
---|
1896 | ) { |
---|
1897 | $depth = $max_depth + 1; |
---|
1898 | |
---|
1899 | // Level label: set an empty level label if they've been disabled. |
---|
1900 | $hierarchy->lineage[$depth] = 'label_'. $depth; |
---|
1901 | $label = ($config['level_labels']['status']) ? t($config['level_labels']['labels'][$depth]) : ''; |
---|
1902 | |
---|
1903 | // Item type. |
---|
1904 | $item_type = (isset($config['editability']['item_types'][$depth])) ? t($config['editability']['item_types'][$depth]) : NULL; |
---|
1905 | $item_type = (!empty($item_type)) ? $item_type : t('item'); |
---|
1906 | |
---|
1907 | // The new level with only a level label and a "<create new item>" option. |
---|
1908 | $option = theme('hierarchical_select_special_option', t('create new !item_type', array('!item_type' => $item_type))); |
---|
1909 | $hierarchy->levels[$depth] = array( |
---|
1910 | 'label_'. $depth => $label, |
---|
1911 | 'create_new_item' => $option, |
---|
1912 | ); |
---|
1913 | } |
---|
1914 | |
---|
1915 | // Calculate the time it took to generate the levels. |
---|
1916 | $end_levels = microtime(); |
---|
1917 | |
---|
1918 | // Add child information. |
---|
1919 | $start_childinfo = microtime(); |
---|
1920 | $hierarchy = _hierarchical_select_hierarchy_add_childinfo($hierarchy, $config); |
---|
1921 | $end_childinfo = microtime(); |
---|
1922 | |
---|
1923 | // Calculate the time it took to build the hierarchy object. |
---|
1924 | $hierarchy->build_time['total'] = ($end_childinfo - $start_lineage) * 1000; |
---|
1925 | $hierarchy->build_time['lineage'] = ($end_lineage - $start_lineage) * 1000; |
---|
1926 | $hierarchy->build_time['levels'] = ($end_levels - $start_levels) * 1000; |
---|
1927 | $hierarchy->build_time['childinfo'] = ($end_childinfo - $start_childinfo) * 1000; |
---|
1928 | |
---|
1929 | return $hierarchy; |
---|
1930 | } |
---|
1931 | |
---|
1932 | /** |
---|
1933 | * Given a level, apply the entity_count and require_entity settings. |
---|
1934 | * |
---|
1935 | * @param $level |
---|
1936 | * A level in the hierarchy. |
---|
1937 | * @param $config |
---|
1938 | * A config array with at least the following settings: |
---|
1939 | * - module |
---|
1940 | * - params |
---|
1941 | * - entity_count |
---|
1942 | * - require_entity |
---|
1943 | * @return |
---|
1944 | * The updated level |
---|
1945 | */ |
---|
1946 | function _hierarchical_select_apply_entity_settings($level, $config) { |
---|
1947 | if (isset($config['special_items'])) { |
---|
1948 | $special_items['exclusive'] = array_keys(array_filter($config['special_items'], '_hierarchical_select_special_item_exclusive')); |
---|
1949 | $special_items['none'] = array_keys(array_filter($config['special_items'], '_hierarchical_select_special_item_none')); |
---|
1950 | } |
---|
1951 | |
---|
1952 | // Only do something when the entity_count or the require_entity (or both) |
---|
1953 | // settings are enabled. |
---|
1954 | // NOTE: this uses the optional "hierarchical_selectentity_count" hook, so |
---|
1955 | // we also check if it's implemented. |
---|
1956 | if (($config['entity_count'] || $config['require_entity']) && module_hook($config['module'], 'hierarchical_select_entity_count')) { |
---|
1957 | foreach ($level as $item => $label) { |
---|
1958 | // We don't want to alter internal or special items. |
---|
1959 | if (!preg_match('/(none|label_\d+|create_new_item)/', $item) |
---|
1960 | && !in_array($item, $special_items['exclusive']) |
---|
1961 | && !in_array($item, $special_items['none']) |
---|
1962 | ) |
---|
1963 | { |
---|
1964 | $entity_count = module_invoke($config['module'], 'hierarchical_select_entity_count', $item, $config['params']); |
---|
1965 | |
---|
1966 | // When the require_entity setting is enabled and the entity count is |
---|
1967 | // zero, then remove the item from the level. |
---|
1968 | // When the item is not removed from the level due to the above and |
---|
1969 | // the entity_count setting is enabled, update the label of the item |
---|
1970 | // to include the entity count. |
---|
1971 | if ($config['require_entity'] && $entity_count == 0) { |
---|
1972 | unset($level[$item]); |
---|
1973 | } |
---|
1974 | elseif ($config['entity_count']) { |
---|
1975 | $level[$item] = "$label ($entity_count)"; |
---|
1976 | } |
---|
1977 | } |
---|
1978 | } |
---|
1979 | } |
---|
1980 | |
---|
1981 | return $level; |
---|
1982 | } |
---|
1983 | |
---|
1984 | /** |
---|
1985 | * Extends a hierarchy object with child information: for each item in the |
---|
1986 | * hierarchy, the child count will be retrieved and stored in the hierarchy |
---|
1987 | * object, in the "childinfo" property. Items are grouped per level. |
---|
1988 | * |
---|
1989 | * @param $hierarchy |
---|
1990 | * A hierarchy object with the "levels" property set. |
---|
1991 | * @param $config |
---|
1992 | * A config array with at least the following settings: |
---|
1993 | * - module |
---|
1994 | * - params |
---|
1995 | * @return |
---|
1996 | * An updated hierarchy object with the "childinfo" property set. |
---|
1997 | */ |
---|
1998 | function _hierarchical_select_hierarchy_add_childinfo($hierarchy, $config) { |
---|
1999 | foreach ($hierarchy->levels as $depth => $level) { |
---|
2000 | foreach (array_keys($level) as $item) { |
---|
2001 | if (!preg_match('/(none|label_\d+|create_new_item)/', $item)) { |
---|
2002 | $hierarchy->childinfo[$depth][$item] = count(module_invoke($config['module'], 'hierarchical_select_children', $item, $config['params'])); |
---|
2003 | } |
---|
2004 | } |
---|
2005 | } |
---|
2006 | |
---|
2007 | return $hierarchy; |
---|
2008 | } |
---|
2009 | |
---|
2010 | /** |
---|
2011 | * Reset the selection if no valid item was selected. The first item in the |
---|
2012 | * array corresponds to the first selected term. As soon as an invalid item |
---|
2013 | * is encountered, the lineage from that level to the deeper levels should be |
---|
2014 | * unset. This is so to ignore selection of a level label. |
---|
2015 | * |
---|
2016 | * @param $selection |
---|
2017 | * Either a single item id or an array of item ids. |
---|
2018 | * @param $module |
---|
2019 | * The module that should be used for HS hooks. |
---|
2020 | * @param $params |
---|
2021 | * The module that should be passed to HS hooks. |
---|
2022 | * @return |
---|
2023 | * The updated selection. |
---|
2024 | */ |
---|
2025 | function _hierarchical_select_hierarchy_validate($selection, $module, $params) { |
---|
2026 | $valid = TRUE; |
---|
2027 | $selection_levels = count($selection); |
---|
2028 | for ($i = 0; $i < $selection_levels; $i++) { |
---|
2029 | // As soon as one invalid item has been found, we'll stop validating; all |
---|
2030 | // subsequently selected items will be removed from the selection. |
---|
2031 | if ($valid) { |
---|
2032 | $valid = module_invoke($module, 'hierarchical_select_valid_item', $selection[$i], $params); |
---|
2033 | if ($i > 0) { |
---|
2034 | $parent = $selection[$i - 1]; |
---|
2035 | $child = $selection[$i]; |
---|
2036 | $children = array_keys(module_invoke($module, 'hierarchical_select_children', $parent, $params)); |
---|
2037 | $valid = $valid && in_array($child, $children); |
---|
2038 | } |
---|
2039 | } |
---|
2040 | if (!$valid) { |
---|
2041 | unset($selection[$i]); |
---|
2042 | } |
---|
2043 | } |
---|
2044 | |
---|
2045 | if (empty($selection)) { |
---|
2046 | $selection = -1; |
---|
2047 | } |
---|
2048 | |
---|
2049 | return $selection; |
---|
2050 | } |
---|
2051 | |
---|
2052 | /** |
---|
2053 | * Helper function to update the lineage of the hierarchy to ensure that the |
---|
2054 | * user selects an item in the deepest level of the hierarchy. |
---|
2055 | * |
---|
2056 | * @param $lineage |
---|
2057 | * The lineage up to the deepest selection the user has made so far. |
---|
2058 | * @param $module |
---|
2059 | * The module that should be used for HS hooks. |
---|
2060 | * @param $params |
---|
2061 | * The params that should be passed to HS hooks. |
---|
2062 | * @return |
---|
2063 | * The updated lineage. |
---|
2064 | */ |
---|
2065 | function _hierarchical_select_hierarchy_enforce_deepest($lineage, $module, $params) { |
---|
2066 | // Use the deepest item as the first parent. Then apply this algorithm: |
---|
2067 | // 1) get the parent's children, stop if no children |
---|
2068 | // 2) choose the first child as the option that is selected by default, by |
---|
2069 | // adding it to the lineage of the hierarchy |
---|
2070 | // 3) make this child the parent, go to step 1. |
---|
2071 | $parent = end($lineage); // The last item in the lineage is the deepest one. |
---|
2072 | $children = module_invoke($module, 'hierarchical_select_children', $parent, $params); |
---|
2073 | while (count($children)) { |
---|
2074 | $first_child = reset(array_keys($children)); |
---|
2075 | $lineage[] = $first_child; |
---|
2076 | $parent = $first_child; |
---|
2077 | $children = module_invoke($module, 'hierarchical_select_children', $parent, $params); |
---|
2078 | } |
---|
2079 | |
---|
2080 | return $lineage; |
---|
2081 | } |
---|
2082 | |
---|
2083 | |
---|
2084 | //---------------------------------------------------------------------------- |
---|
2085 | // Dropbox object generation functions. |
---|
2086 | |
---|
2087 | /** |
---|
2088 | * Generate the dropbox object. |
---|
2089 | * |
---|
2090 | * @param $config |
---|
2091 | * A config array with at least the following settings: |
---|
2092 | * - module |
---|
2093 | * - save_lineage |
---|
2094 | * - params |
---|
2095 | * - dropbox |
---|
2096 | * - title |
---|
2097 | * @param $selection |
---|
2098 | * The selection based on which a dropbox should be generated. |
---|
2099 | * @return |
---|
2100 | * A dropbox object. |
---|
2101 | */ |
---|
2102 | function _hierarchical_select_dropbox_generate($config, $selection) { |
---|
2103 | $dropbox = new stdClass(); |
---|
2104 | $start = microtime(); |
---|
2105 | |
---|
2106 | $dropbox->title = (!empty($config['dropbox']['title'])) ? filter_xss_admin($config['dropbox']['title']) : t('All selections'); |
---|
2107 | $dropbox->lineages = array(); |
---|
2108 | $dropbox->lineages_selections = array(); |
---|
2109 | |
---|
2110 | // Clean selection. |
---|
2111 | foreach ($selection as $key => $item) { |
---|
2112 | if (!module_invoke($config['module'], 'hierarchical_select_valid_item', $item, $config['params'])) { |
---|
2113 | unset($selection[$key]); |
---|
2114 | } |
---|
2115 | } |
---|
2116 | |
---|
2117 | if (!empty($selection)) { |
---|
2118 | // Store the "save lineage" setting, needed in the rendering layer. |
---|
2119 | $dropbox->save_lineage = $config['save_lineage']; |
---|
2120 | if ($config['save_lineage']) { |
---|
2121 | $dropbox->lineages = _hierarchical_select_dropbox_reconstruct_lineages_save_lineage_enabled($config['module'], $selection, $config['params']); |
---|
2122 | } |
---|
2123 | else { |
---|
2124 | // Retrieve the lineage of each item. |
---|
2125 | foreach ($selection as $item) { |
---|
2126 | $dropbox->lineages[] = module_invoke($config['module'], 'hierarchical_select_lineage', $item, $config['params']); |
---|
2127 | } |
---|
2128 | |
---|
2129 | // We will also need the labels of each item in the rendering layer. |
---|
2130 | foreach ($dropbox->lineages as $id => $lineage) { |
---|
2131 | foreach ($lineage as $level => $item) { |
---|
2132 | $dropbox->lineages[$id][$level] = array('value' => $item, 'label' => module_invoke($config['module'], 'hierarchical_select_item_get_label', $item, $config['params'])); |
---|
2133 | } |
---|
2134 | } |
---|
2135 | } |
---|
2136 | |
---|
2137 | // Sanitize the labels. |
---|
2138 | foreach ($dropbox->lineages as $id => $lineage) { |
---|
2139 | foreach ($lineage as $level => $item) { |
---|
2140 | $dropbox->lineages[$id][$level]['label'] = check_plain($dropbox->lineages[$id][$level]['label']); |
---|
2141 | } |
---|
2142 | } |
---|
2143 | |
---|
2144 | usort($dropbox->lineages, '_hierarchical_select_dropbox_sort'); |
---|
2145 | |
---|
2146 | // Now store each lineage's selection too. This is needed on the client side |
---|
2147 | // to enable the remove button to let the server know which selected items |
---|
2148 | // should be removed. |
---|
2149 | foreach ($dropbox->lineages as $id => $lineage) { |
---|
2150 | if ($config['save_lineage']) { |
---|
2151 | // Store the entire lineage. |
---|
2152 | $dropbox->lineages_selections[$id] = array_map('_hierarchical_select_dropbox_lineage_item_get_value', $lineage); |
---|
2153 | } |
---|
2154 | else { |
---|
2155 | // Store only the last (aka the deepest) value of the lineage. |
---|
2156 | $dropbox->lineages_selections[$id][0] = $lineage[count($lineage) - 1]['value']; |
---|
2157 | } |
---|
2158 | } |
---|
2159 | } |
---|
2160 | |
---|
2161 | // Calculate the time it took to build the dropbox object. |
---|
2162 | $dropbox->build_time = (microtime() - $start) * 1000; |
---|
2163 | |
---|
2164 | return $dropbox; |
---|
2165 | } |
---|
2166 | |
---|
2167 | /** |
---|
2168 | * Helper function to reconstruct the lineages given a set of selected items |
---|
2169 | * and the fact that the "save lineage" setting is enabled. |
---|
2170 | * |
---|
2171 | * Note that it's impossible to predict how many lineages if we know the |
---|
2172 | * number of selected items, exactly because the "save lineage" setting is |
---|
2173 | * enabled. |
---|
2174 | * |
---|
2175 | * Worst case time complexity is O(n^3), optimizations are still possible. |
---|
2176 | * |
---|
2177 | * @param $module |
---|
2178 | * The module that should be used for HS hooks. |
---|
2179 | * @param $selection |
---|
2180 | * The selection based on which a dropbox should be generated. |
---|
2181 | * @param $params |
---|
2182 | * Optional. An array of parameters, which may be necessary for some |
---|
2183 | * implementations. |
---|
2184 | * @return |
---|
2185 | * An array of dropbox lineages. |
---|
2186 | */ |
---|
2187 | function _hierarchical_select_dropbox_reconstruct_lineages_save_lineage_enabled($module, $selection, $params) { |
---|
2188 | // We have to reconstruct all lineages from the given set of selected items. |
---|
2189 | // That means: we have to reconstruct every possible combination! |
---|
2190 | $lineages = array(); |
---|
2191 | $root_level = module_invoke($module, 'hierarchical_select_root_level', $params); |
---|
2192 | |
---|
2193 | foreach ($selection as $key => $item) { |
---|
2194 | // Create new lineage if the item can be found in the root level. |
---|
2195 | if (array_key_exists($item, $root_level)) { |
---|
2196 | $lineages[][0] = array('value' => $item, 'label' => $root_level[$item]); |
---|
2197 | unset($selection[$key]); |
---|
2198 | } |
---|
2199 | } |
---|
2200 | |
---|
2201 | // Keep on trying as long as at least one lineage has been extended. |
---|
2202 | $at_least_one = TRUE; |
---|
2203 | for ($i = 0; $at_least_one; $i++) { |
---|
2204 | $at_least_one = FALSE; |
---|
2205 | $num = count($lineages); |
---|
2206 | |
---|
2207 | // Try to improve every lineage. Make sure we don't iterate over |
---|
2208 | // possibly new lineages. |
---|
2209 | for ($id = 0; $id < $num; $id++) { |
---|
2210 | $children = module_invoke($module, 'hierarchical_select_children', $lineages[$id][$i]['value'], $params); |
---|
2211 | |
---|
2212 | $child_added_to_lineage = FALSE; |
---|
2213 | foreach (array_keys($children) as $child) { |
---|
2214 | if (in_array($child, $selection)) { |
---|
2215 | if (!$child_added_to_lineage) { |
---|
2216 | // Add the child to the lineage. |
---|
2217 | $lineages[$id][$i + 1] = array('value' => $child, 'label' => $children[$child]); |
---|
2218 | $child_added_to_lineage = TRUE; |
---|
2219 | $at_least_one = TRUE; |
---|
2220 | } |
---|
2221 | else { |
---|
2222 | // Create new lineage based on current one and add the child. |
---|
2223 | $lineage = $lineages[$id]; |
---|
2224 | $lineage[$i + 1] = array('value' => $child, 'label' => $children[$child]);; |
---|
2225 | |
---|
2226 | // Add the new lineage to the set of lineages |
---|
2227 | $lineages[] = $lineage; |
---|
2228 | } |
---|
2229 | } |
---|
2230 | } |
---|
2231 | } |
---|
2232 | } |
---|
2233 | |
---|
2234 | return $lineages; |
---|
2235 | } |
---|
2236 | |
---|
2237 | /** |
---|
2238 | * Dropbox lineages sorting callback. |
---|
2239 | * |
---|
2240 | * @param $lineage_a |
---|
2241 | * The first lineage. |
---|
2242 | * @param $lineage_b |
---|
2243 | * The second lineage. |
---|
2244 | * @return |
---|
2245 | * An integer that determines which of the two lineages comes first. |
---|
2246 | */ |
---|
2247 | function _hierarchical_select_dropbox_sort($lineage_a, $lineage_b) { |
---|
2248 | $string_a = implode('', array_map('_hierarchical_select_dropbox_lineage_item_get_label', $lineage_a)); |
---|
2249 | $string_b = implode('', array_map('_hierarchical_select_dropbox_lineage_item_get_label', $lineage_b)); |
---|
2250 | return strcmp($string_a, $string_b); |
---|
2251 | } |
---|
2252 | |
---|
2253 | /** |
---|
2254 | * Helper function needed for the array_map() call in the dropbox sorting |
---|
2255 | * callback. |
---|
2256 | * |
---|
2257 | * @param $item |
---|
2258 | * An item in a dropbox lineage. |
---|
2259 | * @return |
---|
2260 | * The value associated with the "label" key of the item. |
---|
2261 | */ |
---|
2262 | function _hierarchical_select_dropbox_lineage_item_get_label($item) { |
---|
2263 | return t($item['label']); |
---|
2264 | } |
---|
2265 | |
---|
2266 | /** |
---|
2267 | * Helper function needed for the array_map() call in the dropbox lineages |
---|
2268 | * selections creation. |
---|
2269 | * |
---|
2270 | * @param $item |
---|
2271 | * An item in a dropbox lineage. |
---|
2272 | * @return |
---|
2273 | * The value associated with the "value" key of the item. |
---|
2274 | */ |
---|
2275 | function _hierarchical_select_dropbox_lineage_item_get_value($item) { |
---|
2276 | return $item['value']; |
---|
2277 | } |
---|
2278 | |
---|
2279 | /** |
---|
2280 | * Smarter version of array_merge_recursive: overwrites scalar values. |
---|
2281 | * |
---|
2282 | * From: http://www.php.net/manual/en/function.array-merge-recursive.php#82976. |
---|
2283 | */ |
---|
2284 | if (!function_exists('array_smart_merge')) { |
---|
2285 | function array_smart_merge($array, $override) { |
---|
2286 | if (is_array($array) && is_array($override)) { |
---|
2287 | foreach ($override as $k => $v) { |
---|
2288 | if (isset($array[$k]) && is_array($v) && is_array($array[$k])) { |
---|
2289 | $array[$k] = array_smart_merge($array[$k], $v); |
---|
2290 | } |
---|
2291 | else { |
---|
2292 | $array[$k] = $v; |
---|
2293 | } |
---|
2294 | } |
---|
2295 | } |
---|
2296 | return $array; |
---|
2297 | } |
---|
2298 | } |
---|
2299 | |
---|
2300 | /** |
---|
2301 | * Helper function needed for the array_filter() call to filter the items |
---|
2302 | * marked with the 'exclusive' property |
---|
2303 | * |
---|
2304 | * @param $item |
---|
2305 | * An item in the 'special_items' setting. |
---|
2306 | * @return |
---|
2307 | * TRUE if it's marked with the 'exclusive' property, FALSE otherwise. |
---|
2308 | */ |
---|
2309 | function _hierarchical_select_special_item_exclusive($item) { |
---|
2310 | return in_array('exclusive', $item); |
---|
2311 | } |
---|
2312 | |
---|
2313 | /** |
---|
2314 | * Helper function needed for the array_filter() call to filter the items |
---|
2315 | * marked with the 'none' property |
---|
2316 | * |
---|
2317 | * @param $item |
---|
2318 | * An item in the 'special_items' setting. |
---|
2319 | * @return |
---|
2320 | * TRUE if it's marked with the 'none' property, FALSE otherwise. |
---|
2321 | */ |
---|
2322 | function _hierarchical_select_special_item_none($item) { |
---|
2323 | return in_array('none', $item); |
---|
2324 | } |
---|
2325 | |
---|
2326 | |
---|
2327 | /** |
---|
2328 | * Abstraction around drupal_add_js() and Views' $form_state['js settings']. |
---|
2329 | * |
---|
2330 | * @param $settings |
---|
2331 | * The JS settings you'd like to add. |
---|
2332 | * @param $form_state |
---|
2333 | * A form state array. |
---|
2334 | */ |
---|
2335 | function _hierarchical_select_add_js_settings($settings, &$form_state) { |
---|
2336 | global $views_ajax_form_js_settings; |
---|
2337 | |
---|
2338 | // If we're on a Views-powered form, we must use $form_state['js settings']. |
---|
2339 | if (isset($form_state['view']) && isset($form_state['ajax']) && !empty($form_state['ajax'])) { |
---|
2340 | $form_state['js settings'] = (!isset($form_state['js settings']) || !is_array($form_state['js settings'])) ? array() : $form_state['js settings']; |
---|
2341 | $form_state['js settings'] = array_merge_recursive($form_state['js settings'], $settings); |
---|
2342 | } |
---|
2343 | // Otherwise, use drupal_add_js(). |
---|
2344 | else { |
---|
2345 | drupal_add_js($settings, 'setting'); |
---|
2346 | |
---|
2347 | // Necessary for Views AJAX pager support. |
---|
2348 | // Also see hierarchical_select_ajax_data_alter(). |
---|
2349 | if (isset($settings['HierarchicalSelect']['settings'])) { |
---|
2350 | $views_ajax_form_js_settings = $settings['HierarchicalSelect']['settings']; |
---|
2351 | } |
---|
2352 | } |
---|
2353 | } |
---|
2354 | |
---|
2355 | |
---|
2356 | /** |
---|
2357 | * Implementation of hook_ajax_data_alter(). |
---|
2358 | * (Necessary for Views AJAX pager support.) |
---|
2359 | */ |
---|
2360 | function hierarchical_select_ajax_data_alter(&$object, $module, $view) { |
---|
2361 | global $views_ajax_form_js_settings; |
---|
2362 | if (!empty($views_ajax_form_js_settings)) { |
---|
2363 | $object->hs_drupal_js_settings = $views_ajax_form_js_settings; |
---|
2364 | $object->__callbacks = array_merge(array('Drupal.HierarchicalSelect.ajaxViewPagerSettingsUpdate'), $object->__callbacks); |
---|
2365 | } |
---|
2366 | } |
---|