The Most Monstrous Function Ever

  1. /**
  2.  * Hierarchical select form element type #process callback.
  3.  */
  4. function hierarchical_select_process($element) {
  5.   static $hsid;
  6.  
  7.   if (!isset($hsid)) {
  8.     $hsid = 0;
  9.   }
  10.   else {
  11.     $hsid++;
  12.   }
  13.   $element['hsid'] = array('#type' => 'value', '#value' => $hsid);
  14.  
  15.   // A hierarchical_select form element expands to multiple items. For example
  16.   // $element['hsid'] got set just above. If #value is not an array, then
  17.   // form_set_value(), which is called by form_builder() will fail, because it
  18.   // assumes that #value is an array, because we are trying to set a child of
  19.   // it.
  20.   if (!is_array($element['#value'])) {
  21.     $element['#value'] = array($element['#value']);
  22.   }
  23.  
  24.   // Store the #name property of each hierarchical_select form item, this is
  25.   // necessary to find this form item back in an AJAX callback.
  26.   _hierarchical_select_store_name($element, $hsid);
  27.  
  28.   // Set up Javascript and add settings specifically for the current
  29.   // hierarchical select.
  30.   _hierarchical_select_setup_js();
  31.   $config = $element['#config'];
  32.     array(
  33.       'HierarchicalSelect' => array(
  34.         'settings' => array(
  35.           $hsid => array(
  36.             'animationDelay'   => ($config['animation_delay'] == 0) ? (int) variable_get('hierarchical_select_animation_delay', 400) : $config['animation_delay'],
  37.             'cacheId'          => $config['module'] .'_'. implode('_', (is_array($config['params'])) ? $config['params'] : array()),
  38.             'renderFlatSelect' => (isset($config['render_flat_select'])) ? (int) $config['render_flat_select'] : 0,
  39.             'createNewItems'  => (isset($config['editability']['status'])) ? (int) $config['editability']['status'] : 0,
  40.             'createNewLevels'  => (isset($config['editability']['allow_new_levels'])) ? (int) $config['editability']['allow_new_levels'] : 0,
  41.             'resizable'        => (isset($config['resizable'])) ? (int) $config['resizable'] : 0,
  42.           ),
  43.         ),
  44.       )
  45.     ),
  46.     'setting'
  47.   );
  48.  
  49.   // Basic config validation and diagnostics.
  50.   if (HS_DEVELOPER_MODE) {
  51.     $diagnostics = array();
  52.     if (!isset($config['module']) || empty($config['module'])) {
  53.       $diagnostics[] = "'module is not set!";
  54.     }
  55.     else {
  56.       $required_params = module_invoke($config['module'], 'hierarchical_select_params');
  57.       $missing_params = array_diff($required_params, array_keys($config['params']));
  58.       if (!empty($missing_params)) {
  59.         $diagnostics[] = "'params' is missing values for: ". implode(', ', $missing_params) .'.';
  60.       }
  61.     }
  62.     $config_id = (isset($config['config_id']) && is_string($config['config_id'])) ? $config['config_id'] : 'none';
  63.     if (empty($diagnostics)) {
  64.       _hierarchical_select_log("Config diagnostics (config id: $config_id): no problems found!");
  65.     }
  66.     else {
  67.       $diagnostics_string = print_r($diagnostics, TRUE);
  68.       $message = "Config diagnostics (config id: $config_id): $diagnostics_string";
  69.       _hierarchical_select_log($message);
  70.       $element['#type']= 'item';
  71.       $element['#value'] = '<p><span style="color:red;">Fix the indicated errors in the #config property first!</span><br />'. nl2br($message) .'</p>';
  72.       return $element;
  73.     }
  74.   }
  75.  
  76.   // Calculate the selections in both the hierarchical select and the dropbox,
  77.   // we need these before we can render anything.
  78.   list($hs_selection, $db_selection) = _hierarchical_select_process_calculate_selections($element);
  79.  
  80.   if (HS_DEVELOPER_MODE) {
  81.     _hierarchical_select_log("Calculated hierarchical select selection:");
  82.     _hierarchical_select_log($hs_selection);
  83.  
  84.     if ($config['dropbox']['status']) {
  85.       _hierarchical_select_log("Calculated dropbox selection:");
  86.       _hierarchical_select_log($db_selection);
  87.     }
  88.   }
  89.  
  90.   // If the exclusive_lineages setting has been configured, and the dropbox
  91.   // is enabled, then do the necessary processing to make exclusive lineages
  92.   // possible.
  93.   if (count($config['exclusive_lineages']) && $config['dropbox']['status']) {
  94.     // When the form is first loaded, $db_selection will contain the selection
  95.     // that we should check, but in updates, $hs_selection will.
  96.     $selection = (!empty($hs_selection)) ? $hs_selection : $db_selection;
  97.  
  98.     // If the current selection of the hierarchical select matches one of the
  99.     // configured exclusive items, then disable the dropbox (to ensure an
  100.     // exclusive selection).
  101.     if (in_array($selection, $config['exclusive_lineages']) // A lineage.
  102.         || (count($selection) == 1 && in_array($selection[0], $config['exclusive_lineages']))) { // An item at the root level.
  103.       // By also updating the configuration stored in $element, we ensure that
  104.       // the validation step, which extracts the configuration again, also gets
  105.       // the updated config.
  106.       $element['#config']['dropbox']['status'] = 0;
  107.  
  108.       $config = $element['#config'];
  109.  
  110.       // When the form is first loaded, $db_selection contained the selection
  111.       // selection that we checked for. Since we've now disabled the dropbox,
  112.       // we should overwrite $hs_selection with the value of $db_selection and
  113.       // reset $db_selection.
  114.       if (empty($hs_selection)) {
  115.         $hs_selection = $db_selection;
  116.         $db_selection = array();
  117.       }
  118.     }
  119.   }
  120.  
  121.   // Generate the $hierarchy and $dropbox objects using the selections that
  122.   // were just calculated.
  123.   $dropbox = (!$config['dropbox']['status']) ? FALSE : _hierarchical_select_dropbox_generate($config, $db_selection);
  124.   $hierarchy = _hierarchical_select_hierarchy_generate($config, $hs_selection, $element['#required'], $dropbox);
  125.  
  126.   if (HS_DEVELOPER_MODE) {
  127.     _hierarchical_select_log('Generated hierarchy in '. $hierarchy->build_time['total'] .' ms:');
  128.     _hierarchical_select_log($hierarchy);
  129.  
  130.     if ($config['dropbox']['status']) {
  131.       _hierarchical_select_log('Generated dropbox in '. $dropbox->build_time .' ms: ');
  132.       _hierarchical_select_log($dropbox);
  133.     }
  134.   }
  135.  
  136.   // Store the hierarchy object in the element, we'll need this if the user's
  137.   // browser supports the active cache system.
  138.   $element['hierarchy'] = array('#type' => 'value', '#value' => $hierarchy);
  139.  
  140.  
  141.   // Ensure that #tree is enabled!
  142.   $element['#tree'] = TRUE;
  143.  
  144.   // If render_flat_select is enabled, render a flat select.
  145.   if ($config['render_flat_select']) {
  146.     $element['flat_select'] = _hierarchical_select_process_render_flat_select($hierarchy, $dropbox, $config);
  147.   }
  148.  
  149.   // Render the hierarchical select.
  150.   $element['hierarchical_select'] = array(
  151.     '#prefix' => '<div class="hierarchical-select clear-block">',
  152.     '#suffix' => '</div>',
  153.   );
  154.   $element['hierarchical_select']['selects'] = _hierarchical_select_process_render_hs_selects($hsid, $hierarchy);
  155.  
  156.   // The selects in the hierarchical select should inherit the #size property.
  157.   foreach (element_children($element['hierarchical_select']['selects']) as $depth) {
  158.     $element['hierarchical_select']['selects'][$depth]['#size'] = $element['#size'];
  159.   }
  160.  
  161.   // Check if a new item is being created.
  162.   $creating_new_item = FALSE;
  163.   if (isset($element['#value']['hierarchical_select']['selects'])) {
  164.     foreach ($element['#value']['hierarchical_select']['selects'] as $depth => $value) {
  165.       if ($value == 'create_new_item' && _hierarchical_select_create_new_item_is_allowed($config, $depth)) {
  166.         $creating_new_item = TRUE;
  167.  
  168.         // We want to override the select in which the "create_new_item"
  169.         // option was selected and hide all selects after that, if they exist.
  170.         for ($i = $depth; $i < count($hierarchy->lineage); $i++) {
  171.           unset($element['hierarchical_select']['selects'][$i]);
  172.         }
  173.  
  174.         $element['hierarchical_select']['create_new_item'] = array(
  175.           '#prefix' => '<div class="'. str_replace('_', '-', $value) .'">',
  176.           '#suffix' => '</div>',
  177.         );
  178.  
  179.         $item_type_depth = ($value == 'create_new_item') ? $depth : $depth + 1;
  180.         $item_type = (!empty($config['editability']['item_types'][$item_type_depth])) ? t($config['editability']['item_types'][$item_type_depth]) : t('item');
  181.  
  182.         $element['hierarchical_select']['create_new_item']['input'] = array(
  183.           '#type' => 'textfield',
  184.           '#size' => 20,
  185.           '#maxlength' => 255,
  186.           '#default_value' => t('new @item', array('@item' => $item_type)),
  187.           '#attributes' => array(
  188.             'title' => t('new @item', array('@item' => $item_type)),
  189.             'class' => 'create-new-item-input'
  190.           ),
  191.           // Use a #theme callback to prevent the textfield from being wrapped
  192.           // in a div. This simplifies the CSS and JS code.
  193.           '#theme' => 'hierarchical_select_textfield',
  194.         );
  195.  
  196.         $element['hierarchical_select']['create_new_item']['create'] = array(
  197.           '#type' => 'button',
  198.           '#value' => t('Create'),
  199.           '#attributes' => array('class' => 'create-new-item-create'),
  200.         );
  201.  
  202.         $element['hierarchical_select']['create_new_item']['cancel'] = array(
  203.           '#type' => 'button',
  204.           '#value' => t('Cancel'),
  205.           '#attributes' => array('class' => 'create-new-item-cancel'),
  206.         );
  207.       }
  208.     }
  209.   }
  210.  
  211.  
  212.   if ($config['dropbox']['status']) {
  213.     if (!$creating_new_item) {
  214.       // Append an "Add" button to the selects.
  215.       $element['hierarchical_select']['dropbox_add'] = array(
  216.         '#type'       => 'button',
  217.         '#value'      => t('Add'),
  218.         '#attributes' => array('class' => 'add-to-dropbox'),
  219.       );
  220.     }
  221.  
  222.     if ($config['dropbox']['limit'] > 0) { // Zero as dropbox limit means no limit.
  223.       if (count($dropbox->lineages) == $config['dropbox']['limit']) {
  224.         $element['dropbox_limit_warning'] = array(
  225.           '#value'  => t("You've reached the maximal number of items you can select."),
  226.           '#prefix' => '<p class="hierarchical-select-dropbox-limit-warning">',
  227.           '#suffix' => '</p>',
  228.         );
  229.  
  230.         // Disable all child form elements of $element['hierarchical_select].
  231.         _hierarchical_select_mark_as_disabled($element['hierarchical_select']);
  232.       }
  233.     }
  234.  
  235.     // Add the hidden part of the dropbox. This will be used to store the
  236.     // currently selected lineages.
  237.     $element['dropbox']['hidden'] = array(
  238.       '#prefix' => '<div class="dropbox-hidden">',
  239.       '#suffix' => '</div>',
  240.     );
  241.     $element['dropbox']['hidden'] = _hierarchical_select_process_render_db_hidden($hsid, $dropbox);
  242.  
  243.     // Add the dropbox-as-a-table that will be visible to the user.
  244.     $element['dropbox']['visible'] = _hierarchical_select_process_render_db_visible($hsid, $dropbox);
  245.   }
  246.  
  247.   // This button and accompanying help text will be hidden when Javascript is
  248.   // enabled.
  249.   $element['nojs'] = array(
  250.     '#prefix' => '<div class="nojs">',
  251.     '#suffix' => '</div>',
  252.   );
  253.   $element['nojs']['update_button'] = array(
  254.     '#type'       => 'button',
  255.     '#value'      => t('Update'),
  256.     '#attributes' => array('class' => 'update-button'),
  257.   );
  258.   $element['nojs']['update_button_help_text'] = array(
  259.     '#value'  => _hierarchical_select_nojs_helptext($config['dropbox']['status']),
  260.     '#prefix' => '<div class="help-text">',
  261.     '#suffix' => '</div>',
  262.   );
  263.  
  264.  
  265.   // Ensure the render order is correct.
  266.   $element['hierarchical_select']['#weight']   = 0;
  267.   $element['dropbox_limit_warning']['#weight'] = 1;
  268.   $element['dropbox']['#weight']               = 2;
  269.   $element['nojs']['#weight']                  = 3;
  270.  
  271.   // This prevents values from in $element['#post'] to be used instead of the
  272.   // generated default values (#default_value).
  273.   // For example: $element['hierarchical_select']['selects']['0']['#default_value']
  274.   // is set to 'label_0' after an "Add" operation. When $element['#post'] is
  275.   // NOT unset, the corresponding value in $element['#post'] will be used
  276.   // instead of the default value that was set. This is undesired behavior.
  277.   unset($element['#post']);
  278.  
  279.   // Finally, calculate the return value of this hierarchical_select form
  280.   // element. This will be set in _hierarchical_select_validate(). (If we'd
  281.   // set it now, it would be overridden again.)
  282.   $element['#return_value'] = _hierarchical_select_process_calculate_return_value($hierarchy, ($config['dropbox']['status']) ? $dropbox : FALSE, $config['module'], $config['params'], $config['save_lineage']);
  283.  
  284.   // Add a validate callback, which will:
  285.   // - validate that the dropbox limit was not exceeded.
  286.   // - set the return value of this form element.
  287.   $element['#validate'] = array('_hierarchical_select_validate' => array());
  288.  
  289.   if (HS_DEVELOPER_MODE) {
  290.     $element['log'] = array('#type' => 'value', '#value' => _hierarchical_select_log(NULL, TRUE));
  291.       array(
  292.         'HierarchicalSelect' => array(
  293.           'initialLog' => array(
  294.             $hsid => $element['log']['#value'],
  295.           ),
  296.         ),
  297.       ),
  298.       'setting'
  299.     );
  300.   }
  301.  
  302.   // If the form item is marked as disabled, disable all child form items as
  303.   // well.
  304.   if ($element['#disabled']) {
  305.     _hierarchical_select_mark_as_disabled($element);
  306.   }
  307.  
  308.   return $element;
  309. }

Comments

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <blockcode>
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>. Beside the tag style "<foo>" it is also possible to use "[foo]".
  • Lines and paragraphs break automatically.

More information about formatting options