View source
<?php
namespace Drupal\better_exposed_filters\Plugin\views\exposed_form;
use Drupal\Component\Utility\Html;
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;
use Drupal\views\Plugin\views\exposed_form\InputRequired;
use Drupal\views\Plugin\views\filter\NumericFilter;
use Drupal\views\Plugin\views\filter\StringFilter;
class BetterExposedFilters extends InputRequired {
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
$bef_options = array();
$existing = $this
->getSettings();
$textInputRequired = $form['text_input_required'];
unset($form['text_input_required']);
$form['input_required'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Input required'),
'#description' => $this
->t('Only display results after the user has selected a filter option.'),
'#default_value' => !empty($this->options['input_required']),
];
$form['text_input_required'] = $textInputRequired += [
'#states' => [
'visible' => [
'input[name="exposed_form_options[input_required]"]' => [
'checked' => TRUE,
],
],
],
];
$bef_options['general']['allow_secondary'] = array(
'#type' => 'checkbox',
'#title' => $this
->t('Enable secondary exposed form options'),
'#default_value' => $existing['general']['allow_secondary'],
'#description' => $this
->t('Allows you to specify some exposed form elements as being secondary options and places those elements in a collapsible "details" element. Use this option to place some exposed filters in an "Advanced Search" area of the form, for example.'),
);
$bef_options['general']['secondary_label'] = array(
'#type' => 'textfield',
'#default_value' => $existing['general']['secondary_label'],
'#title' => $this
->t('Secondary options label'),
'#description' => $this
->t('The name of the details element to hold secondary options. This cannot be left blank or there will be no way to show/hide these options.'),
'#states' => array(
'required' => array(
':input[name="exposed_form_options[bef][general][allow_secondary]"]' => array(
'checked' => TRUE,
),
),
'visible' => array(
':input[name="exposed_form_options[bef][general][allow_secondary]"]' => array(
'checked' => TRUE,
),
),
),
);
$bef_options['general']['autosubmit'] = array(
'#type' => 'checkbox',
'#title' => $this
->t('Autosubmit'),
'#description' => $this
->t('Automatically submit the form once an element is changed.'),
'#default_value' => $existing['general']['autosubmit'],
);
$bef_options['general']['autosubmit_exclude_textfield'] = array(
'#type' => 'checkbox',
'#title' => $this
->t('Exclude Textfield'),
'#description' => $this
->t('Exclude Textfield from autosubmit. User will have to press enter key or click submit.'),
'#default_value' => $existing['general']['autosubmit_exclude_textfield'],
'#states' => array(
'visible' => array(
':input[name="exposed_form_options[bef][general][autosubmit]"]' => array(
'checked' => TRUE,
),
),
),
);
$bef_options['general']['autosubmit_textfield_delay'] = array(
'#type' => 'textfield',
'#title' => $this
->t('Delay for textfield autosubmit'),
'#description' => $this
->t('Configure a delay in ms before triggering autosubmit on textfields.'),
'#default_value' => $existing['general']['autosubmit_textfield_delay'],
'#states' => array(
'visible' => array(
':input[name="exposed_form_options[bef][general][autosubmit]"]' => array(
'checked' => TRUE,
),
':input[name="exposed_form_options[bef][general][autosubmit_exclude_textfield]"]' => array(
'checked' => FALSE,
),
),
),
);
$bef_options['general']['autosubmit_hide'] = array(
'#type' => 'checkbox',
'#title' => $this
->t('Hide submit button'),
'#description' => $this
->t('Hide submit button if javascript is enabled.'),
'#default_value' => $existing['general']['autosubmit_hide'],
'#states' => array(
'visible' => array(
':input[name="exposed_form_options[bef][general][autosubmit]"]' => array(
'checked' => TRUE,
),
),
),
);
$exposed = FALSE;
foreach ($this->view->display_handler
->getHandlers('sort') as $label => $sort) {
if ($sort
->isExposed()) {
$exposed = TRUE;
break;
}
}
if ($exposed) {
$bef_options['sort']['bef_format'] = array(
'#type' => 'select',
'#title' => $this
->t('Display exposed sort options as'),
'#default_value' => $existing['sort']['bef_format'],
'#options' => array(
'default' => $this
->t('Default Views element'),
'bef' => $this
->t('Radio Buttons'),
'bef_links' => $this
->t('Links'),
),
'#description' => $this
->t('Select a format for the exposed sort options.'),
);
$bef_options['sort']['advanced'] = array(
'#type' => 'details',
'#title' => $this
->t('Advanced sort options'),
);
$bef_options['sort']['advanced']['collapsible'] = array(
'#type' => 'checkbox',
'#title' => $this
->t('Make sort options collapsible'),
'#default_value' => $existing['sort']['advanced']['collapsible'],
'#description' => $this
->t('Puts the sort options in a collapsible details element.'),
);
$bef_options['sort']['advanced']['collapsible_label'] = array(
'#type' => 'textfield',
'#title' => $this
->t('Collapsible details element title'),
'#default_value' => empty($existing['sort']['advanced']['collapsible_label']) ? $this
->t('Sort options') : $existing['sort']['advanced']['collapsible_label'],
'#description' => $this
->t('This cannot be left blank or there will be no way to show/hide sort options.'),
'#states' => array(
'visible' => array(
':input[name="exposed_form_options[bef][sort][advanced][collapsible]"]' => array(
'checked' => TRUE,
),
),
'required' => array(
':input[name="exposed_form_options[bef][sort][advanced][collapsible]"]' => array(
'checked' => TRUE,
),
),
),
);
$bef_options['sort']['advanced']['combine'] = array(
'#type' => 'checkbox',
'#title' => $this
->t('Combine sort order with sort by'),
'#default_value' => $existing['sort']['advanced']['combine'],
'#description' => $this
->t('Combines the sort by options and order (ascending or decending) into a single list. Use this to display "Option1 (ascending)", "Option1 (descending)", "Option2 (ascending)", "Option2 (descending)" in a single form element. Sort order should first be exposed by selecting <em>Allow people to choose the sort order</em>.'),
'#states' => [
'enabled' => [
':input[name="exposed_form_options[expose_sort_order]"]' => [
'checked' => TRUE,
],
],
],
);
$bef_options['sort']['advanced']['combine_rewrite'] = array(
'#type' => 'textarea',
'#title' => $this
->t('Rewrite the text displayed'),
'#default_value' => $existing['sort']['advanced']['combine_rewrite'],
'#description' => $this
->t('Use this field to rewrite the text displayed for combined sort options and sort order. Use the format of current_text|replacement_text, one replacement per line. For example: <pre>
Post date Asc|Oldest first
Post date Desc|Newest first
Title Asc|A -> Z
Title Desc|Z -> A</pre> Leave the replacement text blank to remove an option altogether. The order the options appear will be changed to match the order of options in this field.'),
'#states' => array(
'visible' => array(
':input[name="exposed_form_options[bef][sort][advanced][combine]"]' => array(
'checked' => TRUE,
),
),
),
);
$bef_options['sort']['advanced']['reset'] = array(
'#type' => 'checkbox',
'#title' => $this
->t('Include a "Reset sort" option'),
'#default_value' => $existing['sort']['advanced']['reset'],
'#description' => $this
->t('Adds a "Reset sort" link; Views will use the default sort order.'),
);
$bef_options['sort']['advanced']['reset_label'] = array(
'#type' => 'textfield',
'#title' => $this
->t('"Reset sort" label'),
'#default_value' => $existing['sort']['advanced']['reset_label'],
'#description' => $this
->t('This cannot be left blank if the above option is checked'),
'#states' => array(
'visible' => array(
':input[name="exposed_form_options[bef][sort][advanced][reset]"]' => array(
'checked' => TRUE,
),
),
'required' => array(
':input[name="exposed_form_options[bef][sort][advanced][reset]"]' => array(
'checked' => TRUE,
),
),
),
);
$bef_options['sort']['advanced']['is_secondary'] = array(
'#type' => 'checkbox',
'#title' => $this
->t('This is a secondary option'),
'#default_value' => $existing['sort']['advanced']['is_secondary'],
'#states' => array(
'visible' => array(
':input[name="exposed_form_options[bef][general][allow_secondary]"]' => array(
'checked' => TRUE,
),
),
),
'#description' => $this
->t('Places this element in the secondary options portion of the exposed form.'),
);
}
if (isset($this->display->display_options['pager']) && $this->display->display_options['pager']['options']['expose']['items_per_page']) {
$bef_options['pager']['bef_format'] = array(
'#type' => 'select',
'#title' => $this
->t('Display exposed pager options as'),
'#default_value' => $existing['pager']['bef_format'],
'#options' => array(
'default' => $this
->t('Default (Views render element)'),
'bef' => $this
->t('Radio Buttons'),
'bef_links' => $this
->t('Links'),
),
'#description' => $this
->t('Select a format for the exposed pager options.'),
);
$bef_options['pager']['is_secondary'] = array(
'#type' => 'checkbox',
'#title' => $this
->t('This is a secondary option'),
'#default_value' => $existing['pager']['is_secondary'],
'#states' => array(
'visible' => array(
':input[name="exposed_form_options[bef][general][allow_secondary]"]' => array(
'checked' => TRUE,
),
),
),
'#description' => $this
->t('Places this element in the secondary options portion of the exposed form.'),
);
}
$bef_filter_intro = FALSE;
foreach ($this->view->display_handler
->getHandlers('filter') as $label => $filter) {
if (!$filter
->isExposed()) {
continue;
}
if (!$bef_filter_intro) {
$bef_options['bef_intro'] = array(
'#markup' => '<h3>' . $this
->t('Exposed Filter Settings') . '</h3><p>' . $this
->t('This section lets you select additional options for exposed filters. Some options are only available in certain situations. If you do not see the options you expect, please see the <a href=":link">BEF settings documentation page</a> for more details.', array(
':link' => Url::fromUri('http://drupal.org/node/1701012')
->toString(),
)) . '</p>',
);
$bef_filter_intro = TRUE;
}
$bef_standard = FALSE;
$bef_single = FALSE;
$bef_nested = FALSE;
$bef_nested_links = FALSE;
$bef_datepicker = FALSE;
$bef_slider = FALSE;
if (is_a($filter, 'Drupal\\views\\Plugin\\views\\filter\\StringFilter') || is_a($filter, 'Drupal\\views\\Plugin\\views\\filter\\InOperator')) {
if (in_array($filter->operator, array(
'in',
'or',
'and',
))) {
$bef_standard = TRUE;
}
if (in_array($filter->operator, array(
'empty',
'not empty',
))) {
$bef_standard = TRUE;
if (!$filter->options['expose']['multiple']) {
$bef_single = TRUE;
}
}
}
if (is_a($filter, 'Drupal\\views\\Plugin\\views\\filter\\BooleanOperator')) {
$bef_standard = TRUE;
if (!$filter->options['expose']['multiple']) {
$bef_single = TRUE;
}
}
if (is_a($filter, 'Drupal\\taxonomy\\Plugin\\views\\filter\\TaxonomyIndexTid')) {
if ($this->displayHandler->handlers['filter'][$label]->options['type'] != 'select') {
$bef_standard = FALSE;
}
elseif (!empty($this->displayHandler->handlers['filter'][$label]->options['hierarchy'])) {
$bef_nested = TRUE;
$bef_nested_links = TRUE;
}
}
if (is_a($filter, 'Drupal\\views\\Plugin\\views\\filter\\Date') || !empty($filter->date_handler)) {
$bef_datepicker = TRUE;
}
if (is_a($filter, 'Drupal\\views\\Plugin\\views\\filter\\NumericFilter') && !is_a($filter, 'Drupal\\views\\Plugin\\views\\filter\\Date')) {
$bef_slider = TRUE;
}
$display_options = array(
'default' => $this
->t('Default (Views render element)'),
);
if ($bef_standard) {
$display_options['bef'] = $this
->t('Checkboxes/Radio Buttons');
}
if ($bef_nested) {
$display_options['bef_ul'] = $this
->t('Nested Checkboxes/Radio Buttons');
}
if ($bef_single) {
$display_options['bef_single'] = $this
->t('Single on/off checkbox');
}
if ($bef_datepicker) {
$display_options['bef_datepicker'] = $this
->t('jQuery UI Datepicker');
}
if ($bef_slider) {
$display_options['bef_slider'] = $this
->t('jQuery UI slider');
}
if ($bef_standard) {
$display_options['bef_links'] = $this
->t('Links');
if ($bef_nested_links) {
$display_options['bef_ul_links'] = $this
->t('Nested Links');
}
$display_options['bef_hidden'] = $this
->t('Hidden');
}
\Drupal::moduleHandler()
->alter('better_exposed_filters_display_options', $display_options, $filter);
$identifier = '"' . $filter->options['expose']['identifier'] . '"';
if (!empty($filter->options['expose']['label'])) {
$identifier .= $this
->t(' (Filter label: "@fl")', array(
'@fl' => $filter->options['expose']['label'],
));
}
$bef_options[$label]['bef_format'] = array(
'#type' => 'select',
'#title' => $this
->t('Display @identifier exposed filter as', array(
'@identifier' => $identifier,
)),
'#default_value' => $existing[$label]['bef_format'],
'#options' => $display_options,
);
if ($bef_slider) {
$bef_options[$label]['slider_options'] = array(
'#type' => 'details',
'#title' => $this
->t('Slider options for @identifier', array(
'@identifier' => $identifier,
)),
'#open' => TRUE,
'#states' => array(
'visible' => array(
':input[name="exposed_form_options[bef][' . $label . '][bef_format]"]' => array(
'value' => 'bef_slider',
),
),
),
);
$bef_options[$label]['slider_options']['bef_slider_min'] = array(
'#type' => 'textfield',
'#title' => $this
->t('Range minimum'),
'#default_value' => $this->options['bef'][$label]['slider_options']['bef_slider_min'],
'#bef_filter_id' => $label,
'#states' => array(
'required' => array(
':input[name="exposed_form_options[bef][' . $label . '][bef_format]"]' => array(
'value' => 'bef_slider',
),
),
),
'#description' => $this
->t('The minimum allowed value for the jQuery range slider. It can be positive, negative, or zero and have up to 11 decimal places.'),
);
$bef_options[$label]['slider_options']['bef_slider_max'] = array(
'#type' => 'textfield',
'#title' => $this
->t('Range maximum'),
'#default_value' => $this->options['bef'][$label]['slider_options']['bef_slider_max'],
'#bef_filter_id' => $label,
'#states' => array(
'required' => array(
':input[name="exposed_form_options[bef][' . $label . '][bef_format]"]' => array(
'value' => 'bef_slider',
),
),
),
'#description' => $this
->t('The maximum allowed value for the jQuery range slider. It can be positive, negative, or zero and have up to 11 decimal places.'),
);
$bef_options[$label]['slider_options']['bef_slider_step'] = array(
'#type' => 'textfield',
'#title' => $this
->t('Step'),
'#default_value' => empty($this->options['bef'][$label]['slider_options']['bef_slider_step']) ? 1 : $this->options['bef'][$label]['slider_options']['bef_slider_step'],
'#bef_filter_id' => $label,
'#states' => array(
'required' => array(
':input[name="exposed_form_options[bef][' . $label . '][bef_format]"]' => array(
'value' => 'bef_slider',
),
),
),
'#description' => $this
->t('Determines the size or amount of each interval or step the slider takes between the min and max.') . '<br />' . $this
->t('The full specified value range of the slider (Range maximum - Range minimum) must be evenly divisible by the step.') . '<br />' . $this
->t('The step must be a positive number of up to 5 decimal places.'),
);
$bef_options[$label]['slider_options']['bef_slider_animate'] = array(
'#type' => 'textfield',
'#title' => $this
->t('Animate'),
'#default_value' => $this->options['bef'][$label]['slider_options']['bef_slider_animate'],
'#bef_filter_id' => $label,
'#description' => $this
->t('Whether to slide handle smoothly when user click outside handle on the bar. Allowed values are "slow", "normal", "fast" or the number of milliseconds to run the animation (e.g. 1000). If left blank, there will be no animation, the slider will just jump to the new value instantly.'),
);
$bef_options[$label]['slider_options']['bef_slider_orientation'] = array(
'#type' => 'select',
'#title' => $this
->t('Orientation'),
'#options' => array(
'horizontal' => $this
->t('Horizontal'),
'vertical' => $this
->t('Vertical'),
),
'#default_value' => $this->options['bef'][$label]['slider_options']['bef_slider_orientation'],
'#bef_filter_id' => $label,
'#states' => array(
'required' => array(
':input[name="exposed_form_options[bef][' . $label . '][bef_format]"]' => array(
'value' => 'bef_slider',
),
),
),
'#description' => $this
->t('The orientation of the jQuery range slider.'),
);
}
$bef_options[$label]['more_options'] = array(
'#type' => 'details',
'#title' => $this
->t('More options for @identifier', array(
'@identifier' => $identifier,
)),
);
if ($bef_standard) {
$bef_options[$label]['more_options']['bef_select_all_none'] = array(
'#type' => 'checkbox',
'#title' => $this
->t('Add select all/none links'),
'#default_value' => $existing[$label]['more_options']['bef_select_all_none'],
'#disabled' => !$filter->options['expose']['multiple'],
'#description' => $this
->t('Add a "Select All/None" link when rendering the exposed filter using
checkboxes. If this option is disabled, edit the filter and check the
"Allow multiple selections".'),
);
if ($bef_nested) {
$bef_options[$label]['more_options']['bef_select_all_none_nested'] = array(
'#type' => 'checkbox',
'#title' => $this
->t('Add nested all/none selection'),
'#default_value' => $this->options['bef'][$label]['more_options']['bef_select_all_none_nested'],
'#disabled' => !$filter->options['expose']['multiple'] || !$filter->options['hierarchy'],
'#description' => $this
->t('When a parent checkbox is checked, check all its children. If this option
is disabled, edit the filter and check "Allow multiple selections" and
edit the filter settings and check "Show hierarchy in dropdown".'),
);
}
$bef_options[$label]['more_options']['bef_collapsible'] = array(
'#type' => 'checkbox',
'#title' => $this
->t('Make this filter collapsible'),
'#default_value' => $existing[$label]['more_options']['bef_collapsible'],
'#description' => $this
->t('Puts this filter in a collapsible details element'),
);
}
$bef_options[$label]['more_options']['is_secondary'] = array(
'#type' => 'checkbox',
'#title' => $this
->t('This is a secondary option'),
'#default_value' => $existing[$label]['more_options']['is_secondary'],
'#states' => array(
'visible' => array(
':input[name="exposed_form_options[bef][general][allow_secondary]"]' => array(
'checked' => TRUE,
),
),
),
'#description' => $this
->t('Places this element in the secondary options portion of the exposed form.'),
);
$filter_form = array();
$form_state = new FormState();
$form_state
->set('exposed', TRUE);
$filter
->buildExposedForm($filter_form, $form_state);
$supported_types = array(
'entity_autocomplete',
'textfield',
);
$filter_id = $filter->options['expose']['identifier'];
if (isset($filter_form[$filter_id]['#type']) && in_array($filter_form[$filter_id]['#type'], $supported_types) || isset($filter_form[$filter_id]['value']) && in_array($filter_form[$filter_id]['value']['#type'], $supported_types)) {
$bef_options[$label]['more_options']['placeholder_text'] = [
'#type' => 'textfield',
'#title' => $this
->t('Placeholder text'),
'#description' => $this
->t('Text to be shown in the text field until it is edited. Leave blank for no placeholder to be set.'),
'#default_value' => $existing[$label]['more_options']['placeholder_text'],
];
}
if (!$filter instanceof StringFilter && !$filter instanceof NumericFilter) {
$bef_options[$label]['more_options']['rewrite'] = array(
'#title' => $this
->t('Rewrite filter options'),
'#type' => 'details',
);
$bef_options[$label]['more_options']['rewrite']['filter_rewrite_values'] = array(
'#type' => 'textarea',
'#title' => $this
->t('Rewrite the text displayed'),
'#default_value' => $existing[$label]['more_options']['rewrite']['filter_rewrite_values'],
'#description' => $this
->t('Use this field to rewrite the filter options displayed. Use the format of current_text|replacement_text, one replacement per line. For example: <pre>
Current|Replacement
On|Yes
Off|No
</pre> Leave the replacement text blank to remove an option altogether. If using hierarchical taxonomy filters, do not including leading hyphens in the current text.
'),
);
}
}
$form['bef'] = $bef_options;
}
public function validateOptionsForm(&$form, FormStateInterface $form_state) {
$bef = $form_state
->getValue([
'exposed_form_options',
'bef',
]);
foreach ($bef as $id => $settings) {
if ($id == 'general' || $settings['bef_format'] != 'bef_slider') {
continue;
}
$min = $settings['slider_options']['bef_slider_min'];
$max = $settings['slider_options']['bef_slider_max'];
if ($max <= $min) {
$form_state
->setError($form['bef'][$id], $this
->t('The slider max value must be greater than the slider min value.'));
}
$animate = $settings['slider_options']['bef_slider_animate'];
if ($animate != '') {
if ((!is_numeric($animate) || intval($animate) != $animate) && !in_array($animate, [
'slow',
'normal',
'fast',
])) {
$form_state
->setError($form['bef'][$id], $this
->t('The slider animation option for %name must be "slow", "normal", or "fast", the number of milliseconds to run the animation (e.g. 1000), or blank.', [
'%name' => $id,
]));
}
}
$step = $settings['slider_options']['bef_slider_step'];
if ($step <= 0) {
$form_state
->setError($form['bef'][$id], $this
->t('The slider step option for %name must be a positive number.', [
'%name' => $id,
]));
}
if (strlen(substr(strrchr((string) $step, '.'), 1)) > 5) {
$form_state
->setError($form['bef'][$id], $this
->t('The slider step option for %name cannot have more than 5 decimal places.', [
'%name' => $id,
]));
}
if (strpos((string) ($max - $min) / $step, '.')) {
$form_state
->setError($form['bef'][$id], $this
->t('The slider range for %name must be evenly divisible by the step option.', [
'%name' => $id,
]));
}
}
parent::validateOptionsForm($form, $form_state);
}
public function exposedFormAlter(&$form, FormStateInterface $form_state) {
parent::exposedFormAlter($form, $form_state);
$show_apply = FALSE;
$form['#attached']['library'][] = 'better_exposed_filters/general';
$form['#attributes']['class'][] = 'bef-exposed-form';
$bef_add_js = FALSE;
$bef_js = array(
'datepicker' => FALSE,
'slider' => FALSE,
'settings' => array(),
);
$settings = $this
->getSettings();
\Drupal::moduleHandler()
->alter('better_exposed_filters_settings', $settings, $this->view, $this->displayHandler);
if ($allow_secondary = $settings['general']['allow_secondary']) {
$secondary = array(
'#type' => 'details',
'#title' => $settings['general']['secondary_label'],
);
}
if (!empty($settings['general']['autosubmit'])) {
$form = array_merge_recursive($form, array(
'#attributes' => array(
'data-bef-auto-submit-full-form' => '',
),
));
$form['actions']['submit']['#attributes']['data-bef-auto-submit-click'] = '';
$form['#attached']['library'][] = 'better_exposed_filters/auto_submit';
$form['#attached']['drupalSettings']['data']['better_exposed_filters'] = [
'autosubmitTextfieldDelay' => $settings['general']['autosubmit_textfield_delay'],
];
if (!empty($settings['general']['autosubmit_exclude_textfield'])) {
foreach ($form as &$element) {
if (isset($element['#type']) && $element['#type'] == 'textfield') {
$element['#attributes']['data-bef-auto-submit-exclude'] = '';
}
}
}
if (!empty($settings['general']['autosubmit_hide'])) {
$form['actions']['submit']['#attributes']['class'][] = 'js-bef-hide';
}
}
if (isset($settings['sort']) && !empty($form['sort_by'])) {
$show_apply = TRUE;
$collapse = $settings['sort']['advanced']['collapsible'] && !empty($settings['sort']['advanced']['collapsible_label']);
$sort_elems = array();
if ($settings['sort']['advanced']['combine'] && !empty($form['sort_order'])) {
$options = [];
if ($settings['sort']['advanced']['reset']) {
$options[' '] = $this
->t($settings['sort']['advanced']['reset_label']);
}
else {
$form['sort_bef_combine']['#default_value'] = '';
}
$selected = '';
foreach ($form['sort_by']['#options'] as $by_key => $by_val) {
foreach ($form['sort_order']['#options'] as $order_key => $order_val) {
$options["{$by_key} {$order_key}"] = "{$by_val} {$order_val}";
if ($form['sort_order']['#default_value'] == $order_key && empty($selected)) {
$selected = "{$by_key} {$order_key}";
}
}
}
if (!empty($settings['sort']['advanced']['combine_rewrite'])) {
$options = $this
->rewriteOptions($options, $settings['sort']['advanced']['combine_rewrite'], TRUE);
if (!isset($options[$selected])) {
$selected = NULL;
}
}
$form['sort_bef_combine'] = array(
'#options' => $options,
'#default_value' => $selected,
'#title' => $form['sort_by']['#title'],
);
switch ($settings['sort']['bef_format']) {
case 'bef':
$form['sort_bef_combine']['#type'] = 'radios';
$form['sort_bef_combine']['#theme'] = 'bef_radios';
break;
case 'bef_links':
$form['sort_bef_combine']['#theme'] = 'bef_links';
$form['sort_bef_combine']['#bef_path'] = $this
->getExposedFormActionUrl();
break;
case 'default':
$form['sort_bef_combine']['#type'] = 'select';
break;
}
$form['#submit'][] = 'bef_sort_combine_submit';
$form['#info']['sort-sort_bef_combine'] = array(
'value' => 'sort_bef_combine',
);
unset($form['sort_by']);
unset($form['sort_order']);
if ($collapse) {
$sort_elems[] = 'sort_bef_combine';
}
}
else {
if ('bef' == $settings['sort']['bef_format']) {
foreach ([
'sort_by',
'sort_order',
] as $field) {
if (!empty($form[$field])) {
$form[$field]['#theme'] = 'bef_radios';
$form[$field]['#type'] = 'radios';
if (empty($form[$field]['#process'])) {
$form[$field]['#process'] = array();
}
$form[$field]['#process'][] = [
'\\Drupal\\Core\\Render\\Element\\Radios',
'processRadios',
];
}
}
}
elseif ('bef_links' == $settings['sort']['bef_format']) {
$form['sort_by']['#theme'] = 'bef_links';
if (!empty($form['sort_order'])) {
$form['sort_order']['#theme'] = 'bef_links';
}
$form['sort_by']['#bef_path'] = $this
->getExposedFormActionUrl();
if (!empty($form['sort_order'])) {
$form['sort_order']['#bef_path'] = $this
->getExposedFormActionUrl();
}
}
if ($collapse) {
$sort_elems[] = 'sort_by';
if (!empty($form['sort_order'])) {
$sort_elems[] = 'sort_order';
}
}
if ($settings['sort']['advanced']['reset']) {
array_unshift($form['sort_by']['#options'], $settings['sort']['advanced']['reset_label']);
}
}
if ($collapse) {
$form['bef_sort_options'] = array(
'#type' => 'details',
'#title' => $settings['sort']['advanced']['collapsible_label'],
);
foreach ($sort_elems as $elem) {
$form['bef_sort_options'][$elem] = $form[$elem];
unset($form[$elem]);
}
}
if ($allow_secondary && $settings['sort']['advanced']['is_secondary']) {
foreach (array(
'sort_bef_combine',
'sort_by',
'sort_order',
) as $elem) {
if (!empty($form[$elem])) {
$secondary[$elem] = $form[$elem];
unset($form[$elem]);
}
}
}
}
if (isset($settings['pager'])) {
$show_apply = TRUE;
switch ($settings['pager']['bef_format']) {
case 'bef':
$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) {
$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']));
$form['items_per_page']['#bef_path'] = $this
->getExposedFormActionUrl();
}
break;
}
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]);
}
}
}
}
$filters = $form_state
->get('view')->display_handler->handlers['filter'];
foreach ($settings as $label => $options) {
if (empty($filters[$label]) || !$filters[$label]
->isExposed()) {
continue;
}
$field_id = $filters[$label]->options['expose']['identifier'];
if (!empty($settings[$label]['more_options']['placeholder_text'])) {
$form[$field_id]['#placeholder'] = $settings[$label]['more_options']['placeholder_text'];
}
if (!empty($options['more_options']['rewrite']['filter_rewrite_values'])) {
$form[$field_id]['#options'] = $this
->rewriteOptions($form[$field_id]['#options'], $options['more_options']['rewrite']['filter_rewrite_values']);
if (isset($selected) && !isset($form[$field_id]['#options'][$selected])) {
$form[$field_id]['#default_value'] = NULL;
}
}
if (!isset($options['bef_format'])) {
$options['bef_format'] = '';
}
$requires_options = array(
'bef',
'bef_ul',
'bef_links',
'bef_ul_links',
'bef_hidden',
);
if (in_array($options['bef_format'], $requires_options)) {
if (empty($form[$field_id]['#options'])) {
$options['bef_format'] = 'default';
}
else {
$form[$field_id]['#options'] = $this
->cleanOptions($form[$field_id]['#options']);
}
}
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[$field_id]['value']['#type']) && 'date_text' == $form[$field_id]['value']['#type'] || isset($form[$field_id]['min']) && isset($form[$field_id]['max']) && 'date_text' == $form[$field_id]['min']['#type'] && 'date_text' == $form[$field_id]['max']['#type']) {
$convert = array(
'd' => 'dd',
'D' => 'D',
'j' => 'd',
'l' => 'DD',
'z' => 'o',
'F' => 'MM',
'm' => 'mm',
'M' => 'M',
'n' => 'm',
'Y' => 'yy',
'y' => 'y',
);
$format = '';
if (isset($form[$field_id]['value'])) {
$format = $form[$field_id]['value']['#date_format'];
$form[$field_id]['value']['#attributes']['class'][] = 'bef-datepicker';
}
else {
$format = $form[$field_id]['min']['#date_format'];
$form[$field_id]['min']['#attributes']['class'][] = 'bef-datepicker';
$form[$field_id]['max']['#attributes']['class'][] = 'bef-datepicker';
}
$bef_js['datepicker_options']['dateformat'] = str_replace(array_keys($convert), array_values($convert), $format);
}
else {
$fields = array(
'min',
'max',
'value',
);
if (count(array_intersect($fields, array_keys($form[$field_id])))) {
foreach ($fields as $field) {
if (isset($form[$field_id][$field])) {
$form[$field_id][$field]['#attributes']['class'][] = 'bef-datepicker';
}
}
}
else {
$form[$field_id]['#attributes']['class'][] = 'bef-datepicker';
}
}
break;
case 'bef_slider':
$show_apply = TRUE;
$form[$field_id]['#attached']['library'][] = 'core/jquery.ui.slider';
$form[$field_id]['#attached']['library'][] = 'better_exposed_filters/sliders';
$form[$field_id]['#attached']['drupalSettings']['better_exposed_filters']['slider'] = TRUE;
$form[$field_id]['#attached']['drupalSettings']['better_exposed_filters']['slider_options'][$field_id] = [
'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' => Html::getUniqueId($field_id),
'viewId' => $form['#id'],
];
break;
case 'bef_links':
case 'bef_ul_links':
if ($options['bef_format'] == 'bef_ul_links') {
$form[$field_id]['#bef_nested'] = TRUE;
}
$form[$field_id]['#theme'] = 'bef_links';
$form[$field_id]['#bef_path'] = $this
->getExposedFormActionUrl();
break;
case 'bef_single':
$show_apply = TRUE;
$form[$field_id]['#title'] = $filters[$label]->options['expose']['label'];
$form[$field_id]['#type'] = 'checkbox';
if (empty($signal)) {
for ($signal = 'a'; isset($form[$signal]); $signal++) {
}
$form[$signal]['#type'] = 'hidden';
}
$input = $form_state
->getUserInput();
$value = \Drupal::request()->query
->get($field_id);
$checked = isset($input[$signal]) ? isset($value) : $form[$field_id]['#default_value'];
if ($checked) {
if (!in_array($value, array_keys($form[$field_id]['#options']))) {
$checked = FALSE;
}
}
$form[$field_id]['#value'] = $checked ? $value : 0;
break;
case 'bef':
case 'bef_ul':
if ($options['bef_format'] == 'bef_ul') {
$form[$field_id]['#bef_nested'] = TRUE;
}
$show_apply = TRUE;
$form[$field_id]['#options'] = $this
->cleanOptions($form[$field_id]['#options']);
if (empty($form[$field_id]['#multiple'])) {
$form[$field_id]['#type'] = 'radios';
$form[$field_id]['#theme'] = 'bef_radios';
if (empty($form[$field_id]['#process'])) {
$form[$field_id]['#process'] = array();
}
$form[$field_id]['#process'][] = [
'\\Drupal\\Core\\Render\\Element\\Radios',
'processRadios',
];
}
else {
$form[$field_id]['#type'] = 'checkboxes';
$form[$field_id]['#theme'] = 'bef_checkboxes';
if ($options['more_options']['bef_select_all_none'] || $options['more_options']['bef_select_all_none_nested']) {
$form[$field_id]['#attached']['library'][] = 'better_exposed_filters/select_all_none';
$form[$field_id]['#bef_select_all_none'] = $options['more_options']['bef_select_all_none'];
$form[$field_id]['#bef_select_all_none_nested'] = $options['more_options']['bef_select_all_none_nested'];
}
}
break;
case 'bef_hidden':
if (empty($form[$field_id]['#multiple'])) {
$form[$field_id]['#type'] = 'hidden';
}
else {
$form['#info']["filter-{$label}"]['label'] = '';
$form[$field_id]['#theme'] = 'bef_hidden';
}
break;
default:
$show_apply = TRUE;
break;
}
if (!empty($options['more_options']['bef_collapsible'])) {
$form[$field_id]['#theme_wrappers'] = [
'details' => [
'#title' => $form['#info']["filter-{$label}"]['label'] ?: '',
'#description' => $form['#info']["filter-{$label}"]['description'] ?: '',
'#attributes' => [
'class' => [
'form-item',
],
],
'#value' => NULL,
],
];
$form['#info']["filter-{$label}"]['label'] = '';
$form['#info']["filter-{$label}"]['description'] = '';
if (isset($this->view
->getHandlers('filter')[$label]) && $this->view
->getHandlers('filter')[$label]['expose']['use_operator']) {
$operator_id = $this->view
->getHandlers('filter')[$label]['expose']['operator_id'];
$form[$field_id][$operator_id] = $form[$operator_id];
unset($form[$operator_id]);
}
}
if ($allow_secondary && $settings[$label]['more_options']['is_secondary']) {
if ($filters[$label]->options['is_grouped']) {
$identifier = $filters[$label]->options['group_info']['identifier'];
$filter_info_name = "filter-{$identifier}";
}
else {
$identifier = $filters[$label]->options['expose']['identifier'];
$filter_info_name = "filter-{$label}";
}
if (!empty($form[$identifier])) {
if (!empty($this->view
->getHandlers('filter')[$label]['expose']['use_operator'])) {
$op_id = $this->view
->getHandlers('filter')[$label]['expose']['operator_id'];
$secondary[$op_id] = $form[$op_id];
unset($form[$op_id]);
}
$secondary[$identifier] = $form[$identifier];
unset($form[$identifier]);
$secondary[$identifier]['#title'] = $form['#info'][$filter_info_name]['label'];
$secondary[$identifier]['#description'] = $form['#info'][$filter_info_name]['description'];
unset($form['#info'][$filter_info_name]);
}
}
}
$form['submit']['#access'] = $show_apply;
$form['reset']['#access'] = $show_apply;
if ($bef_add_js) {
if ($bef_js['datepicker']) {
$form['#attached']['library'][] = 'core/jquery.ui.datepicker';
$form['#attached']['library'][] = 'better_exposed_filters/datepickers';
}
$form['#attached']['drupalSettings']['better_exposed_filters'] = $bef_js;
}
if ($allow_secondary && !empty($secondary)) {
foreach (Element::children($form) as $index => $child) {
$form[$child]['#weight'] = $index;
}
$form['secondary'] = $secondary;
$form['secondary']['#weight'] = count($form['#info']) - 1;
$form['#info']['filter-secondary']['value'] = 'secondary';
}
}
protected function exposedFilterApplied() {
if (!empty($this->options['input_required'])) {
return parent::exposedFilterApplied();
}
else {
return TRUE;
}
}
protected function rewriteOptions(array $options, $rewriteSettings, $reorder = FALSE) {
if (empty($rewriteSettings) || !is_string($rewriteSettings)) {
return $options;
}
$rewrites = [];
$order = [];
if (!$reorder) {
$order = array_keys($options);
}
$lines = preg_split("/(\r\n|\n|\r)/", trim($rewriteSettings));
foreach ($lines as $line) {
list($search, $replace) = explode('|', $line);
if (!empty($search)) {
$rewrites[$search] = trim($replace);
if ($reorder) {
$key = array_search($search, $options);
if ($key !== FALSE) {
$order[] = $key;
}
}
}
}
$return = [];
if ($reorder && !empty($order)) {
foreach ($order as $key) {
$return[$key] = $options[$key];
unset($options[$key]);
}
$return += $options;
}
else {
$return = $options;
}
foreach ($return as $index => $option) {
if ($option instanceof \stdClass && isset($option->option) && is_array($option->option)) {
$option = array_values($option->option)[0];
}
else {
if (!is_string($option)) {
$option = (string) $option;
}
}
if (!is_string($option)) {
continue;
}
if (isset($rewrites[$option])) {
if ('' == $rewrites[$option]) {
unset($return[$index]);
}
else {
if ($return[$index] instanceof \stdClass) {
$tid = key($return[$index]->option);
$text = current($return[$index]->option);
$return[$index]->option[$tid] = $rewrites[$text];
}
else {
$return[$index] = $rewrites[$option];
}
}
}
}
return $return;
}
protected function cleanOptions(array $options) {
if (is_scalar(reset($options))) {
return $options;
}
$clean = [];
foreach ($options as $index => $value) {
if (is_object($value) && !is_a($value, 'Drupal\\Core\\StringTranslation\\TranslatableMarkup')) {
reset($value->option);
$key = key($value->option);
$val = current($value->option);
$clean[$key] = $val;
}
else {
$clean[$index] = $value;
}
}
return $clean;
}
protected function setDefaults() {
$count = func_num_args();
if (!$count) {
return;
}
elseif (1 == $count) {
return func_get_arg(0);
}
$params = func_get_args();
$return = array_shift($params);
foreach ($params as $array) {
foreach ($array as $key => $value) {
if (is_numeric($key) && !in_array($value, $return)) {
if (is_array($value)) {
$return[] = $this
->setDefaults($return[$key], $value);
}
else {
$return[] = $value;
}
}
else {
if (isset($return[$key]) && is_array($value) && is_array($return[$key])) {
$return[$key] = $this
->setDefaults($return[$key], $value);
}
else {
$return[$key] = $value;
}
}
}
}
return $return;
}
protected function updateLegacySettings($settings) {
if (isset($settings['sort']['collapsible'])) {
$settings['sort']['advanced']['collapsible'] = $settings['sort']['collapsible'];
unset($settings['sort']['collapsible']);
}
if (isset($settings['sort']['collapsible_label'])) {
$settings['sort']['advanced']['collapsible_label'] = $settings['sort']['collapsible_label'];
unset($settings['sort']['collapsible_label']);
}
if (isset($settings['sort']['combine'])) {
$settings['sort']['advanced']['combine'] = $settings['sort']['combine'];
unset($settings['sort']['combine']);
}
if (isset($settings['sort']['reset'])) {
$settings['sort']['advanced']['reset'] = $settings['sort']['reset'];
unset($settings['sort']['reset']);
}
if (isset($settings['sort']['reset_label'])) {
$settings['sort']['advanced']['reset_label'] = $settings['sort']['reset_label'];
unset($settings['sort']['reset_label']);
}
return $settings;
}
protected function getSettings() {
$defaults = array(
'general' => array(
'allow_secondary' => FALSE,
'secondary_label' => $this
->t('Advanced options'),
'autosubmit' => FALSE,
'autosubmit_exclude_textfield' => FALSE,
'autosubmit_textfield_delay' => 500,
'autosubmit_hide' => FALSE,
),
'sort' => array(
'bef_format' => 'default',
'advanced' => array(
'collapsible' => FALSE,
'collapsible_label' => '',
'combine' => FALSE,
'combine_rewrite' => '',
'reset' => FALSE,
'reset_label' => '',
'is_secondary' => FALSE,
),
),
'pager' => array(
'bef_format' => 'default',
'is_secondary' => FALSE,
),
);
$current = [];
if (isset($this->options['bef'])) {
$current = $this
->updateLegacySettings($this->options['bef']);
}
$settings = $this
->setDefaults($defaults, $current);
$filter_defaults = array(
'bef_format' => 'default',
'more_options' => array(
'placeholder_text' => '',
'bef_select_all_none' => FALSE,
'bef_select_all_none_nested' => FALSE,
'bef_collapsible' => FALSE,
'is_secondary' => FALSE,
'rewrite' => array(
'filter_rewrite_values' => '',
),
),
'slider_options' => array(
'bef_slider_min' => 0,
'bef_slider_max' => 99999,
'bef_slider_step' => 1,
'bef_slider_animate' => '',
'bef_slider_orientation' => 'horizontal',
),
);
foreach ($this->view->display_handler
->getHandlers('filter') as $label => $filter) {
if (!$filter
->isExposed()) {
continue;
}
if (!isset($this->options['bef'][$label])) {
$settings[$label] = $filter_defaults;
}
else {
$settings[$label] = $this
->setDefaults($filter_defaults, $this->options['bef'][$label]);
}
}
return $settings;
}
protected function getExposedFormActionUrl() {
if ($this->displayHandler
->getRoutedDisplay()) {
return $this->displayHandler
->getUrl();
}
$request = \Drupal::request();
$url = Url::createFromRequest($request);
$url
->setAbsolute();
return $url;
}
}