function better_exposed_filters_exposed_form_plugin::exposed_form_alter in Better Exposed Filters 7.3
Same name and namespace in other branches
- 6.3 better_exposed_filters_exposed_form_plugin.inc \better_exposed_filters_exposed_form_plugin::exposed_form_alter()
- 6 better_exposed_filters_exposed_form_plugin.inc \better_exposed_filters_exposed_form_plugin::exposed_form_alter()
- 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';
}
}