You are here

facetapi_multiselect.module in Facetapi Multiselect 7

Displays search facets as a multiselect widget.

File

facetapi_multiselect.module
View source
<?php

/**
 * @file
 * Displays search facets as a multiselect widget.
 */

/**
 * Implements hook_facetapi_widgets().
 */
function facetapi_multiselect_facetapi_widgets() {
  return array(
    'facetapi_multiselect' => array(
      'handler' => array(
        'label' => t('Multiselect element'),
        'class' => 'FacetapiMultiSelectWidget',
        'query types' => array(
          'term',
          'date',
        ),
      ),
    ),
  );
}

/**
 * Implements hook_forms().
 */
function facetapi_multiselect_forms($form_id) {

  // Build all multiselect facet forms on the page using the same form
  // callback.
  $forms = array();
  if (strpos($form_id, 'facetapi_multiselect_form_') === 0) {
    $forms[$form_id]['callback'] = 'facetapi_multiselect_form';
  }
  return $forms;
}

/**
 * Form which displays facets as a multiselect element.
 *
 * @param $widget
 *   The widget object (e.g. of class FacetapiMultiSelectWidget) used to build
 *   the facet.
 * @param $element
 *   The facet element, containing information about each available facet in
 *   the group.
 */
function facetapi_multiselect_form($form, &$form_state, $widget, $element) {

  // Record the widget and facet name in an easy-to-access part of the
  // $form_state array. Also record a class that can be used to access the
  // widget in front-end code.
  $facet_name = $widget
    ->getKey();
  $form_state['facetapi_multiselect'] = array(
    'widget' => $widget,
    'facet_name' => $facet_name,
    'facet_class' => drupal_html_class("facetapi-multiselect-{$facet_name}"),
  );

  // Record this in the form too, in case someone needs to access it there.
  $form['#facetapi_multiselect'] = $form_state['facetapi_multiselect'];

  // Get an array of the facet options
  $options = $widget
    ->buildOptions($element);

  // Because of an issue with iOS and multiselect fields, we need to add an
  // empty option. This option must have the `disabled` attribute, so  we'll
  // handle that in preprocess.
  // @see http://stackoverflow.com/questions/34985606/ios-9-2-select-list-multiple-incorrectly-firing-change-event-and-setting-valu
  $options = array(
    'disabled' => '',
  ) + $options;

  // Build the form.
  $form['facets'] = array(
    '#type' => 'select',
    '#multiple' => TRUE,
    '#options' => $options,
    '#default_value' => $widget
      ->buildDefaultValue($element),
    '#attributes' => array(
      'class' => array(
        'facetapi-multiselect',
        $form_state['facetapi_multiselect']['facet_class'],
      ),
    ),
    '#post_render' => array(
      '_facetapi_multiselect_disable_options',
    ),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  $build = $widget
    ->getBuild();
  $settings = $build['#settings']->settings;
  if ($settings['auto_submit']) {
    $form['#attached']['js'][] = drupal_get_path('module', 'facetapi_multiselect') . '/js/facetapi_multiselect_autosubmit.js';
  }
  if ($settings['placeholder_label']) {
    $form['facets']['#attributes']['data-placeholder'] = t($settings['placeholder_label']);
  }
  return $form;
}

/**
 * Set any option items that have a "disabled" value to be disabled
 * @param  string $content The rendered HTML for the select item
 * @param  array $element The Drupal FAPI item
 * @return string          The updated markeup
 */
function _facetapi_multiselect_disable_options($content, $element) {

  // Check PHP and libxml version.
  static $new_libxml;
  if (!isset($new_libxml)) {
    $new_libxml = version_compare(PHP_VERSION, '5.4', '>=') && defined('LIBXML_HTML_NOIMPLIED') && defined('LIBXML_HTML_NODEFDTD');
  }

  // Create a new DOMDocument object with our rendered HTML.
  $select = new DOMDocument();
  $select->encoding = 'utf-8';
  if ($new_libxml) {
    $select
      ->loadHTML(utf8_decode($content), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
  }
  else {
    $select
      ->loadHTML(utf8_decode($content));
  }

  // Loop over our options list
  foreach ($select
    ->getElementsByTagName('option') as $option) {
    if ($option
      ->hasAttribute('value')) {

      // Get the value and see if it's set to 'disabled'. If not, skip
      $value = $option
        ->getAttribute('value');
      if ($value == 'disabled' || strpos($value, ':disabled') !== FALSE) {

        // Set the disabled attribute, remove the value attribute.
        $option
          ->setAttribute('disabled', 'disabled');
        $option
          ->removeAttribute('value');
      }
    }
  }
  return $select
    ->saveHTML();
}

/**
 * Submit handler for facetapi_multiselect_form().
 */
function facetapi_multiselect_form_submit($form, &$form_state) {

  // Remove all the select element's #options from the current list of facets,
  // and add back only the ones that were submitted with the form.
  $exclude = array(
    'q',
    'page',
  );
  $query = drupal_get_query_parameters(NULL, $exclude);
  $original_f = isset($query['f']) ? $query['f'] : array();
  $query['f'] = array_diff($original_f, facetapi_multiple_get_options($form['facets']['#options']));
  foreach ($form_state['values']['facets'] as $facet) {
    if ($facet) {
      $query['f'][] = $facet;
    }
  }
  $query['f'] = array_values($query['f']);

  // Redirect to the new URL, with the new set of active facets.
  $form_state['redirect'] = array(
    current_path(),
    array(
      'query' => $query,
    ),
  );
}

/**
 * Returns an array of all available options from an #options element.
 *
 * This function takes into account optgroups, if they are present (the options
 * contained within those will be merged into the returned array).
 *
 * @param $options
 *   An #options array from a form element.
 *
 * @return
 *   An array containing all options stored within the array. For a flat
 *   #options array (without optgroups), calling this function is equivalent to
 *   calling array_keys().
 */
function facetapi_multiple_get_options($options) {
  $all_options = array();
  foreach ($options as $key => $option) {
    if (is_array($option)) {
      $all_options = array_merge($all_options, facetapi_multiple_get_options($option));
    }
    else {
      $all_options[] = $key;
    }
  }
  return $all_options;
}

Functions

Namesort descending Description
facetapi_multiple_get_options Returns an array of all available options from an #options element.
facetapi_multiselect_facetapi_widgets Implements hook_facetapi_widgets().
facetapi_multiselect_form Form which displays facets as a multiselect element.
facetapi_multiselect_forms Implements hook_forms().
facetapi_multiselect_form_submit Submit handler for facetapi_multiselect_form().
_facetapi_multiselect_disable_options Set any option items that have a "disabled" value to be disabled