widget_links.inc in Facet API 7
Same filename and directory in other branches
The facetapi_links and facetapi_checkbox_links widget plugin classes.
File
plugins/facetapi/widget_links.incView source
<?php
/**
* @file
* The facetapi_links and facetapi_checkbox_links widget plugin classes.
*/
/**
* 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.
*/
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,
);
}
}
/**
* Widget that renders facets as a list of clickable checkboxes.
*
* This widget renders facets in the same way as FacetapiWidgetLinks but uses
* JavaScript to transform the links into checkboxes followed by the facet.
*/
class FacetapiWidgetCheckboxLinks extends FacetapiWidgetLinks {
/**
* Overrides FacetapiWidgetLinks::init().
*
* Adds additional JavaScript settings and CSS.
*/
public function init() {
parent::init();
$this->jsSettings['makeCheckboxes'] = 1;
drupal_add_css(drupal_get_path('module', 'facetapi') . '/facetapi.css');
}
/**
* Overrides FacetapiWidgetLinks::getItemClasses().
*
* Sets the base class for checkbox facet items.
*/
public function getItemClasses() {
return array(
'facetapi-checkbox',
);
}
}
Classes
Name | Description |
---|---|
FacetapiWidgetCheckboxLinks | Widget that renders facets as a list of clickable checkboxes. |
FacetapiWidgetLinks | Widget that renders facets as a list of clickable links. |