You are here

function better_exposed_filters_form_alter in Better Exposed Filters 6.2

Same name and namespace in other branches
  1. 6 better_exposed_filters.module \better_exposed_filters_form_alter()

Implements hook_form_alter()

File

./better_exposed_filters.module, line 14
Allows the use of checkboxes, radio buttons or hidden fields for exposed Views filters.

Code

function better_exposed_filters_form_alter(&$form, $form_state, $form_id) {

  /*
   * Add new display options to the views config form
   *
   * NOTE: In Views 2.x these options were saved as part of the View and we use hook_form_alter
   *    to inject them into the filter config form.  In Views 3.x, with the addition of exposed
   *    forms, we can now do all of this in a plugin (better_exposed_filters_exposed_form_plugin.inc).
   *
   *    To keep things straight, I've created several branches: (currently) two 6.x branches,
   *    one to track Views 2.x (this branch) and another to track Views 3.x.
   *
   *    However, now there is duplicate code in several locations.  Any changes to hook_form_alter
   *    will need to be replicated in better_exposed_filters_exposed_form_plugin (either in
   *    options_form for options settings or exposed_form_alter for displaying the filter).
   */
  if ('2' != substr(views_api_version(), 0, 1)) {

    // Sanity check: only continue for Views 2.x
    return;
  }
  if ('views_ui_config_item_form' == $form_id && !empty($form['options']['expose'])) {

    // Collect current BEF values or set default values
    $curr = array();

    // Build additional form elements to inject into the exposed filter configuration form
    $left = $right = array();

    // Description is now allowed for any exposed filter
    $curr['description'] = empty($form_state['handler']->options['expose']['bef_filter_description']) ? '' : $form_state['handler']->options['expose']['bef_filter_description'];

    // Build a description option form element
    $left['bef_filter_description'] = array(
      '#type' => 'textarea',
      '#title' => t('Description'),
      '#default_value' => $curr['description'],
    );

    // Is this a field we can fully override?  If so, add additional BEF configuration options
    $overrideable = array(
      'select',
      'checkboxes',
      'radios',
    );
    if (in_array($form['options']['value']['#type'], $overrideable)) {

      // Collect the remaining existing/default values for BEF-operable exposed filters
      $curr['format'] = empty($form_state['handler']->options['expose']['bef_format']) ? 'default' : $form_state['handler']->options['expose']['bef_format'];
      $curr['allnone'] = empty($form_state['handler']->options['expose']['bef_select_all_none']) ? FALSE : $form_state['handler']->options['expose']['bef_select_all_none'];
      $curr['collapsible'] = empty($form_state['handler']->options['expose']['bef_collapsible']) ? FALSE : $form_state['handler']->options['expose']['bef_collapsible'];
      $curr['termcount'] = empty($form_state['handler']->options['expose']['bef_show_term_count']) ? FALSE : $form_state['handler']->options['expose']['bef_show_term_count'];
      $curr['termdepth'] = empty($form_state['handler']->options['expose']['bef_taxonomy_depth']) ? 0 : $form_state['handler']->options['expose']['bef_taxonomy_depth'];

      // Build format selection element
      $left['bef_format'] = array(
        '#type' => 'select',
        '#title' => t('Display exposed filter as'),
        '#default_value' => $curr['format'],
        '#options' => array(
          'default' => t('Default select list'),
          'bef' => t('Checkboxes/Radio Buttons'),
          'bef_ul' => t('Nested Checkboxes/Radio Buttons'),
          'bef_hidden' => t('Hidden'),
        ),
        '#description' => t('Select a format for the exposed filter.  The "Nested" option places
           hierarchical taxonomy filters in a nested, unordered list.
           The "Hidden" option is
           generally used for multi-step filters.  Note: if "Force single"
           is checked, radio buttons will be used.  If "Force single" is
           unchecked, checkboxes will be used.'),
      );
      if ($form['options']['value']['#type'] == 'radios' && count($form['options']['value']['#options']) == 3 && isset($form['options']['value']['#options'][1]) && isset($form['options']['value']['#options'][0])) {
        $left['bef_format']['#options']['bef_single'] = t('Single on/off checkbox');
      }

      // Build check all/none option form element
      $right['bef_select_all_none'] = array(
        '#type' => 'checkbox',
        '#title' => t('Add select all/none links'),
        '#default_value' => $curr['allnone'],
        '#description' => t('Add a "Select All/None" link when rendering the exposed filter using
           checkboxes. NOTE: The link is built at page load, so it will not appear
           in the "Live Preview" which is loaded dynamically.'),
      );

      // Put filter in collapsible fieldset option
      $right['bef_collapsible'] = array(
        '#type' => 'checkbox',
        '#title' => t('Make this filter collapsible'),
        '#default_value' => $curr['collapsible'],
        '#description' => t('Puts this filter in a collapsible fieldset'),
      );

      // Build show term count form element
      if (t('Taxonomy') == $form_state['handler']->definition['group']) {
        $right['bef_show_term_count'] = array(
          '#type' => 'checkbox',
          '#title' => t('Show term node count'),
          '#default_value' => $curr['termcount'],
          '#description' => t('Show number of nodes after each term, in paranthesis, like this:
            "Term A (4), Term B (10)", etc.'),
        );

        // Add option to limit depth of hierarchical taxonomy vocabs
        if ('views_handler_filter_term_node_tid_depth' == $form_state['handler']->definition['handler']) {
          $options = array();
          $options[0] = t('No limit');
          for ($i = 1; $i <= 10; $i++) {
            $options[$i] = format_plural($i, '1 level deep', '@count levels deep');
          }
          $right['bef_taxonomy_depth'] = array(
            '#type' => 'select',
            '#title' => t('Limit taxonomy w/ depth filter to'),
            '#options' => $options,
            '#default_value' => $curr['termdepth'],
            '#description' => t('Limit the depth of a hierarchical taxonomy filter to this depth. For example, if your taxonomy includes Country -> State -> City, setting a limit of 2 would show only Country and State in the filter.'),
          );
        }
      }

      // if (t('Taxonomy') == $form_state['handler']->definition['group']) {
    }

    // if (in_array($form['options']['value']['#type'], $overrideable)) {
    // Insert BEF form elements into the exposed filter form
    if (!empty($left)) {
      $expose = $form['options']['expose'];
      $first_chunk = array_splice($expose, 0, array_search('end_left', array_keys($expose)));
      $form['options']['expose'] = array_merge($first_chunk, $left, $expose);
    }
    if (!empty($right)) {
      $expose = $form['options']['expose'];
      $first_chunk = array_splice($expose, 0, array_search('end_checkboxes', array_keys($expose)));
      $form['options']['expose'] = array_merge($first_chunk, $right, $expose);
    }
  }

  // if ('views_ui_config_item_form' ...) {

  /*
   * Update exposed filters to show either checkboxes or radio buttons
   */
  if ('views_exposed_form' == $form_id) {

    // If we have no visible filters, we don't show the Apply button
    $show_apply = FALSE;

    // Go through each filter checking for a Better Exposed Filter setting
    foreach ($form_state['view']->filter as $field => $filter) {
      if ($filter->options['exposed']) {

        // Form element is designated by the element ID which is user-configurable
        $field_id = $form['#info']["filter-{$field}"]['value'];

        // Show number of nodes after each term
        if ($filter->options['expose']['bef_show_term_count']) {
          $form[$field_id]['#termcount'] = $filter->options['expose']['bef_show_term_count'];
        }

        // Limit the depth of hierarchical taxonomy term filters
        if (!empty($filter->options['expose']['bef_taxonomy_depth'])) {
          $limit = $filter->options['expose']['bef_taxonomy_depth'];

          // Because top level terms have no '-' prepended to them
          $limit--;
          $remaining = array();
          foreach ($form[$field_id]['#options'] as $index => $option) {
            if (!is_object($option)) {

              // Most likely the "Any" option
              $remaining[] = $option;
              continue;
            }
            $option_text = $option->option;
            if (!$option_text) {

              // ????
              continue;
            }
            $option_text = array_pop($option_text);
            $true_option = ltrim($option_text[0], '-');
            if (!empty($true_option)) {
              $depth = strpos($option_text, $true_option);
              if ($depth === FALSE || $depth <= $limit) {
                $remaining[] = $option;
              }
            }
          }
          $form[$field_id]['#options'] = $remaining;
        }

        // Add a description to the exposed filter
        if (isset($filter->options['expose']['bef_filter_description'])) {
          $form[$field_id]['#description'] = $filter->options['expose']['bef_filter_description'];
        }
        if (isset($filter->options['expose']['bef_format'])) {
          switch ($filter->options['expose']['bef_format']) {
            case 'bef_ul':
              $show_apply = TRUE;
              $form[$field_id]['#bef_nested'] = TRUE;

            // Intentionally falling through
            case 'bef':
            case 'bef_single':
              $show_apply = TRUE;
              if (empty($form[$field_id]['#multiple'])) {

                // Single-select -- display as radio buttons
                $form[$field_id]['#type'] = 'radios';
                $form[$field_id]['#process'] = array(
                  'expand_radios',
                  'views_process_dependency',
                );

                // Use the single checkbox
                if ($filter->options['expose']['bef_format'] == 'bef_single') {
                  $form[$field_id]['#type'] = 'checkbox';
                  $form[$field_id]['#title'] = $form['#info']["filter-{$field_id}"]['label'];

                  // Remove this option. We only need the values 1 and 0
                  unset($form[$field_id]['#options']['All']);

                  // Drupal core has a bug dealing with type checkbox. This is a
                  // fix to make sure the checkboxes are on and off based on user
                  // selection
                  if ($form_state['input'][$field_id] == 'All') {
                    $form[$field_id]['#default_value'] = 0;
                    $form_state['input'][$field_id] = 0;
                  }
                  $form[$field_id]['#value'] = $form_state['input'][$field_id];
                }

                // Clean up objects from the options array (happens for taxonomy-based filters)
                $options = $form[$field_id]['#options'];
                $form[$field_id]['#options'] = array();
                foreach ($options as $index => $option) {
                  if (is_object($option)) {
                    foreach ($option->option as $key => $val) {
                      $form[$field_id]['#options'][$key] = $val;
                    }
                  }
                  else {
                    $form[$field_id]['#options'][$index] = $option;
                  }
                }
                if (isset($form[$field_id]['#options']['All'])) {

                  // @TODO: The terms 'All' and 'Any' are customizable in Views
                  if (!$filter->options['expose']['optional']) {

                    // Some third-party filter handlers still add the "Any" option even if this is not
                    // an optional filter.  Zap it here if they do.
                    unset($form[$field_id]['#options']['All']);
                  }
                  else {

                    // Otherwise, make sure the "Any" text is clean
                    $form[$field_id]['#options']['All'] = check_plain($form[$field_id]['#options']['All']);
                  }
                }

                // Render as radio buttons or radio buttons in a collapsible fieldset
                if (!empty($filter->options['expose']['bef_collapsible'])) {

                  // Use the option label for the title of the fieldset
                  $form[$field_id]['#title'] = $form['#info']["filter-{$field}"]['label'];
                  unset($form['#info']["filter-{$field}"]['label']);

                  // Pass the description and title along in a way such that it doesn't get rendered as part of
                  // the exposed form widget.  We'll render them as part of the fieldset.
                  $form[$field_id]['#bef_description'] = $form[$field_id]['#description'];
                  unset($form[$field_id]['#description']);
                  $form[$field_id]['#bef_title'] = $form[$field_id]['#title'];
                  unset($form[$field_id]['#title']);

                  // Take care of adding the fieldset in the theme layer
                  $form[$field_id]['#theme'] = 'select_as_radios_fieldset';
                }
                else {

                  // Render select element as radio buttons
                  $form[$field_id]['#theme'] = 'select_as_radios';
                }
              }
              else {

                // Render as checkboxes or checkboxes enclosed in a collapsible fieldset
                if (!empty($filter->options['expose']['bef_collapsible'])) {

                  // Use exposed filter widget label as legend for this fieldset
                  $form[$field_id]['#title'] = $form['#info']["filter-{$field}"]['label'];
                  unset($form['#info']["filter-{$field}"]['label']);
                  $form[$field_id]['#theme'] = 'select_as_checkboxes_fieldset';
                }
                else {
                  $form[$field_id]['#theme'] = 'select_as_checkboxes';
                }

                // Add BEF's JavaScript to the mix to handle select all/none functionality
                drupal_add_js(drupal_get_path('module', 'better_exposed_filters') . '/better_exposed_filters.js');

                // Add select all/none functionality to this filter.
                if ($filter->options['expose']['bef_select_all_none']) {
                  if (!isset($form[$field_id]['#attributes'])) {
                    $form[$field_id]['#attributes'] = array();
                  }
                  if (empty($form[$field_id]['#attributes']['class'])) {
                    $form[$field_id]['#attributes']['class'] = 'bef-select-all-none';
                  }
                  else {
                    $form[$field_id]['#attributes']['class'] .= ' bef-select-all-none';
                  }
                }
              }

              // if (empty($form[$field_id]['#multiple'])) { ... } else {
              break;
            case 'bef_hidden':
              $form['#info']["filter-{$field}"]['label'] = '';

              // Hide the label
              if (empty($form[$field_id]['#multiple'])) {
                $form[$field_id]['#type'] = 'hidden';
              }
              else {
                $form[$field_id]['#theme'] = 'select_as_hidden';
              }
              break;
            case 'default':
              $show_apply = TRUE;
              break;
          }

          // switch ($filter->options['expose']['bef_format']) {
        }
        else {

          // This is an exposed filter that is not controled by BEF.
          $show_apply = TRUE;
        }
      }

      // if ($filter->options['exposed']) {
    }

    // foreach (...)
    // If our form has no visible filters, hide the submit button.
    $form['submit']['#access'] = $show_apply;
  }

  // if ('views_exposed_form' == $form_id)
}