better_exposed_filters.module in Better Exposed Filters 6.2
Allows the use of checkboxes, radio buttons or hidden fields for exposed Views filters.
Thanks to Ben Buckman (http://echodittolabs.org/) for the original concept.
File
better_exposed_filters.moduleView source
<?php
/**
* @file
* Allows the use of checkboxes, radio buttons or hidden fields for exposed Views filters.
*
* Thanks to Ben Buckman (http://echodittolabs.org/) for the original concept.
*/
/**
* Implements hook_form_alter()
*/
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)
}
/**
* Implements hook_theme()
*/
function better_exposed_filters_theme($existing, $type, $theme, $path) {
return array(
'select_as_checkboxes' => array(
'function' => 'theme_select_as_checkboxes',
'file' => 'better_exposed_filters.theme',
),
'select_as_checkboxes_fieldset' => array(
'function' => 'theme_select_as_checkboxes_fieldset',
'file' => 'better_exposed_filters.theme',
),
'select_as_radios' => array(
'function' => 'theme_select_as_radios',
'file' => 'better_exposed_filters.theme',
),
'select_as_radios_fieldset' => array(
'function' => 'theme_select_as_radios_fieldset',
'file' => 'better_exposed_filters.theme',
),
'select_as_hidden' => array(
'function' => 'theme_select_as_hidden',
'file' => 'better_exposed_filters.theme',
),
'select_as_tree' => array(
'function' => 'theme_select_as_tree',
'file' => 'better_exposed_filters.theme',
),
'select_as_links' => array(
'function' => 'theme_select_as_links',
'file' => 'better_exposed_filters.theme',
),
);
}
Functions
Name | Description |
---|---|
better_exposed_filters_form_alter | Implements hook_form_alter() |
better_exposed_filters_theme | Implements hook_theme() |