You are here

public function BetterExposedFilters::buildOptionsForm in Better Exposed Filters 8.4

Same name and namespace in other branches
  1. 8.5 src/Plugin/views/exposed_form/BetterExposedFilters.php \Drupal\better_exposed_filters\Plugin\views\exposed_form\BetterExposedFilters::buildOptionsForm()
  2. 8.3 src/Plugin/views/exposed_form/BetterExposedFilters.php \Drupal\better_exposed_filters\Plugin\views\exposed_form\BetterExposedFilters::buildOptionsForm()

Build the views options form and adds custom options for BEF.

@inheritDoc

Overrides InputRequired::buildOptionsForm

File

src/Plugin/views/exposed_form/BetterExposedFilters.php, line 189

Class

BetterExposedFilters
Exposed form plugin that provides a basic exposed form.

Namespace

Drupal\better_exposed_filters\Plugin\views\exposed_form

Code

public function buildOptionsForm(&$form, FormStateInterface $form_state) {

  // Ensure that the form values are stored in their original location, and
  // not dependent on their position in the form tree. We are moving around
  // a few elements to make the UI more user friendly.
  $original_form = [];
  parent::buildOptionsForm($original_form, $form_state);
  foreach (Element::children($original_form) as $element) {
    $original_form[$element]['#parents'] = [
      'exposed_form_options',
      $element,
    ];
  }

  // Save shorthand for BEF options.
  $bef_options = $this->options['bef'];

  // User raw user input for AJAX callbacks.
  $user_input = $form_state
    ->getUserInput();
  $bef_input = $user_input['exposed_form_options']['bef'] ?? NULL;

  /*
   * General BEF settings
   */

  // Reorder some existing form elements.
  $form['bef']['general']['submit_button'] = $original_form['submit_button'];
  $form['bef']['general']['reset_button'] = $original_form['reset_button'];
  $form['bef']['general']['reset_button_label'] = $original_form['reset_button_label'];

  // Add the 'auto-submit' functionality.
  $form['bef']['general']['autosubmit'] = [
    '#type' => 'checkbox',
    '#title' => $this
      ->t('Enable auto-submit'),
    '#description' => $this
      ->t('Automatically submits the form when an element has changed.'),
    '#default_value' => $bef_options['general']['autosubmit'],
  ];
  $form['bef']['general']['autosubmit_exclude_textfield'] = [
    '#type' => 'checkbox',
    '#title' => $this
      ->t('Exclude Textfield'),
    '#description' => $this
      ->t('Exclude textfields from auto-submit. User will have to press enter key, or click submit button.'),
    '#default_value' => $bef_options['general']['autosubmit_exclude_textfield'],
    '#states' => [
      'visible' => [
        ':input[name="exposed_form_options[bef][general][autosubmit]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];
  $form['bef']['general']['autosubmit_textfield_delay'] = [
    '#type' => 'number',
    '#title' => $this
      ->t('Delay for textfield autosubmit'),
    '#description' => $this
      ->t('Configure a delay in ms before triggering autosubmit on textfields.'),
    '#default_value' => $bef_options['general']['autosubmit_textfield_delay'],
    '#min' => 0,
    '#states' => [
      'visible' => [
        ':input[name="exposed_form_options[bef][general][autosubmit]"]' => [
          'checked' => TRUE,
        ],
        ':input[name="exposed_form_options[bef][general][autosubmit_exclude_textfield]"]' => [
          'checked' => FALSE,
        ],
      ],
    ],
  ];
  $form['bef']['general']['autosubmit_hide'] = [
    '#type' => 'checkbox',
    '#title' => $this
      ->t('Hide submit button'),
    '#description' => $this
      ->t('Hides submit button if auto-submit and javascript are enabled.'),
    '#default_value' => $bef_options['general']['autosubmit_hide'],
    '#states' => [
      'visible' => [
        ':input[name="exposed_form_options[bef][general][autosubmit]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];

  // Insert a checkbox to make the input required optional just before the
  // input required text field. Only show the text field if the input required
  // option is selected.
  $form['bef']['general']['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' => $bef_options['general']['input_required'],
  ];
  $original_form['text_input_required'] += [
    '#states' => [
      'visible' => [
        'input[name="exposed_form_options[bef][general][input_required]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];
  $form['bef']['general']['text_input_required'] = $original_form['text_input_required'];

  /*
   * Allow exposed form items to be displayed as secondary options.
   */
  $form['bef']['general']['allow_secondary'] = [
    '#type' => 'checkbox',
    '#title' => $this
      ->t('Enable secondary exposed form options'),
    '#default_value' => $bef_options['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.'),
  ];
  $form['bef']['general']['secondary_label'] = [
    '#type' => 'textfield',
    '#default_value' => $bef_options['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' => [
      'required' => [
        ':input[name="exposed_form_options[bef][general][allow_secondary]"]' => [
          'checked' => TRUE,
        ],
      ],
      'visible' => [
        ':input[name="exposed_form_options[bef][general][allow_secondary]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];
  $form['bef']['general']['secondary_open'] = [
    '#type' => 'checkbox',
    '#default_value' => $bef_options['general']['secondary_open'],
    '#title' => $this
      ->t('Secondary option open by default'),
    '#description' => $this
      ->t('Indicates whether the details element should be open by default.'),
    '#states' => [
      'visible' => [
        ':input[name="exposed_form_options[bef][general][allow_secondary]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];

  /*
   * Add options for exposed sorts.
   */

  // Add intro explaining BEF sorts.
  $documentation_uri = Url::fromUri('http://drupal.org/node/1701012')
    ->toString();
  $form['bef']['sort']['bef_intro'] = [
    '#markup' => '<h3>' . $this
      ->t('Exposed Sort Settings') . '</h3><p>' . $this
      ->t('This section lets you select additional options for exposed sorts. 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.', [
      ':link' => $documentation_uri,
    ]) . '</p>',
  ];

  // Iterate over each sort and determine if any sorts are exposed.
  $is_sort_exposed = FALSE;

  /** @var \Drupal\views\Plugin\views\HandlerBase $sort */
  foreach ($this->view->display_handler
    ->getHandlers('sort') as $sort) {
    if ($sort
      ->isExposed()) {
      $is_sort_exposed = TRUE;
      break;
    }
  }
  $form['bef']['sort']['empty'] = [
    '#type' => 'item',
    '#description' => $this
      ->t('No sort elements have been exposed yet.'),
    '#access' => !$is_sort_exposed,
  ];
  if ($is_sort_exposed) {
    $options = [];
    foreach ($this->sortWidgetManager
      ->getDefinitions() as $plugin_id => $definition) {
      if ($definition['class']::isApplicable()) {
        $options[$plugin_id] = $definition['label'];
      }
    }
    $form['bef']['sort']['configuration'] = [
      '#prefix' => "<div id='bef-sort-configuration'>",
      '#suffix' => "</div>",
      '#type' => 'container',
    ];

    // Get selected plugin_id on AJAX callback directly from the form state.
    $selected_plugin_id = $bef_input['sort']['configuration']['plugin_id'] ?? $bef_options['sort']['plugin_id'];
    $form['bef']['sort']['configuration']['plugin_id'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('Display exposed sort options as'),
      '#default_value' => $selected_plugin_id,
      '#options' => $options,
      '#description' => $this
        ->t('Select a format for the exposed sort options.'),
      '#ajax' => [
        'event' => 'change',
        'effect' => 'fade',
        'progress' => 'throbber',
        // Since views options forms are complex, they're built by
        // Drupal in a different way. To bypass this problem we need to
        // provide the full path to the Ajax callback.
        'callback' => __CLASS__ . '::ajaxCallback',
        'wrapper' => 'bef-sort-configuration',
      ],
    ];

    // Move some existing form elements.
    $form['bef']['sort']['configuration']['exposed_sorts_label'] = $original_form['exposed_sorts_label'];
    $form['bef']['sort']['configuration']['expose_sort_order'] = $original_form['expose_sort_order'];
    $form['bef']['sort']['configuration']['sort_asc_label'] = $original_form['sort_asc_label'];
    $form['bef']['sort']['configuration']['sort_desc_label'] = $original_form['sort_desc_label'];
    if ($selected_plugin_id) {
      $plugin_configuration = $bef_options['sort'] ?? [];

      /** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */
      $plugin = $this->sortWidgetManager
        ->createInstance($selected_plugin_id, $plugin_configuration);
      $plugin
        ->setView($this->view);
      $subform =& $form['bef']['sort']['configuration'];
      $subform_state = SubformState::createForSubform($subform, $form, $form_state);
      $subform += $plugin
        ->buildConfigurationForm($subform, $subform_state);
    }
  }

  /*
   * Add options for exposed pager.
   */
  $documentation_uri = Url::fromUri('http://drupal.org/node/1701012')
    ->toString();
  $form['bef']['pager']['bef_intro'] = [
    '#markup' => '<h3>' . $this
      ->t('Exposed Pager Settings') . '</h3><p>' . $this
      ->t('This section lets you select additional options for exposed pagers. 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.', [
      ':link' => $documentation_uri,
    ]) . '</p>',
  ];
  $pager = $this->view
    ->getPager();
  $is_pager_exposed = $pager && $pager
    ->usesExposed();
  $form['bef']['pager']['empty'] = [
    '#type' => 'item',
    '#description' => $this
      ->t('No pager elements have been exposed yet.'),
    '#access' => !$is_pager_exposed,
  ];
  if ($is_pager_exposed) {
    $options = [];
    foreach ($this->pagerWidgetManager
      ->getDefinitions() as $plugin_id => $definition) {
      if ($definition['class']::isApplicable()) {
        $options[$plugin_id] = $definition['label'];
      }
    }
    $form['bef']['pager']['configuration'] = [
      '#prefix' => "<div id='bef-pager-configuration'>",
      '#suffix' => "</div>",
      '#type' => 'container',
    ];

    // Get selected plugin_id on AJAX callback directly from the form state.
    $selected_plugin_id = $bef_input['pager']['configuration']['plugin_id'] ?? $bef_options['pager']['plugin_id'];
    $form['bef']['pager']['configuration']['plugin_id'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('Display exposed pager options as'),
      '#default_value' => $selected_plugin_id,
      '#options' => $options,
      '#description' => $this
        ->t('Select a format for the exposed pager options.'),
      '#ajax' => [
        'event' => 'change',
        'effect' => 'fade',
        'progress' => 'throbber',
        // Since views options forms are complex, they're built by
        // Drupal in a different way. To bypass this problem we need to
        // provide the full path to the Ajax callback.
        'callback' => __CLASS__ . '::ajaxCallback',
        'wrapper' => 'bef-pager-configuration',
      ],
    ];
    if ($selected_plugin_id) {
      $plugin_configuration = $bef_options['pager'] ?? [];

      /** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */
      $plugin = $this->pagerWidgetManager
        ->createInstance($selected_plugin_id, $plugin_configuration);
      $plugin
        ->setView($this->view);
      $subform =& $form['bef']['pager']['configuration'];
      $subform_state = SubformState::createForSubform($subform, $form, $form_state);
      $subform += $plugin
        ->buildConfigurationForm($subform, $subform_state);
    }
  }

  /*
   * Add options for exposed filters.
   */
  $documentation_uri = Url::fromUri('http://drupal.org/node/1701012')
    ->toString();
  $form['bef']['filter']['bef_intro'] = [
    '#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.', [
      ':link' => $documentation_uri,
    ]) . '</p>',
  ];

  // Iterate over each filter and add BEF filter options.

  /** @var \Drupal\views\Plugin\views\HandlerBase $filter */
  foreach ($this->view->display_handler
    ->getHandlers('filter') as $filter_id => $filter) {
    if (!$filter
      ->isExposed()) {
      continue;
    }
    $options = [];
    foreach ($this->filterWidgetManager
      ->getDefinitions() as $plugin_id => $definition) {
      if ($definition['class']::isApplicable($filter, $this->displayHandler->handlers['filter'][$filter_id]->options)) {
        $options[$plugin_id] = $definition['label'];
      }
    }

    // Alter the list of available widgets for this filter.
    $this->moduleHandler
      ->alter('better_exposed_filters_display_options', $options, $filter);

    // Get a descriptive label for the filter.
    $label = $this
      ->t('Exposed filter @filter', [
      '@filter' => $filter->options['expose']['identifier'],
    ]);
    if (!empty($filter->options['expose']['label'])) {
      $label = $this
        ->t('Exposed filter "@filter" with label "@label"', [
        '@filter' => $filter->options['expose']['identifier'],
        '@label' => $filter->options['expose']['label'],
      ]);
    }
    $form['bef']['filter'][$filter_id] = [
      '#type' => 'details',
      '#title' => $label,
      '#collapsed' => FALSE,
      '#collapsible' => TRUE,
    ];
    $form['bef']['filter'][$filter_id]['configuration'] = [
      '#prefix' => "<div id='bef-filter-{$filter_id}-configuration'>",
      '#suffix' => "</div>",
      '#type' => 'container',
    ];

    // Get selected plugin_id on AJAX callback directly from the form state.
    $selected_plugin_id = $bef_input['filter'][$filter_id]['configuration']['plugin_id'] ?? $bef_options['filter'][$filter_id]['plugin_id'];
    $form['bef']['filter'][$filter_id]['configuration']['plugin_id'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('Exposed filter widget:'),
      '#default_value' => $selected_plugin_id,
      '#options' => $options,
      '#ajax' => [
        'event' => 'change',
        'effect' => 'fade',
        'progress' => 'throbber',
        // Since views options forms are complex, they're built by
        // Drupal in a different way. To bypass this problem we need to
        // provide the full path to the Ajax callback.
        'callback' => __CLASS__ . '::ajaxCallback',
        'wrapper' => 'bef-filter-' . $filter_id . '-configuration',
      ],
    ];
    if ($selected_plugin_id) {
      $plugin_configuration = $bef_options['filter'][$filter_id] ?? [];

      /** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */
      $plugin = $this->filterWidgetManager
        ->createInstance($selected_plugin_id, $plugin_configuration);
      $plugin
        ->setView($this->view);
      $plugin
        ->setViewsHandler($filter);
      $subform =& $form['bef']['filter'][$filter_id]['configuration'];
      $subform_state = SubformState::createForSubform($subform, $form, $form_state);
      $subform += $plugin
        ->buildConfigurationForm($subform, $subform_state);
    }
  }
}