You are here

function better_exposed_filters_exposed_form_plugin::exposed_form_alter in Better Exposed Filters 7.3

Same name and namespace in other branches
  1. 6.3 better_exposed_filters_exposed_form_plugin.inc \better_exposed_filters_exposed_form_plugin::exposed_form_alter()
  2. 6 better_exposed_filters_exposed_form_plugin.inc \better_exposed_filters_exposed_form_plugin::exposed_form_alter()
  3. 7 better_exposed_filters_exposed_form_plugin.inc \better_exposed_filters_exposed_form_plugin::exposed_form_alter()

Tweak the exposed filter form to show Better Exposed Filter options.

Parameters

array $form: Exposed form array.

array $form_state: Current state of form variables.

Overrides views_plugin_exposed_form::exposed_form_alter

File

./better_exposed_filters_exposed_form_plugin.inc, line 809
Provides an Better Exposed Filters exposed form plugin for View 3.x.

Class

better_exposed_filters_exposed_form_plugin
Better exposed filter form plugin class.

Code

function exposed_form_alter(&$form, &$form_state) {
  parent::exposed_form_alter($form, $form_state);

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

  // Collect BEF's Javascript settings, add to Drupal.settings at the end.
  // Historical note: We used to only add BEF's Javascript when absolutely
  // needed. Eventually, much of that functionality worked its way into the
  // normal usage of BEF so that we now turn those Jvaascript behaviors on
  // by default. (See https://drupal.org/node/1807114).
  $bef_add_js = TRUE;
  $bef_js = array(
    'datepicker' => FALSE,
    'slider' => FALSE,
    'settings' => array(),
    'autosubmit' => FALSE,
  );

  // Some widgets will require additional CSS.
  $bef_add_css = FALSE;

  // Grab BEF settings.
  $settings = $this
    ->_bef_get_settings();

  // Allow modules/themes to alter BEF settings before they are passed to the
  // exposed form widgets.
  $context['view'] = $this->view;
  $context['display'] = $this->display;
  drupal_alter('better_exposed_filters_settings', $settings, $context);

  // Adds view's arguments (if any) to the path to be used use for #bef_path.
  $view_path = $this->view->args ? implode('/', array_merge(array(
    $this->view
      ->get_path(),
  ), $this->view->args)) : $this->view
    ->get_path();

  // Some elements may be placed in a secondary fieldset (eg: "Advanced
  // search options"). Place this after the exposed filters and before the
  // rest of the items in the exposed form.
  if ($allow_secondary = $settings['general']['allow_secondary']) {

    // If one of the secondary widgets has exposed input, do not collapse the
    // secondary fieldset. Or is the "always open" or "always closed" option
    // is selected, use that instead.
    $secondary_collapse = TRUE;
    if ($settings['general']['secondary_collapse_override']) {
      $secondary_collapse = $settings['general']['secondary_collapse_override'] == 2;
    }
    else {
      $exposed_input = $this->view
        ->get_exposed_input();
      foreach ($this->display->handler
        ->get_handlers('filter') as $label => $filter) {
        if (!$filter->options['exposed']) {
          continue;
        }
        if (!empty($exposed_input[$filter->options['expose']['identifier']]) && $settings[$label]['more_options']['is_secondary']) {
          $secondary_collapse = FALSE;
          break;
        }
      }
    }
    $secondary = array(
      '#type' => 'fieldset',
      '#title' => filter_xss_admin(t($settings['general']['secondary_label'])),
      '#collapsible' => TRUE,
      '#collapsed' => $secondary_collapse,
      '#theme' => 'secondary_exposed_elements',
    );
  }

  /*
   * Handle exposed sort elements.
   */
  if (isset($settings['sort']) && !empty($form['sort_by']) && !empty($form['sort_order'])) {
    $show_apply = TRUE;

    // If selected, collect all sort-related form elements and put them
    // in a collapsible fieldset.
    $collapse = $settings['sort']['advanced']['collapsible'] && !empty($settings['sort']['advanced']['collapsible_label']);
    $sort_elems = array();

    // Check for combined sort_by and sort_order.
    if ($settings['sort']['advanced']['combine']) {
      $form_state['#combine_param'] = $settings['sort']['advanced']['combine_param'];

      // Combine sort_by and sort_order into a single element.
      $form[$settings['sort']['advanced']['combine_param']] = array(
        '#type' => 'radios',
        // Already sanitized by Views.
        '#title' => $form['sort_by']['#title'],
      );
      $options = array();

      // If using the bef_toggle_links format, determine which links should
      // not be shown.
      $hidden_options = array();

      // Add reset sort option at the top of the list.
      if ($settings['sort']['advanced']['reset']) {
        $options[' '] = t($settings['sort']['advanced']['reset_label']);
      }
      else {
        $form[$settings['sort']['advanced']['combine_param']]['#default_value'] = '';
      }
      $selected = '';
      $used_sort_keys = array();
      foreach ($form['sort_by']['#options'] as $by_key => $by_val) {
        foreach ($form['sort_order']['#options'] as $order_key => $order_val) {

          // Use a space to separate the two keys, we'll unpack them in our
          // submit handler.
          $options["{$by_key} {$order_key}"] = "{$by_val} {$order_val}";
          if ($form['sort_order']['#default_value'] == $order_key && empty($selected)) {

            // Respect default sort order set in Views. The default sort field
            // will be the first one if there are multiple sort criteria.
            $selected = "{$by_key} {$order_key}";

            // If 'Remember the last selection' was selected, remember,
            if (isset($_SESSION['views'][$this->view->name][$this->view->current_display])) {
              $by_key = $_SESSION['views'][$this->view->name][$this->view->current_display]['sort_by'];
              $order_key = $_SESSION['views'][$this->view->name][$this->view->current_display]['sort_order'];
              $selected = "{$by_key} {$order_key}";
            }
          }
          if ($settings['sort']['bef_format'] == 'bef_toggle_links') {
            if (isset($used_sort_keys[$by_key]) || !empty($form_state['input'][$settings['sort']['advanced']['combine_param']]) && $form_state['input'][$settings['sort']['advanced']['combine_param']] == "{$by_key} {$order_key}" || empty($form_state['input'][$settings['sort']['advanced']['combine_param']]) && $selected == "{$by_key} {$order_key}") {
              $hidden_options["{$by_key} {$order_key}"] = "{$by_val} {$order_val}";
            }
            else {
              $used_sort_keys[$by_key] = $order_key;
            }
          }
        }
      }

      // Rewrite the option values if any were specified.
      if (!empty($settings['sort']['advanced']['combine_rewrite'])) {
        $lines = explode("\n", trim($settings['sort']['advanced']['combine_rewrite']));
        $rewrite = array();
        foreach ($lines as $line) {
          list($search, $replace) = explode('|', $line);
          if (isset($search)) {
            $rewrite[$search] = $replace;
          }
        }

        // Create new options array, based on order set in the rewrite settings.
        $rewritten_options = array();
        foreach ($rewrite as $search => $replace) {
          if ($index = array_search($search, $options)) {
            if ('' == $replace) {
              unset($options[$index]);
              if ($selected == $index) {

                // Avoid "Illegal choice" errors.
                $selected = NULL;
              }
            }
            else {
              $rewritten_options[$index] = $replace;
            }
          }
        }

        // Append any remaining non-rewritten options.
        $options = $rewritten_options + $options;
      }
      $form[$settings['sort']['advanced']['combine_param']] = array(
        '#type' => 'radios',
        '#options' => $options,
        '#hidden_options' => $hidden_options,
        '#settings' => array(
          'toggle_links' => $settings['sort']['bef_format'] == 'bef_toggle_links',
          'combine_param' => $settings['sort']['advanced']['combine_param'],
        ),
        '#default_value' => $selected,
        // Already sanitized by Views.
        '#title' => $form['sort_by']['#title'],
      );

      // Handle display-specific details.
      switch ($settings['sort']['bef_format']) {
        case 'bef':
          $form[$settings['sort']['advanced']['combine_param']]['#prefix'] = '<div class="bef-sort-combined bef-select-as-radios">';
          $form[$settings['sort']['advanced']['combine_param']]['#suffix'] = '</div>';
          break;
        case 'bef_links':
        case 'bef_toggle_links':
          $bef_add_js = TRUE;
          $form[$settings['sort']['advanced']['combine_param']]['#theme'] = 'select_as_links';

          // Exposed form displayed as blocks can appear on pages other than
          // the view results appear on. This can cause problems with
          // select_as_links options as they will use the wrong path. We
          // provide a hint for theme functions to correct this.
          if (!empty($this->display->display_options['exposed_block'])) {
            $form[$settings['sort']['advanced']['combine_param']]['#bef_path'] = $view_path;
          }
          break;
        case 'default':
          $form[$settings['sort']['advanced']['combine_param']]['#type'] = 'select';
          break;
      }

      // Add our submit routine to process.
      $form['#submit'][] = 'bef_sort_combine_submit';

      // Pretend we're another exposed form widget.
      $form['#info']['sort-sort_bef_combine'] = array(
        'value' => $settings['sort']['advanced']['combine_param'],
      );

      // Remove the existing sort_by and sort_order elements.
      unset($form['sort_by']);
      unset($form['sort_order']);
      if ($collapse) {
        $sort_elems[] = $settings['sort']['advanced']['combine_param'];
      }
    }
    else {

      // Leave sort_by and sort_order as separate elements.
      if ('bef' == $settings['sort']['bef_format']) {
        $form['sort_by']['#type'] = 'radios';
        if (empty($form['sort_by']['#process'])) {
          $form['sort_by']['#process'] = array();
        }
        array_unshift($form['sort_by']['#process'], 'form_process_radios');
        $form['sort_by']['#prefix'] = '<div class="bef-sortby bef-select-as-radios">';
        $form['sort_by']['#suffix'] = '</div>';
        $form['sort_order']['#type'] = 'radios';
        if (empty($form['sort_order']['#process'])) {
          $form['sort_order']['#process'] = array();
        }
        array_unshift($form['sort_order']['#process'], 'form_process_radios');
        $form['sort_order']['#prefix'] = '<div class="bef-sortorder bef-select-as-radios">';
        $form['sort_order']['#suffix'] = '</div>';
      }
      elseif ('bef_links' == $settings['sort']['bef_format']) {
        $form['sort_by']['#theme'] = 'select_as_links';
        $form['sort_order']['#theme'] = 'select_as_links';

        // Exposed form displayed as blocks can appear on pages other than the
        // view results appear on. This can cause problems with
        // select_as_links options as they will use the wrong path. We provide
        // a hint for theme functions to correct this.
        if (!empty($this->display->display_options['exposed_block'])) {
          $form['sort_by']['#bef_path'] = $form['sort_order']['#bef_path'] = $view_path;
        }
      }
      if ($collapse) {
        $sort_elems[] = 'sort_by';
        $sort_elems[] = 'sort_order';
      }

      // Add reset sort option if selected.
      if ($settings['sort']['advanced']['reset']) {
        array_unshift($form['sort_by']['#options'], $settings['sort']['advanced']['reset_label']);
      }
    }

    /* Ends: if ($settings['sort']['advanced']['combine']) { ... } else { */
    if ($collapse) {
      $form['bef_sort_options'] = array(
        '#type' => 'fieldset',
        '#collapsible' => TRUE,
        '#collapsed' => TRUE,
        '#title' => filter_xss_admin($settings['sort']['advanced']['collapsible_label']),
      );
      foreach ($sort_elems as $elem) {
        $form['bef_sort_options'][$elem] = $form[$elem];
        unset($form[$elem]);
      }
    }

    // Check if this is a secondary form element.
    if ($allow_secondary && $settings['sort']['advanced']['is_secondary']) {
      foreach (array(
        $settings['sort']['advanced']['combine_param'],
        'sort_by',
        'sort_order',
      ) as $elem) {
        if (!empty($form[$elem])) {
          $secondary[$elem] = $form[$elem];
          unset($form[$elem]);
        }
      }
    }
  }
  elseif (isset($settings['sort']) && !empty($form['sort_by'])) {
    if ('bef_links' == $settings['sort']['bef_format']) {
      $bef_add_js = TRUE;
      $form['sort_by']['#theme'] = 'select_as_links';

      // Exposed form displayed as blocks can appear on pages other than
      // the view results appear on. This can cause problems with
      // select_as_links options as they will use the wrong path. We
      // provide a hint for theme functions to correct this.
      if (!empty($this->display->display_options['exposed_block'])) {
        $form['sort_by']['#bef_path'] = $view_path;
      }
    }
  }

  // Apply autosubmit sort values.
  if (empty($this->options['autosubmit']) && !empty($settings['sort']['advanced']['autosubmit'])) {
    $bef_js['autosubmit'] = TRUE;
    foreach (array(
      $settings['sort']['advanced']['combine_param'],
      'sort_by',
      'sort_order',
    ) as $elem) {
      if (!empty($form[$elem])) {
        $form[$elem] = array_merge_recursive($form[$elem], array(
          '#attributes' => array(
            'class' => array(
              'ctools-auto-submit',
            ),
          ),
        ));
      }
    }
  }

  /* Ends: if (isset($settings['sort'])) { */

  /*
   * Handle exposed pager elements.
   */
  if (isset($settings['pager'])) {
    switch ($settings['pager']['bef_format']) {
      case 'bef':
        $show_apply = TRUE;
        $form['items_per_page']['#type'] = 'radios';
        if (empty($form['items_per_page']['#process'])) {
          $form['items_per_page']['#process'] = array();
        }
        array_unshift($form['items_per_page']['#process'], 'form_process_radios');
        $form['items_per_page']['#prefix'] = '<div class="bef-sortby bef-select-as-radios">';
        $form['items_per_page']['#suffix'] = '</div>';
        break;
      case 'bef_links':
        if (count($form['items_per_page']['#options']) > 1) {
          $bef_add_js = TRUE;
          $form['items_per_page']['#theme'] = 'select_as_links';
          $form['items_per_page']['#items_per_page'] = max($form['items_per_page']['#default_value'], key($form['items_per_page']['#options']));

          // Exposed form displayed as blocks can appear on pages other than
          // the view results appear on. This can cause problems with
          // select_as_links options as they will use the wrong path. We
          // provide a hint for theme functions to correct this.
          if (!empty($this->display->display_options['exposed_block'])) {
            $form['items_per_page']['#bef_path'] = $view_path;
          }
        }
        break;
    }

    // Check if this is a secondary form element.
    if ($allow_secondary && $settings['pager']['is_secondary']) {
      foreach (array(
        'items_per_page',
        'offset',
      ) as $elem) {
        if (!empty($form[$elem])) {
          $secondary[$elem] = $form[$elem];
          unset($form[$elem]);
        }
      }
    }
  }

  // Shorthand for all filters in this view.
  $filters = $form_state['view']->display_handler->handlers['filter'];

  // Get the order of all filters.
  $filters_order = array_flip(array_keys($filters));

  // Go through each saved option looking for Better Exposed Filter settings.
  foreach ($settings as $label => $options) {

    // Sanity check: Ensure this filter is an exposed filter.
    if (empty($filters[$label]) || !$filters[$label]->options['exposed']) {
      continue;
    }

    // Form element is designated by the element ID which is user-
    // configurable.
    $filter_key = 'filter-' . (!empty($filters[$label]->options['is_grouped']) ? $filters[$label]->options['group_info']['identifier'] : $label);
    $filter_id = $form['#info'][$filter_key]['value'];

    // Token replacement on BEF Description fields.
    if (!empty($options['more_options']['bef_filter_description'])) {

      // Collect replacement data.
      $data = array();
      $available = $options['more_options']['tokens']['available'];
      if (in_array('vocabulary', $available) && isset($filters[$label]->definition['vocabulary'])) {
        $data['vocabulary'] = taxonomy_vocabulary_machine_name_load($filters[$label]->definition['vocabulary']);
      }

      /* Others? */

      // Replace tokens.
      $options['more_options']['bef_filter_description'] = token_replace($options['more_options']['bef_filter_description'], $data);
      $form[$filter_id]['#bef_description'] = $options['more_options']['bef_filter_description'];
    }

    // Handle filter value rewrites.
    if (!empty($options['more_options']['rewrite']['filter_rewrite_values'])) {
      $lines = explode("\n", trim($options['more_options']['rewrite']['filter_rewrite_values']));
      $rewrite = array();
      foreach ($lines as $line) {
        list($search, $replace) = explode('|', $line);
        if (isset($search)) {
          $rewrite[$search] = $replace;
        }
      }
      foreach ($form[$filter_id]['#options'] as $index => $option) {
        $is_object = FALSE;
        if (is_object($option)) {

          // Taxonomy filters use objects instead of text.
          $is_object = TRUE;
          $option = reset($option->option);

          // Hierarchical filters prepend hyphens to indicate depth. We need
          // to remove them for comparison, but keep them after replacement to
          // ensure nested options display correctly.
          $option = ltrim($option, '-');
        }
        if (isset($rewrite[$option])) {
          if ('' == $rewrite[$option]) {
            unset($form[$filter_id]['#options'][$index]);
          }
          else {
            if ($is_object) {

              // Taxonomy term filters are stored as objects. Use str_replace
              // to ensure that keep hyphens for hierarchical filters.
              $tid = key($form[$filter_id]['#options'][$index]->option);
              $original = current($form[$filter_id]['#options'][$index]->option);
              $form[$filter_id]['#options'][$index]->option[$tid] = str_replace($option, $rewrite[$option], $original);
            }
            else {
              $form[$filter_id]['#options'][$index] = $rewrite[$option];
            }
          }
        }
      }
    }

    // @TODO: Is this conditional needed anymore after the existing settings
    // array default values were added?
    if (!isset($options['bef_format'])) {
      $options['bef_format'] = '';
    }

    // These BEF options require a set of given options to work (namely,
    // $form[$filter_id]['#options'] needs to set). But it is possible to
    // adjust settings elsewhere in the view that removes these options from
    // the form (eg: changing a taxonomy term filter from dropdown to
    // autocomplete). Check for that here and revert to Views' default filter
    // in those cases.
    $requires_options = array(
      'bef',
      'bef_ul',
      'bef_links',
    );
    if (in_array($options['bef_format'], $requires_options) && !array_key_exists('#options', $form[$filter_id])) {
      $options['bef_format'] = 'default';
    }
    switch ($options['bef_format']) {
      case 'bef_datepicker':
        $show_apply = TRUE;
        $bef_add_js = TRUE;
        $bef_js['datepicker'] = TRUE;
        $bef_js['datepicker_options'] = array();
        if (isset($form[$filter_id]['value']['#type']) && 'date_text' == $form[$filter_id]['value']['#type'] || isset($form[$filter_id]['min']) && isset($form[$filter_id]['max']) && 'date_text' == $form[$filter_id]['min']['#type'] && 'date_text' == $form[$filter_id]['max']['#type']) {

          /*
           * Convert Date API formatting to jQuery formatDate formatting.
           *
           * @TODO: To be honest, I'm not sure this is needed.  Can you set a
           * Date API field to accept anything other than Y-m-d? Well, better
           * safe than sorry...
           *
           * @see http://us3.php.net/manual/en/function.date.php
           * @see http://docs.jquery.com/UI/Datepicker/formatDate
           *
           * Array format: PHP date format => jQuery formatDate format
           * (comments are for the PHP format, lines that are commented out do
           * not have a jQuery formatDate equivalent, but maybe someday they
           * will...)
           */
          $convert = array(
            /* Day */

            // Day of the month, 2 digits with leading zeros 01 to 31.
            'd' => 'dd',
            // A textual representation of a day, three letters  Mon through
            // Sun.
            'D' => 'D',
            // Day of the month without leading zeros  1 to 31.
            'j' => 'd',
            // (lowercase 'L') A full textual representation of the day of the
            // week Sunday through Saturday.
            'l' => 'DD',
            // ISO-8601 numeric representation of the day of the week (added
            // in PHP 5.1.0) 1 (for Monday) through 7 (for Sunday).
            // 'N' => ' ',
            // English ordinal suffix for the day of the month, 2 characters
            // st, nd, rd or th. Works well with j.
            // 'S' => ' ',
            // Numeric representation of the day of the week 0 (for Sunday)
            // through 6 (for Saturday).
            // 'w' => ' ',
            // The day of the year (starting from 0) 0 through 365.
            'z' => 'o',
            /* Week */

            // ISO-8601 week number of year, weeks starting on Monday (added
            // in PHP 4.1.0) Example: 42 (the 42nd week in the year).
            // 'W' => ' ',
            //

            /* Month */

            // A full textual representation of a month, such as January or
            // March  January through December.
            'F' => 'MM',
            // Numeric representation of a month, with leading zeros 01
            // through 12.
            'm' => 'mm',
            // A short textual representation of a month, three letters  Jan
            // through Dec.
            'M' => 'M',
            // Numeric representation of a month, without leading zeros  1
            // through 12.
            'n' => 'm',
            // Number of days in the given month 28 through 31.
            // 't' => ' ',
            //

            /* Year */

            // Whether it's a leap year  1 if it is a leap year, 0 otherwise.
            // 'L' => ' ',
            // ISO-8601 year number. This has the same value as Y, except that
            // if the ISO week number (W) belongs to the previous or next
            // year, that year is used instead. (added in PHP 5.1.0).
            // Examples: 1999 or 2003.
            // 'o' => ' ',
            // A full numeric representation of a year, 4 digits Examples:
            // 1999 or 2003.
            'Y' => 'yy',
            // A two digit representation of a year  Examples: 99 or 03.
            'y' => 'y',
          );
          $format = '';
          if (isset($form[$filter_id]['value'])) {
            $format = $form[$filter_id]['value']['#date_format'];
            $form[$filter_id]['value']['#attributes']['class'][] = 'bef-datepicker';

            // This element renders via Drupal's FormAPI so we can use
            // #description instead of passing the description along to BEF's
            // theme functions.
            if (!empty($form[$filter_id]['#bef_description'])) {
              $form[$filter_id]['#description'] = $form[$filter_id]['#bef_description'];
            }
          }
          else {

            // Both min and max share the same format.
            $format = $form[$filter_id]['min']['#date_format'];
            $form[$filter_id]['min']['#attributes']['class'][] = 'bef-datepicker';
            $form[$filter_id]['max']['#attributes']['class'][] = 'bef-datepicker';

            // Description goes with the second field for in-between filters.
            if (!empty($form[$filter_id]['#bef_description'])) {
              $form[$filter_id]['max']['#description'] = $form[$filter_id]['#bef_description'];
            }
          }
          $bef_js['datepicker_options']['dateFormat'] = json_encode(str_replace(array_keys($convert), array_values($convert), $format));
        }
        else {
          $bef_js['datepicker_options']['dateFormat'] = '';

          /*
           * Standard Drupal date field.  Depending on the settings, the field
           * can be at $form[$filter_id] (single field) or
           * $form[$filter_id][subfield] for two-value date fields or filters
           * with exposed operators.
           */
          $fields = array(
            'min',
            'max',
            'value',
          );
          if (count(array_intersect($fields, array_keys($form[$filter_id])))) {
            $final = '';
            foreach ($fields as $field) {
              if (isset($form[$filter_id][$field])) {
                $form[$filter_id][$field]['#attributes']['class'][] = 'bef-datepicker';
                $final = $field;
              }
            }

            // Description goes with the second field for in-between filters.
            if (!empty($form[$filter_id]['#bef_description'])) {
              $form[$filter_id][$final]['#description'] = $form[$filter_id]['#bef_description'];
            }
          }
          else {
            $form[$filter_id]['#attributes']['class'][] = 'bef-datepicker';

            // This element renders via Drupal's FormAPI so we can use
            // #description instead of passing the description along to BEF's
            // theme functions.
            if (!empty($form[$filter_id]['#bef_description'])) {
              $form[$filter_id]['#description'] = $form[$filter_id]['#bef_description'];
            }
          }
        }
        if (!empty($options['more_options']['datepicker_options'])) {
          foreach (explode("\n", $options['more_options']['datepicker_options']) as $setting) {
            list($key, $val) = explode(':', trim($setting), 2);

            // No need to json_encode() this value like we do other datepicker
            // options as it should already be entered in JSON format in the
            // UI. While having an admin enter JSON in a text field is not
            // ideal, it is how the jQueryUI widget documentation shows it in
            // their examples.
            $bef_js['datepicker_options'][$key] = $val;
          }
        }
        break;
      case 'bef_slider':
        $show_apply = TRUE;
        $bef_add_js = TRUE;
        $bef_add_css = TRUE;
        $bef_js['slider'] = TRUE;

        // Add js options for the slider for this filter.
        $bef_js['slider_options'][$filter_id] = array(
          'min' => $options['slider_options']['bef_slider_min'],
          'max' => $options['slider_options']['bef_slider_max'],
          'step' => $options['slider_options']['bef_slider_step'],
          'animate' => $options['slider_options']['bef_slider_animate'],
          'orientation' => $options['slider_options']['bef_slider_orientation'],
          'id' => drupal_html_id($filter_id),
          'viewId' => $form['#id'],
        );

        // We need a wrapping element that covers all elements in the
        // slider -- not just the text fields, but descriptions as well. When
        // placed in the secondary fieldset, we lose what we usually get from
        // the FormAPI.
        if ($options['more_options']['is_secondary']) {
          $form[$filter_id]['#prefix'] = '<div class="bef-slider-wrapper">';
          $form[$filter_id]['#suffix'] = '</div>';
        }

        // This element renders via Drupal's FormAPI so we can use
        // #description instead of passing the description along to BEF's
        // theme functions.
        if (!empty($form[$filter_id]['#bef_description'])) {
          $children = element_children($form[$filter_id]);
          if (count($children) > 1) {

            // Put the description on the last child or this element.
            $form[$filter_id][end($children)]['#description'] = $form[$filter_id]['#bef_description'];
          }
          else {
            $form[$filter_id]['#description'] = $form[$filter_id]['#bef_description'];
          }
        }
        break;
      case 'bef_links':
        $bef_add_js = TRUE;
        $form[$filter_id]['#theme'] = 'select_as_links';

        // Exposed form displayed as blocks can appear on pages other than
        // the view results appear on. This can cause problems with
        // select_as_links options as they will use the wrong path. We provide
        // a hint for theme functions to correct this.
        if (!empty($this->display->display_options['exposed_block'])) {
          $form[$filter_id]['#bef_path'] = $view_path;
        }
        break;
      case 'bef_single':
        $show_apply = TRUE;

        // Use filter label as checkbox label.
        $form[$filter_id]['#title'] = $filters[$label]->options['expose']['label'];
        $form[$filter_id]['#description'] = $options['more_options']['bef_filter_description'];
        $form[$filter_id]['#return_value'] = 1;
        $form[$filter_id]['#type'] = 'checkbox';

        // Views populates missing values in $form_state['input'] with the
        // defaults and a checkbox does not appear in $_GET (or $_POST) so it
        // will appear to be missing when a user submits a form. Because of
        // this, instead of unchecking the checkbox value will revert to the
        // default. More, the default value for select values is reused which
        // results in the checkbox always checked. So we need to add a form
        // element to see whether the form is submitted or not and then we
        // need to look at $_GET directly to see whether the checkbox is
        // there. For security reasons, we must not copy the $_GET value.
        // First, let's figure out a short name for the signal element and
        // then add it.
        if (empty($signal)) {
          for ($signal = 'a'; isset($form[$signal]); $signal++) {
          }

          // This is all the signal element needs.
          $form[$signal]['#type'] = 'hidden';
        }
        $checked = isset($form_state['input'][$signal]) ? isset($_GET[$filter_id]) : $form[$filter_id]['#default_value'];

        // Now we know whether the checkbox is checked or not, set #value
        // accordingly.
        $form[$filter_id]['#value'] = $checked ? $form[$filter_id]['#return_value'] : 0;

        // Handoff to the theme layer.
        $form[$filter_id]['#theme'] = 'checkbox';
        break;
      case 'bef_ul':
        $show_apply = TRUE;
        $form[$filter_id]['#bef_nested'] = TRUE;

      /* Intentionally falling through to case 'bef'. */
      case 'bef':
        $show_apply = TRUE;
        if (empty($form[$filter_id]['#multiple'])) {

          // Single-select -- display as radio buttons.
          $form[$filter_id]['#type'] = 'radios';
          if (empty($form[$filter_id]['#process'])) {
            $form[$filter_id]['#process'] = array();
          }
          array_unshift($form[$filter_id]['#process'], 'form_process_radios');

          // Add description.
          if (!empty($form[$filter_id]['#bef_description'])) {
            $form[$filter_id]['#description'] = $form[$filter_id]['#bef_description'];
          }

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

            // @TODO: The terms 'All' and 'Any' are customizable in Views.
            if ($filters[$label]->options['expose']['multiple']) {

              // 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[$filter_id]['#options']['All']);
            }
            else {

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

          // Render as radio buttons or radio buttons in a collapsible
          // fieldset.
          if (!empty($options['more_options']['bef_collapsible'])) {

            // 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.
            if (isset($form['#info'][$filter_key]['label'])) {
              $form[$filter_id]['#bef_title'] = $form['#info'][$filter_key]['label'];
              unset($form['#info'][$filter_key]['label']);
            }
            if (!empty($options['more_options']['bef_filter_description'])) {
              $form[$filter_id]['#bef_description'] = $options['more_options']['bef_filter_description'];
              if (isset($form[$filter_id]['#description'])) {
                unset($form[$filter_id]['#description']);
              }
            }

            // If the operator is exposed as well, put it inside the fieldset.
            if ($filters[$label]->options['expose']['use_operator']) {
              $operator_id = $filters[$label]->options['expose']['operator_id'];
              $form[$filter_id]['#bef_operator'] = $form[$operator_id];
              unset($form[$operator_id]);
            }

            // Add collapse/expand Javascript and BEF CSS to prevent collapsed
            // fieldset from disappearing.
            if (empty($form[$filter_id]['#attached']['js'])) {
              $form[$filter_id]['#attached']['js'] = array();
            }
            $form[$filter_id]['#attached']['js'][] = 'misc/form.js';
            $form[$filter_id]['#attached']['js'][] = 'misc/collapse.js';
            if (empty($form[$filter_id]['#attached']['css'])) {
              $form[$filter_id]['#attached']['css'] = array();
            }
            $form[$filter_id]['#attached']['css'][] = drupal_get_path('module', 'better_exposed_filters') . '/better_exposed_filters.css';

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

            // Render select element as radio buttons.
            $form[$filter_id]['#attributes']['class'][] = 'bef-select-as-radios';
            $form[$filter_id]['#theme'] = 'select_as_radios';
          }
        }
        else {

          // Render as checkboxes or checkboxes enclosed in a collapsible
          // fieldset.
          if (!empty($options['more_options']['bef_collapsible'])) {

            // 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.
            if (isset($form['#info'][$filter_key]['label'])) {
              $form[$filter_id]['#bef_title'] = $form['#info'][$filter_key]['label'];
              unset($form['#info'][$filter_key]['label']);
            }
            if (!empty($options['more_options']['bef_filter_description'])) {
              $form[$filter_id]['#bef_description'] = $options['more_options']['bef_filter_description'];
              if (isset($form[$filter_id]['#description'])) {
                unset($form[$filter_id]['#description']);
              }
            }

            // If the operator is exposed as well, put it inside the fieldset.
            if ($filters[$label]->options['expose']['use_operator']) {
              $operator_id = $filters[$label]->options['expose']['operator_id'];
              $form[$filter_id]['#bef_operator'] = $form[$operator_id];
              unset($form[$operator_id]);
            }

            // Add collapse/expand Javascript and BEF CSS to prevent collapsed
            // fieldset from disappearing.
            if (empty($form[$filter_id]['#attached']['js'])) {
              $form[$filter_id]['#attached']['js'] = array();
            }
            $form[$filter_id]['#attached']['js'][] = 'misc/form.js';
            $form[$filter_id]['#attached']['js'][] = 'misc/collapse.js';
            if (empty($form[$filter_id]['#attached']['css'])) {
              $form[$filter_id]['#attached']['css'] = array();
            }
            $form[$filter_id]['#attached']['css'][] = drupal_get_path('module', 'better_exposed_filters') . '/better_exposed_filters.css';

            // Take care of adding the fieldset in the theme layer.
            $form[$filter_id]['#theme'] = 'select_as_checkboxes_fieldset';
          }
          else {
            $form[$filter_id]['#theme'] = 'select_as_checkboxes';
          }
          if ($options['more_options']['bef_select_all_none'] || $options['more_options']['bef_select_all_none_nested']) {
            $bef_add_js = TRUE;
            if ($options['more_options']['bef_select_all_none']) {
              $form[$filter_id]['#bef_select_all_none'] = TRUE;
            }
            if ($options['more_options']['bef_select_all_none_nested']) {
              $form[$filter_id]['#bef_select_all_none_nested'] = TRUE;
            }
          }
        }

        /* Ends: if (empty($form[$filter_id]['#multiple'])) { ... } else { */

        // Add term descriptions, if appropriate. Pass along to theme
        // functions for rendering.
        if ($options['more_options']['bef_term_description'] && $filters[$filter_id] instanceof views_handler_filter_term_node_tid) {
          $tids = array();
          foreach ($form[$filter_id]['#options'] as $tid => $option) {
            if (is_object($option)) {
              reset($option->option);
              $tid = key($option->option);
            }
            $tids[] = $tid;
          }
          $terms = taxonomy_term_load_multiple($tids);
          foreach ($terms as $tid => $term) {
            $form[$filter_id]['#bef_term_descriptions'][$tid] = check_markup($term->description, $term->format, '', TRUE);
          }
        }
        break;
      case 'bef_hidden':

        // Hide the label.
        $form['#info'][$filter_key]['label'] = '';
        if (empty($form[$filter_id]['#multiple'])) {
          $form[$filter_id]['#type'] = 'hidden';
        }
        else {
          $form[$filter_id]['#theme'] = 'select_as_hidden';
        }
        break;
      default:

        // Handles functionality for exposed filters that are not rendered
        // using BEF.
        $show_apply = TRUE;

        // Add a description to the exposed filter.
        if (!empty($options['more_options']['bef_filter_description'])) {
          $children = element_children($form[$filter_id]);
          if (count($children) > 1) {

            // A filter may have multiple children if it's a in-between
            // filter. In this case, put the description on the last item.
            $form[$filter_id][end($children)]['#description'] = t($options['more_options']['bef_filter_description']);
          }
          else {
            $form[$filter_id]['#description'] = t($options['more_options']['bef_filter_description']);
          }
        }
        break;
    }

    /* Ends switch ($options['bef_format']) */

    // Apply autosubmit filter values.
    if (empty($this->options['autosubmit']) && !empty($options['more_options']['autosubmit'])) {
      $bef_js['autosubmit'] = TRUE;

      // Check type form element.
      if (isset($form[$filter_id]['value'])) {
        $form[$filter_id]['value'] = array_merge_recursive($form[$filter_id]['value'], array(
          '#attributes' => array(
            'class' => array(
              'ctools-auto-submit',
            ),
          ),
        ));
      }
      else {
        $form[$filter_id] = array_merge_recursive($form[$filter_id], array(
          '#attributes' => array(
            'class' => array(
              'ctools-auto-submit',
            ),
          ),
        ));
      }
    }

    // Override "Any" label, if applicable.
    if (!empty($options['more_options']['any_label']) && !empty($form[$filter_id]['#options']['All'])) {
      $form[$filter_id]['#options']['All'] = $options['more_options']['any_label'];
    }

    // Check if this is a secondary form element.
    if ($allow_secondary && $settings[$label]['more_options']['is_secondary']) {
      $identifier = $form['#info'][$filter_key]['value'];
      if (!empty($form[$identifier])) {

        // Move from the main form to the secondary options fieldset.
        $children = element_children($form[$identifier]);
        $secondary[$identifier] = $form[$identifier];
        unset($form[$identifier]);
        if (1 < count($children)) {

          // Some elements can have multiple children, for example min/max
          // fields on an in-between filter. In those cases Add the label
          // to the first element.
          $secondary[$identifier][$children[0]]['#title'] = isset($form['#info'][$filter_key]['label']) ? $form['#info'][$filter_key]['label'] : '';
          unset($form['#info'][$filter_key]);
        }
        else {
          $secondary[$identifier]['#title'] = isset($form['#info'][$filter_key]['label']) ? $form['#info'][$filter_key]['label'] : '';
          unset($form['#info'][$filter_key]);
        }

        // Ensure secondary options respect ordering of filters. Pass this
        // along to the theme function for rendering. Multiply existing
        // position by 2 so that we have room to stick the exposed operator
        // before the filter.
        if (isset($filters[$label]->position)) {
          $secondary[$identifier]['#bef_position'] = $filters[$label]->position * 2;
        }
        else {
          $secondary[$identifier]['#bef_position'] = $filters_order[$label] * 2;
        }

        // Move exposed operators with exposed filters.
        if (!empty($filters[$label]->options['expose']['use_operator'])) {
          $op_id = $filters[$label]->options['expose']['operator_id'];
          $secondary[$op_id] = $form[$op_id];
          unset($form[$op_id]);

          // Make sure operators appear just before the filter they are
          // associated with.
          if (isset($filters[$label]->position)) {
            $secondary[$op_id]['#bef_position'] = $filters[$label]->position * 2 - 1;
          }
          else {
            $secondary[$op_id]['#bef_position'] = $filters_order[$label] * 2 - 1;
          }
        }
      }
    }
  }

  // If our form has no visible filters, hide the submit button.
  if (!$show_apply) {
    if (isset($form['submit'])) {
      $form['submit']['#attributes']['class'][] = 'element-hidden';
    }
    if (isset($form['reset'])) {
      $form['reset']['#attributes']['class'][] = 'element-hidden';
    }
  }

  // Add Javascript as needed.
  if ($bef_add_js) {

    // Add jQuery UI library code as needed.
    if ($bef_js['datepicker']) {
      drupal_add_library('system', 'ui.datepicker');
    }
    if ($bef_js['slider']) {
      drupal_add_library('system', 'ui.slider');
    }
    if ($bef_js['autosubmit']) {
      $form['submit']['#attributes']['class'][] = 'ctools-use-ajax';
      $form['submit']['#attributes']['class'][] = 'ctools-auto-submit-click';
      $form['#attached']['js'][] = drupal_get_path('module', 'ctools') . '/js/auto-submit.js';
    }
    drupal_add_js(array(
      'better_exposed_filters' => $bef_js,
    ), 'setting');
    drupal_add_js(drupal_get_path('module', 'better_exposed_filters') . '/better_exposed_filters.js');
  }
  if ($bef_add_css) {
    drupal_add_css(drupal_get_path('module', 'better_exposed_filters') . '/better_exposed_filters.css');
  }

  // Check for secondary elements.
  if ($allow_secondary && !empty($secondary)) {

    // Add secondary elements after regular exposed filter elements.
    $remaining = array_splice($form, count($form['#info']) + 1);
    $form['secondary'] = $secondary;
    $form = array_merge($form, $remaining);
    $form['#info']['filter-secondary']['value'] = 'secondary';
  }
}