You are here

class FacetapiWidgetLinks in Facet API 7

Same name and namespace in other branches
  1. 6.3 plugins/facetapi/widget_links.inc \FacetapiWidgetLinks
  2. 7.2 plugins/facetapi/widget_links.inc \FacetapiWidgetLinks

Widget that renders facets as a list of clickable links.

Links make it easy for users to narrow down their search results by clicking on them. The render arrays use theme_item_list() to generate the HTML markup.

Hierarchy

Expanded class hierarchy of FacetapiWidgetLinks

2 string references to 'FacetapiWidgetLinks'
facetapi_facetapi_widgets in ./facetapi.facetapi.inc
Implements hook_facetapi_widgets().
hook_facetapi_widgets in ./facetapi.api.php
Define all widget plugins provided by the module.

File

plugins/facetapi/widget_links.inc, line 14
The facetapi_links and facetapi_checkbox_links widget plugin classes.

View source
class FacetapiWidgetLinks extends FacetapiWidget {

  /**
   * Overrides FacetapiWidget::__construct().
   *
   * For links, it is better to use the machine name of the facet as opposed to
   * the alias for the key. Alias are usually, but not necessarily, unique. It
   * doesn't make sense to group links in the same element as they are usually
   * rendered in blocks with are separate from one another.
   */
  public function __construct($id, array $realm, FacetapiFacet $facet, stdClass $settings) {
    parent::__construct($id, $realm, $facet, $settings);
    $this->jsSettings['limit'] = $this->settings->settings['soft_limit'];
    $this->key = $facet['name'];
  }

  /**
   * Implements FacetapiWidget::execute().
   *
   * Transforms the render array into something that can be themed by
   * theme_item_list().
   *
   * @see FacetapiWidgetLinks::setThemeHooks()
   * @see FacetapiWidgetLinks::buildListItems()
   */
  public function execute() {
    $element =& $this->build[$this->facet['field alias']];

    // Sets each item's theme hook, builds item list.
    $this
      ->setThemeHooks($element);
    $element = array(
      '#theme' => 'item_list',
      '#items' => $this
        ->buildListItems($element),
      '#attributes' => $this->build['#attributes'],
    );
  }

  /**
   * Recursive function that sets each item's theme hook.
   *
   * The individual items will be rendered by different theme hooks depending on
   * whether or not they are active.
   *
   * @param array &$build
   *   A render array containing the facet items.
   *
   * @return FacetapiWidget
   *   An instance of this class.
   *
   * @see theme_facetapi_link_active()
   * @see theme_facetapi_link_inactive()
   */
  protected function setThemeHooks(array &$build) {
    foreach ($build as $value => &$item) {
      $item['#theme'] = $item['#active'] ? 'facetapi_link_active' : 'facetapi_link_inactive';
      if (!empty($item['#item_children'])) {
        $this
          ->setThemeHooks($item['#item_children']);
      }
    }
    return $this;
  }

  /**
   * Transforms the render array for use with theme_item_list().
   *
   * The recursion allows this function to act on the various levels of a
   * hierarchical data set.
   *
   * @param array $build
   *   The items in the facet's render array being transformed.
   *
   * @return array
   *   The "items" parameter for theme_item_list().
   */
  function buildListItems($build) {
    $settings = $this->settings->settings;
    $attributes_init = array(
      'class' => $this
        ->getItemClasses(),
    );

    // Builds rows.
    $items = array();
    foreach ($build as $value => $item) {

      // Reset attributes for each item, combining potential custom ones and the
      // default "class" attribute. The item's "#attributes" property can be
      // used to e.g. set rel="nofollow" for individual items.
      $attributes = isset($item['#attributes']) ? $item['#attributes'] + $attributes_init : $attributes_init;
      $row = array(
        'class' => array(),
      );

      // Allow adding classes via altering.
      if (isset($item['#class'])) {
        $attributes['class'] = array_merge($attributes['class'], $item['#class']);
      }

      // Initializes variables passed to theme hook.
      $variables = array(
        'text' => $item['#markup'],
        'path' => $item['#path'],
        'count' => $settings['display_count'] ? $item['#count'] : NULL,
        'options' => array(
          'attributes' => $attributes,
        ),
      );

      // Adds the facetapi-zero-results class to items that have no results.
      if (!$item['#count']) {
        $variables['options']['attributes']['class'][] = 'facetapi-zero-results';
      }
      else {

        // We need nofollow only for items with non zero results.
        if ($settings['nofollow']) {

          // Check if rel="nofollow" should be added to links.
          switch ($settings['nofollow']) {

            // Always add nofollow.
            case 1:
              $variables['options']['attributes']['rel'] = 'nofollow';
              break;

            // Add nofollow if there is already an active item of this facet.
            case 2:
              if ($this->facet
                ->getAdapter()
                ->getActiveItems(array(
                'name' => $this->key,
              ))) {
                $variables['options']['attributes']['rel'] = 'nofollow';
              }
              break;

            // Add nofollow if there is already an active item of any facet.
            case 3:
              if ($this->facet
                ->getAdapter()
                ->getAllActiveItems()) {
                $variables['options']['attributes']['rel'] = 'nofollow';
              }
              break;
          }
        }
      }

      // Add an ID to identify this link.
      $variables['options']['attributes']['id'] = drupal_html_id('facetapi-link');
      $variables['options'] += array(
        'html' => $item['#html'],
        'query' => $item['#query'],
      );

      // If the item is active, the li is active
      if ($item['#active']) {
        $row['class'][] = 'active';
      }

      // If the item has no children, it is a leaf.
      if (empty($item['#item_children'])) {
        $row['class'][] = 'leaf';
      }
      else {

        // If the item is active or the "show_expanded" setting is selected,
        // show this item as expanded so we see its children.
        if ($item['#active'] || !empty($settings['show_expanded'])) {
          $row['class'][] = 'expanded';
          $row['children'] = $this
            ->buildListItems($item['#item_children']);
        }
        else {
          $row['class'][] = 'collapsed';
        }
      }

      // Gets theme hook, adds last minute classes.
      $class = $item['#active'] ? 'facetapi-active' : 'facetapi-inactive';
      $variables['options']['attributes']['class'][] = $class;

      // Themes the link, adds row to items.
      $row['data'] = theme($item['#theme'], $variables);
      $items[] = $row;
    }
    return $items;
  }

  /**
   * Gets the base class array for a facet item.
   *
   * Classes that extend FacetapiWidgetLinks will often override this method to
   * alter the link displays via CSS without having to touch the render array.
   *
   * @return array
   *   An array of classes.
   */
  function getItemClasses() {
    return array();
  }

  /**
   * Overrides FacetapiWidget::settingsForm().
   */
  function settingsForm(&$form, &$form_state) {

    // @see http://drupal.org/node/735528 for supporting multiple values in the
    // FAPI #states. The following hack adds multiple form elements and uses CSS
    // and JavaScript to ensure only one is displayed at a time. Only the last
    // form element actually passes the value.
    $form['widget']['widget_settings']['links'][$this->id]['soft_limit'] = array(
      '#type' => 'select',
      '#title' => t('Soft limit'),
      '#default_value' => $this->settings->settings['soft_limit'],
      '#options' => array(
        0 => t('No limit'),
      ) + drupal_map_assoc(array(
        50,
        40,
        30,
        20,
        15,
        10,
        5,
        3,
      )),
      '#description' => t('Limits the number of displayed facets via JavaScript.'),
      '#states' => array(
        'visible' => array(
          'select[name="widget"]' => array(
            'value' => $this->id,
          ),
        ),
      ),
    );

    // @see http://drupal.org/node/1370342
    $form['widget']['widget_settings']['links'][$this->id]['nofollow'] = array(
      '#type' => 'select',
      '#title' => t('Prevent crawlers from following facet links'),
      '#default_value' => empty($this->settings->settings['nofollow']) ? 0 : $this->settings->settings['nofollow'],
      '#description' => t('Add the <code>rel="nofollow"</code> attribute to facet links to maximize SEO by preventing crawlers from indexing duplicate content and getting stuck in loops.'),
      '#options' => array(
        0 => t('never'),
        1 => t('always'),
        2 => t('only for links that select multiple items of the same facet'),
        3 => t('if there is already a selected item from any facet'),
      ),
      '#states' => array(
        'visible' => array(
          'select[name="widget"]' => array(
            'value' => $this->id,
          ),
        ),
      ),
    );

    // @see http://drupal.org/node/735528
    if ($this->facet['hierarchy callback']) {
      $form['widget']['widget_settings']['links'][$this->id]['show_expanded'] = array(
        '#type' => 'checkbox',
        '#title' => t('Expand hierarchy'),
        '#default_value' => !empty($this->settings->settings['show_expanded']),
        '#description' => t('Show the entire tree regardless of whether the parent items are active.'),
        '#states' => array(
          'visible' => array(
            'select[name="widget"]' => array(
              'value' => $this->id,
            ),
          ),
        ),
      );
    }

    // Hides all but the last element. The #states system will override this,
    // however it is necessary if JavaScript is disabled so multiple elements
    // aren't displayed to the user.
    $last = end($form['widget']['widget_settings']['links']);
    foreach ($form['widget']['widget_settings']['links'] as $id => $element) {
      if ($last != $element) {
        $form['widget']['widget_settings']['links'][$id]['#attributes']['style'] = 'display: none;';
      }
    }
  }

  /**
   * Overrides FacetapiWidget::getDefaultSettings().
   */
  function getDefaultSettings() {
    return array(
      'soft_limit' => 20,
      'nofollow' => 1,
      'show_expanded' => 0,
    );
  }

}

Members

Namesort descending Modifiers Type Description Overrides
FacetapiWidget::$build protected property The render that alterations are being applied to.
FacetapiWidget::$facet protected property The facet object containing the facet definition and required contexts.
FacetapiWidget::$id protected property The machine name of the plugin associated with this instance.
FacetapiWidget::$jsSettings protected property The widget's JavaScript settings that are passed to drupal_add_js().
FacetapiWidget::$key protected property The key of the facet's render array added to the realm's render array.
FacetapiWidget::$realm protected property The realm definition as returned by facetapi_realm_load().
FacetapiWidget::$settings protected property An array of facet settings.
FacetapiWidget::applySorts protected function Applies the sorts to the facet items recursively.
FacetapiWidget::getBuild public function Returns the altered render array acted on by FacetapiWidget::execute().
FacetapiWidget::getId public function Gets the machine name of the plugin.
FacetapiWidget::getJavaScriptSettings public function Returns the JavaScript settings that are passed to drupal_add_js().
FacetapiWidget::getKey public function Gets key used to append FacetapiWidget::build to the realm's render array.
FacetapiWidget::init public function Initializes the build, must be invoked prior to executing this widget. 1
FacetapiWidget::sortCallback protected function Applies sort information via the callback in the sort definition.
FacetapiWidget::sortFacet function Applies sorting algorithms to the items in the facet's render array.
FacetapiWidgetLinks::buildListItems function Transforms the render array for use with theme_item_list().
FacetapiWidgetLinks::execute public function Implements FacetapiWidget::execute(). Overrides FacetapiWidget::execute
FacetapiWidgetLinks::getDefaultSettings function Overrides FacetapiWidget::getDefaultSettings(). Overrides FacetapiWidget::getDefaultSettings
FacetapiWidgetLinks::getItemClasses function Gets the base class array for a facet item. 1
FacetapiWidgetLinks::setThemeHooks protected function Recursive function that sets each item's theme hook.
FacetapiWidgetLinks::settingsForm function Overrides FacetapiWidget::settingsForm(). Overrides FacetapiWidget::settingsForm
FacetapiWidgetLinks::__construct public function Overrides FacetapiWidget::__construct(). Overrides FacetapiWidget::__construct