function hierarchical_select_process in Hierarchical Select 5.3
Same name and namespace in other branches
- 5 hierarchical_select.module \hierarchical_select_process()
- 5.2 hierarchical_select.module \hierarchical_select_process()
- 6.3 hierarchical_select.module \hierarchical_select_process()
Hierarchical select form element type #process callback.
File
- ./
hierarchical_select.module, line 316 - This module defines the "hierarchical_select" form element, which is a greatly enhanced way for letting the user select items in a hierarchy.
Code
function hierarchical_select_process($element) {
if (!isset($element['#value']['hsid'])) {
// The HSID is stored in the session, to allow for multiple Hierarchical
// Select form items on the same page of which at least one is added through
// AHAH. A normal static variable won't do in this case, because then at
// least two Hierarchical Select form items will have HSID 0, because they
// are generated in different requests, both of which will have a first HSID
// of 0. This will then cause problems on the page.
if (!isset($_SESSION['hsid'])) {
$_SESSION['hsid'] = 0;
}
else {
// Let the HSID go from 0 to 99, then start over. Larger numbers are
// pointless: who's going to use more than a hundred Hierarchical Select
// form items on the same page?
$_SESSION['hsid'] = ($_SESSION['hsid'] + 1) % 100;
}
$hsid = $_SESSION['hsid'];
}
else {
$hsid = check_plain($element['#value']['hsid']);
}
$element['hsid'] = array(
'#type' => 'hidden',
'#value' => $hsid,
);
// A hierarchical_select form element expands to multiple items. For example
// $element['hsid'] got set just above. If #value is not an array, then
// form_set_value(), which is called by form_builder() will fail, because it
// assumes that #value is an array, because we are trying to set a child of
// it.
if (!is_array($element['#value'])) {
$element['#value'] = array(
$element['#value'],
);
}
// Store the #name property of each hierarchical_select form item, this is
// necessary to find this form item back in an AJAX callback.
_hierarchical_select_store_name($element, $hsid);
// Set up Javascript and add settings specifically for the current
// hierarchical select.
_hierarchical_select_setup_js();
$config = _hierarchical_select_inherit_default_config($element['#config']);
drupal_add_js(array(
'HierarchicalSelect' => array(
'settings' => array(
$hsid => array(
'animationDelay' => $config['animation_delay'] == 0 ? (int) variable_get('hierarchical_select_animation_delay', 400) : $config['animation_delay'],
'cacheId' => $config['module'] . '_' . implode('_', is_array($config['params']) ? $config['params'] : array()),
'renderFlatSelect' => isset($config['render_flat_select']) ? (int) $config['render_flat_select'] : 0,
'createNewItems' => isset($config['editability']['status']) ? (int) $config['editability']['status'] : 0,
'createNewLevels' => isset($config['editability']['allow_new_levels']) ? (int) $config['editability']['allow_new_levels'] : 0,
'resizable' => isset($config['resizable']) ? (int) $config['resizable'] : 0,
),
),
),
), 'setting');
// Basic config validation and diagnostics.
if (HS_DEVELOPER_MODE) {
$diagnostics = array();
if (!isset($config['module']) || empty($config['module'])) {
$diagnostics[] = t("'module is not set!");
}
elseif (!module_exists($config['module'])) {
$diagnostics[] = t('the module that should be used (module) is not installed!', array(
'%module' => $config['module'],
));
}
else {
$required_params = module_invoke($config['module'], 'hierarchical_select_params');
$missing_params = array_diff($required_params, array_keys($config['params']));
if (!empty($missing_params)) {
$diagnostics[] = t("'params' is missing values for: ") . implode(', ', $missing_params) . '.';
}
}
$config_id = isset($config['config_id']) && is_string($config['config_id']) ? $config['config_id'] : 'none';
if (empty($diagnostics)) {
_hierarchical_select_log("Config diagnostics (config id: {$config_id}): no problems found!");
}
else {
$diagnostics_string = print_r($diagnostics, TRUE);
$message = "Config diagnostics (config id: {$config_id}): {$diagnostics_string}";
_hierarchical_select_log($message);
$element['#type'] = 'item';
$element['#value'] = '<p><span style="color:red;">Fix the indicated errors in the #config property first!</span><br />' . nl2br($message) . '</p>';
return $element;
}
}
// Calculate the selections in both the hierarchical select and the dropbox,
// we need these before we can render anything.
list($hs_selection, $db_selection) = _hierarchical_select_process_calculate_selections($element);
if (HS_DEVELOPER_MODE) {
_hierarchical_select_log("Calculated hierarchical select selection:");
_hierarchical_select_log($hs_selection);
if ($config['dropbox']['status']) {
_hierarchical_select_log("Calculated dropbox selection:");
_hierarchical_select_log($db_selection);
}
}
// If the exclusive_lineages setting has been configured, and the dropbox
// is enabled, then do the necessary processing to make exclusive lineages
// possible.
if (count($config['exclusive_lineages']) && $config['dropbox']['status']) {
// When the form is first loaded, $db_selection will contain the selection
// that we should check, but in updates, $hs_selection will.
$selection = !empty($hs_selection) ? $hs_selection : $db_selection;
// If the current selection of the hierarchical select matches one of the
// configured exclusive items, then disable the dropbox (to ensure an
// exclusive selection).
if (in_array($selection, $config['exclusive_lineages']) || count($selection) == 1 && in_array($selection[0], $config['exclusive_lineages'])) {
// An item at the root level.
// By also updating the configuration stored in $element, we ensure that
// the validation step, which extracts the configuration again, also gets
// the updated config.
$element['#config']['dropbox']['status'] = 0;
$config = _hierarchical_select_inherit_default_config($element['#config']);
// When the form is first loaded, $db_selection contained the selection
// selection that we checked for. Since we've now disabled the dropbox,
// we should overwrite $hs_selection with the value of $db_selection and
// reset $db_selection.
if (empty($hs_selection)) {
$hs_selection = $db_selection;
$db_selection = array();
}
}
}
// Generate the $hierarchy and $dropbox objects using the selections that
// were just calculated.
$dropbox = !$config['dropbox']['status'] ? FALSE : _hierarchical_select_dropbox_generate($config, $db_selection);
$hierarchy = _hierarchical_select_hierarchy_generate($config, $hs_selection, $element['#required'], $dropbox);
if (HS_DEVELOPER_MODE) {
_hierarchical_select_log('Generated hierarchy in ' . $hierarchy->build_time['total'] . ' ms:');
_hierarchical_select_log($hierarchy);
if ($config['dropbox']['status']) {
_hierarchical_select_log('Generated dropbox in ' . $dropbox->build_time . ' ms: ');
_hierarchical_select_log($dropbox);
}
}
// Store the hierarchy object in the element, we'll need this if the user's
// browser supports the active cache system.
$element['hierarchy'] = array(
'#type' => 'value',
'#value' => $hierarchy,
);
// Ensure that #tree is enabled!
$element['#tree'] = TRUE;
// If render_flat_select is enabled, render a flat select.
if ($config['render_flat_select']) {
$element['flat_select'] = _hierarchical_select_process_render_flat_select($hierarchy, $dropbox, $config);
}
// Render the hierarchical select.
$element['hierarchical_select'] = array(
'#theme' => 'hierarchical_select_selects_container',
);
$element['hierarchical_select']['selects'] = _hierarchical_select_process_render_hs_selects($hsid, $hierarchy);
// The selects in the hierarchical select should inherit the #size property.
foreach (element_children($element['hierarchical_select']['selects']) as $depth) {
$element['hierarchical_select']['selects'][$depth]['#size'] = $element['#size'];
}
// Check if a new item is being created.
$creating_new_item = FALSE;
if (isset($element['#value']['hierarchical_select']['selects'])) {
foreach ($element['#value']['hierarchical_select']['selects'] as $depth => $value) {
if ($value == 'create_new_item' && _hierarchical_select_create_new_item_is_allowed($config, $depth)) {
$creating_new_item = TRUE;
// We want to override the select in which the "create_new_item"
// option was selected and hide all selects after that, if they exist.
for ($i = $depth; $i < count($hierarchy->lineage); $i++) {
unset($element['hierarchical_select']['selects'][$i]);
}
$element['hierarchical_select']['create_new_item'] = array(
'#prefix' => '<div class="' . str_replace('_', '-', $value) . '">',
'#suffix' => '</div>',
);
$item_type_depth = $value == 'create_new_item' ? $depth : $depth + 1;
$item_type = !empty($config['editability']['item_types'][$item_type_depth]) ? t($config['editability']['item_types'][$item_type_depth]) : t('item');
$element['hierarchical_select']['create_new_item']['input'] = array(
'#type' => 'textfield',
'#size' => 20,
'#maxlength' => 255,
'#default_value' => t('new @item', array(
'@item' => $item_type,
)),
'#attributes' => array(
'title' => t('new @item', array(
'@item' => $item_type,
)),
'class' => 'create-new-item-input',
),
// Use a #theme callback to prevent the textfield from being wrapped
// in a div. This simplifies the CSS and JS code.
'#theme' => 'hierarchical_select_textfield',
);
$element['hierarchical_select']['create_new_item']['create'] = array(
'#type' => 'button',
'#value' => t('Create'),
'#attributes' => array(
'class' => 'create-new-item-create',
),
);
$element['hierarchical_select']['create_new_item']['cancel'] = array(
'#type' => 'button',
'#value' => t('Cancel'),
'#attributes' => array(
'class' => 'create-new-item-cancel',
),
);
}
}
}
if ($config['dropbox']['status']) {
if (!$creating_new_item) {
// Append an "Add" button to the selects.
$element['hierarchical_select']['dropbox_add'] = array(
'#type' => 'button',
'#value' => t('Add'),
'#attributes' => array(
'class' => 'add-to-dropbox',
),
);
}
if ($config['dropbox']['limit'] > 0) {
// Zero as dropbox limit means no limit.
if (count($dropbox->lineages) == $config['dropbox']['limit']) {
$element['dropbox_limit_warning'] = array(
'#value' => t("You've reached the maximal number of items you can select."),
'#prefix' => '<p class="hierarchical-select-dropbox-limit-warning">',
'#suffix' => '</p>',
);
// Disable all child form elements of $element['hierarchical_select].
_hierarchical_select_mark_as_disabled($element['hierarchical_select']);
}
}
// Add the hidden part of the dropbox. This will be used to store the
// currently selected lineages.
$element['dropbox']['hidden'] = array(
'#prefix' => '<div class="dropbox-hidden">',
'#suffix' => '</div>',
);
$element['dropbox']['hidden'] = _hierarchical_select_process_render_db_hidden($hsid, $dropbox);
// Add the dropbox-as-a-table that will be visible to the user.
$element['dropbox']['visible'] = _hierarchical_select_process_render_db_visible($hsid, $dropbox);
}
// This button and accompanying help text will be hidden when Javascript is
// enabled.
$element['nojs'] = array(
'#prefix' => '<div class="nojs">',
'#suffix' => '</div>',
);
$element['nojs']['update_button'] = array(
'#type' => 'button',
'#value' => t('Update'),
'#attributes' => array(
'class' => 'update-button',
),
);
$element['nojs']['update_button_help_text'] = array(
'#value' => _hierarchical_select_nojs_helptext($config['dropbox']['status']),
'#prefix' => '<div class="help-text">',
'#suffix' => '</div>',
);
// Ensure the render order is correct.
$element['hierarchical_select']['#weight'] = 0;
$element['dropbox_limit_warning']['#weight'] = 1;
$element['dropbox']['#weight'] = 2;
$element['nojs']['#weight'] = 3;
// This prevents values from in $element['#post'] to be used instead of the
// generated default values (#default_value).
// For example: $element['hierarchical_select']['selects']['0']['#default_value']
// is set to 'label_0' after an "Add" operation. When $element['#post'] is
// NOT unset, the corresponding value in $element['#post'] will be used
// instead of the default value that was set. This is undesired behavior.
unset($element['#post']);
// Finally, calculate the return value of this hierarchical_select form
// element. This will be set in _hierarchical_select_validate(). (If we'd
// set it now, it would be overridden again.)
$element['#return_value'] = _hierarchical_select_process_calculate_return_value($hierarchy, $config['dropbox']['status'] ? $dropbox : FALSE, $config['module'], $config['params'], $config['save_lineage']);
// Add a validate callback, which will:
// - validate that the dropbox limit was not exceeded.
// - set the return value of this form element.
$element['#validate'] = array(
'_hierarchical_select_validate' => array(),
);
if (HS_DEVELOPER_MODE) {
$element['log'] = array(
'#type' => 'value',
'#value' => _hierarchical_select_log(NULL, TRUE),
);
drupal_add_js(array(
'HierarchicalSelect' => array(
'initialLog' => array(
$hsid => $element['log']['#value'],
),
),
), 'setting');
}
// If the form item is marked as disabled, disable all child form items as
// well.
if ($element['#disabled']) {
_hierarchical_select_mark_as_disabled($element);
}
return $element;
}