| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181 |
- <?php
- /**
- * @file
- * An abstracted facet API that can be used by various search backends.
- */
- /**
- * Constant for the "AND" operator.
- */
- define('FACETAPI_OPERATOR_AND', 'and');
- /**
- * Constant for the "OR" operator.
- */
- define('FACETAPI_OPERATOR_OR', 'or');
- /**
- * String that represents a time gap of a year between two dates.
- */
- define('FACETAPI_DATE_YEAR', 'YEAR');
- /**
- * String that represents a time gap of a month between two dates.
- */
- define('FACETAPI_DATE_MONTH', 'MONTH');
- /**
- * String that represents a time gap of a day between two dates.
- */
- define('FACETAPI_DATE_DAY', 'DAY');
- /**
- * String that represents a time gap of an hour between two dates.
- */
- define('FACETAPI_DATE_HOUR', 'HOUR');
- /**
- * String that represents a time gap of a minute between two dates.
- */
- define('FACETAPI_DATE_MINUTE', 'MINUTE');
- /**
- * String that represents a time gap of a second between two dates.
- */
- define('FACETAPI_DATE_SECOND', 'SECOND');
- /**
- * Date string for ISO 8601 date formats.
- */
- define('FACETAPI_DATE_ISO8601', 'Y-m-d\TH:i:s\Z');
- /**
- * Regex pattern for range queries.
- */
- define('FACETAPI_REGEX_RANGE', '/^[\[\{](\S+) TO (\S+)[\]\}]$/');
- /**
- * Regex pattern for date queries.
- */
- define('FACETAPI_REGEX_DATE', '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/');
- /**
- * Regex pattern for date ranges.
- */
- define('FACETAPI_REGEX_DATE_RANGE', '/^\[(' . trim(FACETAPI_REGEX_DATE, '/^$') . ') TO (' . trim(FACETAPI_REGEX_DATE, '/^$') . ')\]$/');
- // Calls block specific hooks and overrides.
- module_load_include('inc', 'facetapi', 'facetapi.block');
- /**
- * Implements hook_facetapi_hook_info().
- */
- function facetapi_hook_info() {
- return array(
- 'facetapi_adapters' => array(
- 'group' => 'facetapi',
- ),
- 'facetapi_dependencies' => array(
- 'group' => 'facetapi',
- ),
- 'facetapi_empty_behaviors' => array(
- 'group' => 'facetapi',
- ),
- 'facetapi_facet_info' => array(
- 'group' => 'facetapi',
- ),
- 'facetapi_filters' => array(
- 'group' => 'facetapi',
- ),
- 'facetapi_query_types' => array(
- 'group' => 'facetapi',
- ),
- 'facetapi_realm_info' => array(
- 'group' => 'facetapi',
- ),
- 'facetapi_sort_info' => array(
- 'group' => 'facetapi',
- ),
- 'facetapi_url_processors' => array(
- 'group' => 'facetapi',
- ),
- 'facetapi_widgets' => array(
- 'group' => 'facetapi',
- ),
- 'current_search_items' => array(
- 'group' => 'facetapi',
- ),
- );
- }
- /**
- * Implements hook_menu().
- */
- function facetapi_menu() {
- $items = array();
- // Builds the realm settings forms for each searcher.
- foreach (facetapi_get_searcher_info() as $searcher => $searcher_info) {
- // Only build router items automatically if a path is provided.
- if (empty($searcher_info['path'])) {
- continue;
- }
- // Builds realm settings.
- $first = TRUE;
- foreach (facetapi_get_realm_info() as $realm_name => $realm) {
- if ($first) {
- $first = FALSE;
- // Add the first realm as a default local task.
- $items[$searcher_info['path'] . '/facets'] = array(
- 'title' => 'Facets',
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('facetapi_realm_settings_form', $searcher, $realm_name),
- 'access callback' => 'facetapi_access_callback',
- 'type' => MENU_LOCAL_TASK,
- 'file' => 'facetapi.admin.inc',
- );
- $items[$searcher_info['path'] . '/facets/' . $realm_name] = array(
- 'title' => $realm['label'],
- 'type' => MENU_DEFAULT_LOCAL_TASK,
- 'weight' => $realm['weight'],
- );
- }
- else {
- // Add all additional realms as local tasks.
- $items[$searcher_info['path'] . '/facets/' . $realm_name] = array(
- 'title' => $realm['label'],
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('facetapi_realm_settings_form', $searcher, $realm_name),
- 'access callback' => 'facetapi_access_callback',
- 'type' => MENU_LOCAL_TASK,
- 'file' => 'facetapi.admin.inc',
- );
- }
- }
- }
- $items['admin/config/search/facetapi/%facetapi_adapter/%facetapi_realm/%facetapi_facet/edit'] = array(
- 'title' => 'Configure facet display',
- 'load arguments' => array(4),
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('facetapi_facet_display_form', 4, 5, 6),
- 'access callback' => 'facetapi_access_callback',
- 'type' => MENU_LOCAL_ACTION,
- 'context' => MENU_CONTEXT_INLINE,
- 'weight' => -15,
- 'file' => 'facetapi.admin.inc',
- );
- $items['admin/config/search/facetapi/%facetapi_adapter/%facetapi_realm/%facetapi_dependencies/dependencies'] = array(
- 'title' => 'Configure facet dependencies',
- 'load arguments' => array(4),
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('facetapi_facet_dependencies_form', 4, 5, 6),
- 'access callback' => 'facetapi_access_callback',
- 'type' => MENU_LOCAL_ACTION,
- 'context' => MENU_CONTEXT_INLINE,
- 'weight' => -10,
- 'file' => 'facetapi.admin.inc',
- );
- $items['admin/config/search/facetapi/%facetapi_adapter/%facetapi_realm/%facetapi_filters/filters'] = array(
- 'title' => 'Configure facet filters',
- 'load arguments' => array(4),
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('facetapi_facet_filters_form', 4, 5, 6),
- 'access callback' => 'facetapi_access_callback',
- 'type' => MENU_LOCAL_ACTION,
- 'context' => MENU_CONTEXT_INLINE,
- 'weight' => -5,
- 'file' => 'facetapi.admin.inc',
- );
- $items['admin/config/search/facetapi/%facetapi_adapter/%facetapi_realm/%facetapi_facet/export'] = array(
- 'title' => 'Export facet configuration',
- 'load arguments' => array(4),
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('facetapi_export_form', 4, 5, 6),
- 'access callback' => 'facetapi_access_callback',
- 'type' => MENU_NORMAL_ITEM,
- 'file' => 'facetapi.admin.inc',
- );
- $items['admin/config/search/facetapi/%facetapi_adapter/%facetapi_realm/%facetapi_facet/revert'] = array(
- 'title' => 'Revert facet configuration',
- 'load arguments' => array(4),
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('facetapi_revert_form', 4, 5, 6),
- 'access callback' => 'facetapi_access_callback',
- 'type' => MENU_NORMAL_ITEM,
- 'file' => 'facetapi.admin.inc',
- );
- return $items;
- }
- /**
- * Implements hook_ctools_plugin_type().
- */
- function facetapi_ctools_plugin_type() {
- return array(
- 'adapters' => array(
- 'use hooks' => TRUE,
- ),
- 'dependencies' => array(
- 'use hooks' => TRUE,
- ),
- 'empty_behaviors' => array(
- 'use hooks' => TRUE,
- ),
- 'filters' => array(
- 'use hooks' => TRUE,
- ),
- 'query_types' => array(
- 'use hooks' => TRUE,
- ),
- 'url_processors' => array(
- 'use hooks' => TRUE,
- ),
- 'widgets' => array(
- 'use hooks' => TRUE,
- ),
- );
- }
- /**
- * Implements hook_theme().
- */
- function facetapi_theme() {
- return array(
- 'facetapi_title' => array(
- 'variables' => array('title' => NULL, 'facet' => array()),
- 'file' => 'facetapi.theme.inc',
- ),
- 'facetapi_facet_missing' => array(
- 'variables' => array('field_name' => NULL),
- 'file' => 'facetapi.theme.inc',
- ),
- 'facetapi_count' => array(
- 'variables' => array('count' => NULL),
- 'file' => 'facetapi.theme.inc',
- ),
- 'facetapi_link_inactive' => array(
- 'variables' => array('text' => NULL, 'path' => NULL, 'options' => array(), 'count' => 0),
- 'file' => 'facetapi.theme.inc',
- ),
- 'facetapi_link_active' => array(
- 'variables' => array('text' => NULL, 'path' => NULL, 'options' => array()),
- 'file' => 'facetapi.theme.inc',
- ),
- 'facetapi_deactivate_widget' => array(
- 'variables' => array('text' => NULL),
- 'file' => 'facetapi.theme.inc',
- ),
- 'facetapi_accessible_markup' => array(
- 'variables' => array('text' => NULL, 'active' => NULL),
- 'file' => 'facetapi.theme.inc',
- ),
- 'facetapi_realm_settings_table' => array(
- 'render element' => 'element',
- 'file' => 'facetapi.admin.inc',
- ),
- 'facetapi_sort_settings_table' => array(
- 'render element' => 'element',
- 'file' => 'facetapi.admin.inc',
- ),
- 'facetapi_filter_settings_table' => array(
- 'render element' => 'element',
- 'file' => 'facetapi.admin.inc',
- ),
- );
- }
- /**
- * Custom access callback. Checks if the user has either the "administer search"
- * OR "administer facets" permissions.
- *
- * @param stdClass|NULL $account
- * (optional) The account to check, if not given use currently logged in user.
- *
- * @return boolean
- * TRUE if the user has access to the resource, FALSE otherwise.
- */
- function facetapi_access_callback($account = NULL) {
- global $user;
- if (!isset($account)) {
- $account = $user;
- }
- return user_access('administer search', $account) || user_access('administer facets', $account);
- }
- /**
- * Implements hook_permission().
- */
- function facetapi_permission() {
- return array(
- 'administer facets' => array(
- 'title' => t('Administer Facets'),
- ),
- );
- }
- ////
- ////
- //// facetapi_*_load() functions
- ////
- ////
- /**
- * Instantiates the adapter plugin associated with the searcher.
- *
- * @param $searcher
- * The machine readable name of searcher.
- *
- * @return FacetapiAdapter
- * The adapter object, FALSE if the object can't be loaded.
- */
- function facetapi_adapter_load($searcher) {
- $adapters = &drupal_static(__FUNCTION__, array());
- if (!isset($adapters[$searcher])) {
- $searcher_info = facetapi_get_searcher_info();
- if (isset($searcher_info[$searcher]['adapter'])) {
- ctools_include('plugins');
- $id = $searcher_info[$searcher]['adapter'];
- $class = ctools_plugin_load_class('facetapi', 'adapters', $id, 'handler');
- $adapters[$searcher] = ($class) ? new $class($searcher_info[$searcher]) : FALSE;
- }
- else {
- $adapters[$searcher] = FALSE;
- }
- }
- return $adapters[$searcher];
- }
- /**
- * Loads the dependency plugins associated with the facet.
- *
- * @param $facet_name
- * The machine readable name of the facet.
- * @param $searcher
- * The machine readable name of the searcher module.
- *
- * @return array
- * An array of FacetapiDependency objects, FALSE if no plugins.
- */
- function facetapi_dependencies_load($facet_name, $searcher) {
- $dependencies = array();
- $facet = facetapi_facet_load($facet_name, $searcher);
- if ($facet && ($adapter = facetapi_adapter_load($searcher))) {
- foreach ($facet['dependency plugins'] as $id) {
- // NOTE: CTools plugin component is loaded by facetapi_adapter_load().
- $class = ctools_plugin_load_class('facetapi', 'dependencies', $id, 'handler');
- $settings = $adapter->getFacet($facet)->getSettings();
- $dependencies[] = new $class($id, $adapter, $facet, $settings);
- }
- }
- return ($dependencies) ? $dependencies : FALSE;
- }
- /**
- * Returns array of filter options available to the facet.
- *
- * @param $facet_name
- * The machine readable name of the facet.
- * @param $searcher
- * The machine readable name of the searcher.
- *
- * @return array
- * An array of FacetapiFilter objects, FALSE if no plugins.
- */
- function facetapi_filters_load($facet_name, $searcher) {
- $filters = array('plugins' => array());
- if ($filters['facet'] = facetapi_facet_load($facet_name, $searcher)) {
- $filters['plugins'] = facetapi_get_filters($filters['facet']);
- }
- return ($filters['plugins']) ? $filters : FALSE;
- }
- /**
- * Returns a realm definition.
- *
- * @param $realm_name
- * The machine readable name of the realm.
- *
- * @return array
- * An array containing the realm definition, FALSE if the realm is invalid.
- * - name: The machine name of the realm.
- * - label: The human readable name of the realm displayed in the admin UI.
- * - description: The description of the realm displayed in the admin UI.
- * - element type: The type of element facets are rendered as, such as 'links'
- * or 'form elements'.
- * - default widget: The default widget plugin id for facets in the realm.
- * - settings callback: A callback that alters the realm settings form.
- * - sortable: Whether the facets can be sorted via the admin UI.
- * - weight: The weight of the realm's menu item in comparison to the others.
- */
- function facetapi_realm_load($realm_name) {
- $realm_info = facetapi_get_realm_info();
- return (isset($realm_info[$realm_name])) ? $realm_info[$realm_name] : FALSE;
- }
- /**
- * Returns a facet definition.
- *
- * @param $facet_name
- * The machine readable name of the facet.
- * @param $searcher
- * The machine readable name of the searcher.
- *
- * @return
- * An array containing the facet definition, FALSE if the facet is invalid.
- * - name: Machine readable name of the facet.
- * - label: Human readable name of the facet displayed in settings forms.
- * - description: Description of the facet displayed in settings forms.
- * - field: The field name used by the backend to store and retrieve data from
- * the search index it is associated with.
- * - field alias: The query string variable inside of the filter key used to
- * pass the filter information through the query string.
- * - field api name: The machine readable name of the Field API field data the
- * facet is associated with, FALSE if it is not associated with a field.
- * - field api bundles: An array of entity names that this field contains
- * bundle information for.
- * - query types: The query type plugins that that this facet supports. For
- * example, numeric fields support "term" and "range_filter" queries.
- * - alter callbacks: Callbacks that alter the initialized render array
- * returned by the query type plugin. Defaults to an empty array.
- * - dependency plugins: An array of dependency plugin IDs that are supported
- * by this facet.
- * - default widget: The widget plugin ID used if no plugin has been selected
- * or the one selected is not valid.
- * - allowed operators: An array keyed by operator constant to boolean values
- * specifying whether the operator is supported.
- * - facet missing allowed: Whether or not missing facets are allowed.
- * - facet mincount allowed: Whether or not the facet supports the "minimum
- * facet count" setting.
- * - weight: The weight of the facet
- * - map callback: The callback used to map the raw values returned by the
- * index to something human readable.
- * - map options: An array of options passed to the map callback.
- * - hierarchy callback: A callback that maps the parent / child relationships
- * of the facet data, defaults to FALSE meaning the list is flat.
- * - values callback: In instances where facet data is not returned by the
- * backend, provide a list of values that can be used.
- * - min callback: For facets containing ranges, a callback returning the
- * minimum value in the index.
- * - max callback: For facets containing ranges, a callback returning the
- * maximum value in the index.
- * - default sorts: An array of available sorts. Each item is an array
- * containing two values, the first being the item being filtered on, the
- * second being the SORT_* constant.
- */
- function facetapi_facet_load($facet_name, $searcher) {
- $facet_info = facetapi_get_facet_info($searcher);
- return (isset($facet_info[$facet_name])) ? $facet_info[$facet_name] : FALSE;
- }
- ////
- ////
- //// facetapi_get_*() functions
- ////
- ////
- /**
- * Returns all defined searcher definitions.
- *
- * @return array
- * An array of searcher information. Each array is keyed by the machine
- * readable searcher name and contains the following items:
- * - name: The machine readable name of the searcher.
- * - label: The human readable name of the searcher displayed in the admin UI.
- * - adapter: The adapter plugin ID associated with the searcher.
- * - url processor: The URL processor plugin ID associated with the searcher.
- * - types: An array containing the types of content indexed by the searcher.
- * A type is usually an entity such as 'node', but it can be a non-entity
- * value as well.
- * - path: The MENU_DEFAULT_LOCAL_TASK item which the admin UI page is added
- * to as a MENU_LOCAL_TASK. An empty string if the backend manages the admin
- * UI menu items internally.
- * - supports facet missing: TRUE if the searcher supports "missing" facets.
- * - supports facet mincount: TRUE if the searcher supports the minimum facet
- * count setting.
- * - include default facets: TRUE if the searcher should include the facets
- * defined in facetapi_facetapi_facet_info() when indexing node content,
- * FALSE if they should be skipped.
- */
- function facetapi_get_searcher_info() {
- $searcher_info = array();
- foreach (module_implements('facetapi_searcher_info') as $module) {
- // Iterates over the module's searcher definition.
- foreach ((array) module_invoke($module, 'facetapi_searcher_info') as $searcher => $info) {
- // @see http://drupal.org/node/1167974
- // Converts "type" to an array and stores in "types".
- // @todo Remove in later versions.
- if (isset($info['type']) && !isset($info['types'])) {
- $info['types'] = array($info['type']);
- }
- // @see http://drupal.org/node/1304010
- // Converts "url_processor" to "url processor" for consistency.
- // @todo Remove in later versions.
- if (isset($info['url_processor']) && !isset($info['url processor'])) {
- $info['url processor'] = $info['url_processor'];
- }
- $info += array(
- 'module' => $module,
- 'name' => $searcher,
- 'path' => '',
- 'types' => array('node'),
- 'url processor' => 'standard',
- 'supports facet missing' => FALSE,
- 'supports facet mincount' => FALSE,
- 'include default facets' => TRUE,
- );
- // @see http://drupal.org/node/1167974
- // Makes sure old style "type" is present.
- if (!isset($info['type'])) {
- $info['type'] = $info['types'][key($info['types'])];
- }
- // Maps "types" so we can do faster lookups via isset().
- $info['types'] = drupal_map_assoc($info['types']);
- $searcher_info[$searcher] = $info;
- }
- }
- drupal_alter('facetapi_searcher_info', $searcher_info);
- array_walk($searcher_info, 'facetapi_map_assoc', 'types');
- return $searcher_info;
- }
- /**
- * Returns a list of active searchers.
- *
- * An active searcher means that facet data is parsed and processed by the
- * backend. Any searcher's adapter who's FacetapiAdapter::addActiveFilters() was
- * called is automatically added to this list.
- *
- * @return array
- * An associative array of active adapters
- */
- function facetapi_get_active_searchers() {
- $searchers = &drupal_static('facetapi_active_searchers', array());
- return $searchers;
- }
- /**
- * Returns all defined realm definitions.
- *
- * @return array
- * An array of realm definitions. Each definition is an array keyed by the
- * machine readable name of the realm. See the return value of the
- * facetapi_realm_load() function for the structure of the definitions.
- */
- function facetapi_get_realm_info() {
- $realm_info = &drupal_static(__FUNCTION__);
- if (NULL === $realm_info) {
- $realm_info = module_invoke_all('facetapi_realm_info');
- foreach ($realm_info as $realm_name => $realm) {
- $realm_info[$realm_name] += array(
- 'name' => $realm_name,
- 'label' => $realm_name,
- 'description' => '',
- 'default widget' => '',
- 'settings callback' => FALSE,
- 'element type' => 'links',
- 'sortable' => TRUE,
- 'weight' => 0,
- );
- }
- drupal_alter('facetapi_realm_info', $realm_info);
- uasort($realm_info, 'drupal_sort_weight');
- }
- return $realm_info;
- }
- /**
- * Returns all defined facet definitions available to the searcher.
- *
- * @param $searcher
- * A string containing the machine readable name of the searcher.
- *
- * @return array
- * An array of facet definitions. Each definition is an array keyed by the
- * machine readable name of the facet. See the return value of the
- * facetapi_facet_load() function for the structure of the definitions.
- */
- function facetapi_get_facet_info($searcher) {
- $facet_info = &drupal_static(__FUNCTION__, array());
- // Gets facet info if we haven't gotten it already.
- if (!isset($facet_info[$searcher])) {
- // Builds cache ID.
- global $language;
- $cid = 'facetapi:facet_info:' . $searcher . ':' . $language->language;
- // Checks if our results are cached.
- $cache = cache_get($cid);
- if (!empty($cache->data)) {
- $facet_info[$searcher] = $cache->data;
- }
- else {
- $searcher_info = facetapi_get_searcher_info();
- $facet_info[$searcher] = array();
- // Invokes hook_facetapi_facet_info(), normalizes facets.
- foreach (module_implements('facetapi_facet_info') as $module) {
- $facets = call_user_func($module . '_facetapi_facet_info', $searcher_info[$searcher]);
- if (!$facets || !is_array($facets)) {
- $facets = array();
- }
- // Iterates over facet definitions, merges defaults.
- foreach ($facets as $facet_name => $info) {
- // @see http://drupal.org/node/1161434
- // Converts "query type" to an array and stores in "query types".
- // @todo Remove in later versions.
- if (isset($info['query type']) && !isset($info['query types'])) {
- $info['query types'] = array($info['query type']);
- }
- $facet_info[$searcher][$facet_name] = $info;
- $facet_info[$searcher][$facet_name] += array(
- 'name' => $facet_name,
- 'label' => $facet_name,
- 'description' => '',
- 'field' => $facet_name,
- 'field alias' => isset($info['field']) ? $info['field'] : $facet_name,
- 'field api name' => FALSE,
- 'field api bundles' => array(),
- 'query types' => array('term'),
- 'alter callbacks' => array(),
- 'dependency plugins' => array(),
- 'default widget' => FALSE,
- 'allowed operators' => array(FACETAPI_OPERATOR_AND => TRUE, FACETAPI_OPERATOR_OR => TRUE),
- 'facet missing allowed' => FALSE,
- 'facet mincount allowed' => FALSE,
- 'weight' => 0,
- 'map callback' => FALSE,
- 'map options' => array(),
- 'hierarchy callback' => FALSE,
- 'values callback' => FALSE,
- 'min callback' => FALSE,
- 'max callback' => FALSE,
- 'default sorts' => array(
- array('active', SORT_DESC),
- array('count', SORT_DESC),
- array('display', SORT_ASC),
- ),
- );
- // @see http://drupal.org/node/1161434
- // Makes sure old style "query type" is present.
- // @todo Remove in later versions.
- if (!isset($facet_info[$searcher][$facet_name]['query type'])) {
- $type = key($facet_info[$searcher][$facet_name]['query types']);
- $facet_info[$searcher][$facet_name]['type'] = $type;
- }
- }
- }
- // Invokes alter hook, sorts and returns.
- drupal_alter('facetapi_facet_info', $facet_info[$searcher], $searcher_info[$searcher]);
- array_walk($facet_info[$searcher], 'facetapi_map_assoc', 'field api bundles');
- uasort($facet_info[$searcher], 'drupal_sort_weight');
- // Caches the result.
- cache_set($cid, $facet_info[$searcher], 'cache', CACHE_TEMPORARY);
- }
- }
- return $facet_info[$searcher];
- }
- /**
- * Implements hook_field_create_instance().
- */
- function facetapi_field_create_instance($instance) {
- cache_clear_all('facetapi:facet_info:', 'cache', TRUE);
- }
- /**
- * Implements hook_field_delete_instance().
- */
- function facetapi_field_delete_instance($instance) {
- cache_clear_all('facetapi:facet_info:', 'cache', TRUE);
- }
- /**
- * Wrapper around drupal_map_assoc() for a key in all items of an array.
- *
- * Useful as an array_walk() callback.
- *
- * @param array &$array
- * The array being modified.
- * @param $name
- * The key of the array being modified, usually the name of a definition.
- * @param $key
- * The key in the array being passed to drupal_map_assoc().
- */
- function facetapi_map_assoc(&$array, $name, $key) {
- $array[$key] = drupal_map_assoc($array[$key]);
- }
- /**
- * Returns all sort definitions.
- *
- * @return array
- * An associative array of sort definitions keyed by sort name. Each sort
- * definition contains:
- * - name: The machine readable name of the sort.
- * - title: The human readable name of the sort displayed in the admin UI.
- * - callback: The uasort() callback the render array is passed to.
- * - description: The description of the sort displayed in the admin UI.
- * - weight: The default weight of the sort specifying its processing order.
- */
- function facetapi_get_sort_info() {
- $sort_info = &drupal_static(__FUNCTION__);
- if (NULL === $sort_info) {
- $sort_info = module_invoke_all('facetapi_sort_info');
- foreach ($sort_info as $sort_name => $info) {
- $sort_info[$sort_name] += array(
- 'name' => $sort_name,
- 'label' => $sort_name,
- 'callback' => '',
- 'requirements' => array(),
- 'description' => '',
- 'weight' => 0,
- );
- }
- drupal_alter('facetapi_sort_info', $sort_info);
- }
- return $sort_info;
- }
- /**
- * Returns all filter definitions available to the facet.
- *
- * Each filter plugin must pass all the requirements checks specified in its
- * definition. Only plugins that pass all requirements are returned.
- *
- * @param array $facet
- * The facet definition as returned by facetapi_facet_load().
- *
- * @return array
- * An associative array of filter plugin definitions keyed by the plugin ID.
- * Each filter plugin definition contains:
- * - handler: An associative array containing:
- * - label: The human readable name of the plugin displayed in the admin UI.
- * - description: The description of the plugin displayed in the admin UI.
- * - class: The class containing the plugin.
- */
- function facetapi_get_filters(array $facet) {
- $plugins = array();
- // Iterates over all defined plugins.
- foreach (ctools_get_plugins('facetapi', 'filters') as $id => $plugin) {
- $plugin['handler'] += array(
- 'label' => $id,
- 'description' => '',
- 'requirements' => array(),
- );
- // Checks requirements.
- if (facetapi_check_requirements($plugin['handler']['requirements'], array(), $facet)) {
- $plugins[$id] = $plugin;
- }
- }
- return $plugins;
- }
- /**
- * Gets raw settings from the datbase, caches as a satic variable.
- *
- * Avoid using this function directly as it will not load default settings. Use
- * the FacetapiAdapter::getFacetSettings*() method instead.
- *
- * @param $searcher
- * A string containing the searcher.
- *
- * @return array
- * An array of settings keyed by name.
- *
- * @see FacetapiAdapter::getFacetSettings()
- * @see FacetapiAdapter::getFacetSettingsGlobal()
- */
- function facetapi_get_searcher_settings($searcher) {
- $settings = &drupal_static(__FUNCTION__, array());
- if (!isset($settings[$searcher])) {
- ctools_include('export');
- $args = array('searcher' => $searcher);
- $settings[$searcher] = ctools_export_load_object('facetapi', 'conditions', $args);
- }
- return $settings[$searcher];
- }
- /**
- * Returns all enabled facet definitions available to the searcher.
- *
- * If a realm is passed, this function returns all facets enabled in the realm.
- * If no realm is passed, this function returns all facets that are enabled in
- * at least one realm.
- *
- * @param $searcher
- * The machine readable name of the searcher.
- * @param $realm_name
- * The machine readable name of the realm, pass NULL to return all facets that
- * are enabled in at least one realm.
- *
- * @return array
- * An array of facet definitions. Each definition is an array keyed by the
- * machine readable name of the facet. See the return value of the
- * facetapi_facet_load() function for the structure of the definitions.
- */
- function facetapi_get_enabled_facets($searcher, $realm_name = NULL) {
- $enabled_facets = &drupal_static(__FUNCTION__, array());
- $cid = $searcher . ':' . (string) $realm_name;
- if (!isset($enabled_facets[$cid])) {
- $facets = array();
- // Gets cached settings, finds enabled facets.
- $cached = facetapi_get_searcher_settings($searcher);
- foreach ($cached as $name => $settings) {
- $test_enabled = (NULL === $realm_name || $realm_name == $settings->realm);
- if ($test_enabled && $settings->enabled) {
- $facets[$settings->facet] = $settings->facet;
- }
- }
- // Gets facet definitions for all enabled facets.
- $facet_info = facetapi_get_facet_info($searcher);
- $enabled_facets[$cid] = array_intersect_key($facet_info, $facets);
- // Alter enabled facets on the fly.
- drupal_alter('facetapi_enabled_facets', $enabled_facets[$cid], $searcher, $realm_name);
- }
- return $enabled_facets[$cid];
- }
- ////
- ////
- //// Utility functions
- ////
- ////
- /**
- * Translates a string via the translator module.
- *
- * @param $name
- * The name of the string in "textgroup:object_type:object_key:property_name"
- * format.
- * @param $string
- * The string being translated.
- * @param $langcode
- * The language code to translate to a language other than what is used to
- * display the page. Defaults to NULL, which uses the current language.
- *
- * @return
- * The translated string.
- */
- function facetapi_translate_string($name, $string, $langcode = NULL) {
- if ($module = variable_get('facetapi:translator_module', NULL)) {
- $function = $module . '_facetapi_translate_string';
- if (function_exists($function)) {
- $string = $function($name, $string, $langcode);
- }
- }
- return $string;
- }
- /**
- * Tests whether a searcher is active or not.
- *
- * @param $searcher
- * The machine readable name of the searcher.
- *
- * @return FacetapiAdapter
- * The adapter object, FALSE if the object can't be loaded.
- */
- function facetapi_is_active_searcher($searcher) {
- $searchers = facetapi_get_active_searchers();
- return (isset($searchers[$searcher]));
- }
- /**
- * Adds an active searcher to the list.
- *
- * @param $searcher
- * The machine readable name of the searcher.
- *
- * @see facetapi_get_active_searchers();
- */
- function facetapi_add_active_searcher($searcher) {
- $searchers = &drupal_static('facetapi_active_searchers', array());
- $searchers[$searcher] = $searcher;
- }
- /**
- * Tests whether a single facet is enabled in a given realm.
- *
- * @param $searcher
- * The machine readable name of the searcher.
- * @param $realm_name
- * The machine readable name of the realm, pass NULL to test if the facet is
- * enabled in at least one realm.
- * @param $facet_name
- * The machine readable name of the facet.
- *
- * @return
- * A boolean flagging whether the facet is enabled in the passed realm.
- */
- function facetapi_facet_enabled($searcher, $realm_name, $facet_name) {
- $enabled_facets = facetapi_get_enabled_facets($searcher, $realm_name, $facet_name);
- return isset($enabled_facets[$facet_name]);
- }
- /**
- * Builds a facet realm.
- *
- * Converts the facet data into a render array suitable for passing to the
- * drupal_render() function.
- *
- * @param $searcher
- * The machine readable name of the searcher.
- * @param $realm_name
- * The machine readable name of the realm.
- *
- * @return array
- * The realm's render array.
- */
- function facetapi_build_realm($searcher, $realm_name) {
- $adapter = facetapi_adapter_load($searcher);
- return ($adapter) ? $adapter->buildRealm($realm_name) : array();
- }
- /**
- * Checks requirements.
- *
- * Requirements fail if at least one requirements callback returns FALSE. Note
- * that if no requirement callbacks are passed, this function will return TRUE.
- *
- * @param array $requirements
- * The requirements keyed by callback to options.
- * @param array $realm
- * The realm definition.
- * @param array $facet
- * The facet definition.
- *
- * @return
- * A boolean flagging whether all requirements were passed.
- */
- function facetapi_check_requirements(array $requirements, array $realm, array $facet, $operator = 'AND') {
- $return = TRUE;
- module_load_include('inc', 'facetapi', 'facetapi.requirements');
- foreach ($requirements as $callback => $options) {
- if (!call_user_func($callback, $realm, $facet, $options, $operator)) {
- $return = FALSE;
- break;
- }
- }
- return $return;
- }
- /**
- * Enables or disables a facet for this page load only.
- *
- * @param $searcher
- * The machine readable name of the searcher.
- * @param $realm_name
- * The machine readable name of the realm, pass NULL for all realms.
- * @param $facet_name
- * The machine readable name of the facet.
- * @param $status
- * A boolean flagging whether the facet is enabled or disabled.
- * @param $batch_process
- * A boolean flagging whether batch processing is being performed. If set to
- * TRUE, the list of enabled facets won't be rebuild and the active items
- * won't be re-processed. Note that these tasks will have to be performed
- * manually in order for the status to be properly set.
- */
- function facetapi_set_facet_status($searcher, $realm_name, $facet_name, $status, $batch_process) {
- // Rebuild static if not batch processing.
- if (!$batch_process) {
- drupal_static('facetapi_get_enabled_facets', array(), TRUE);
- }
- // Pulls the list of enabled facets so we can modify it here.
- facetapi_get_enabled_facets($searcher, $realm_name);
- $enabled_facets = &drupal_static('facetapi_get_enabled_facets', array());
- // Performs the operation by setting or unsetting the facet.
- $cid = $searcher . ':' . (string) $realm_name;
- if ($status && !isset($enabled_facets[$cid][$facet_name])) {
- if ($facet = facetapi_facet_load($facet_name, $searcher)) {
- // Add facet to static, which enables it.
- $enabled_facets[$cid][$facet_name] = $facet;
- // If facet isn't already globally enabled, enable it.
- if (!isset($enabled_facets[$searcher . ':'][$facet_name])) {
- // Ensure sure static is set before modifying it.
- facetapi_get_enabled_facets($searcher, NULL);
- $enabled_facets[$searcher . ':'][$facet_name] = $facet;
- }
- }
- }
- elseif (!$status && isset($enabled_facets[$cid][$facet_name])) {
- // Removes facet to static, which disables it.
- unset($enabled_facets[$cid][$facet_name]);
- // If acting globally, disable facet in all realms.
- if (!$realm_name) {
- foreach (facetapi_get_realm_info() as $realm) {
- // Ensure sure static is set before unsetting the facet.
- facetapi_get_enabled_facets($searcher, $realm['name']);
- unset($enabled_facets[$searcher . ':' . $realm['name']][$facet_name]);
- }
- }
- }
- else {
- return;
- }
- // Re-process the active items since the list of active facets has changed.
- if (!$batch_process && ($adapter = facetapi_adapter_load($searcher))) {
- $adapter->processActiveItems();
- }
- }
- /**
- * Enables a facet for this page load only.
- *
- * If you are enabling facets in the block realm, you will have to force the
- * delta mapping so that the block can be configured even if it is disabled via
- * the Facet API interface. Otherwise you will not be able to assign the block
- * to a region because it won't be available in admin/structure/block.
- *
- * @param $searcher
- * The machine readable name of the searcher.
- * @param $realm_name
- * The machine readable name of the realm, pass NULL for all realms.
- * @param $facet_name
- * The machine readable name of the facet.
- * @param $batch_process
- * A boolean flagging whether batch processing is being performed.
- *
- * @see facetapi_set_facet_status()
- * @see hook_facetapi_force_delta_mapping().
- */
- function facetapi_set_facet_enabled($searcher, $realm_name, $facet_name, $batch_process = FALSE) {
- return facetapi_set_facet_status($searcher, $realm_name, $facet_name, TRUE, $batch_process);
- }
- /**
- * Disables a facet for this page load only.
- *
- * @param $searcher
- * The machine readable name of the searcher.
- * @param $realm_name
- * The machine readable name of the realm, pass NULL for all realms.
- * @param $facet_name
- * The machine readable name of the facet.
- * @param $batch_process
- * A boolean flagging whether batch processing is being performed.
- *
- * @see facetapi_set_facet_status()
- */
- function facetapi_set_facet_disabled($searcher, $realm_name, $facet_name, $batch_process = FALSE) {
- return facetapi_set_facet_status($searcher, $realm_name, $facet_name, FALSE, $batch_process);
- }
- /**
- * Sets the facet status in a given realm, stores settings in the database.
- *
- * @param FacetapiAdapter $adapter
- * The adapter object of the searcher the settings are being modified for.
- * @param array $realm
- * The realm definition as returned by facetapi_realm_load().
- * @param array $facet
- * The facet definition as returned by facetapi_facet_load().
- * @param $status
- * Flags whether or not the facet is being enabled or disabled.
- * @param $weight
- * If the realm is sortable, allows the assigning of a weight. Pass FALSE to
- * maintain the previously stored value.
- * @param $batch_process
- * A boolean flagging whether batch processing is being performed. If set to
- * TRUE, the caches and statics won't be reset.
- *
- * @reutrn
- * TRUE if the operation succeeded, FALSE otherwise.
- */
- function facetapi_save_facet_status(FacetapiAdapter $adapter, array $realm, array $facet, $status, $weight, $batch_process) {
- // Loads the realm settings, sets enabled flag and weight.
- $settings = $adapter->getFacet($facet)->getSettings($realm);
- $settings->enabled = ($status) ? 1 : 0;
- if (FALSE !== $weight) {
- $settings->settings['weight'] = $realm['sortable'] ? $weight : 0;
- }
- // Saves the settings in the database, stores the result.
- // NOTE: CTools export componenet loaded in the getSettings() method.
- $success = (FALSE !== ctools_export_crud_save('facetapi', $settings));
- // Clears caches and statics if we are not batch processing.
- if ($success && !$batch_process) {
- drupal_static('facetapi_get_searcher_settings', array(), TRUE);
- drupal_static('facetapi_get_enabled_facets', array(), TRUE);
- if ('block' == $realm['name']) {
- cache_clear_all(NULL, 'cache_block');
- cache_clear_all('facetapi:delta_map', 'cache');
- }
- }
- return $success;
- }
- /**
- * Enables a facet in a given realm, stores settings in the database.
- *
- * @param FacetapiAdapter $adapter
- * The adapter object of the searcher the settings are being modified for.
- * @param array $realm
- * The realm definition as returned by facetapi_realm_load().
- * @param array $facet
- * The facet definition as returned by facetapi_facet_load().
- * @param $weight
- * If the realm is sortable, allows the assigning of a weight. Pass FALSE to
- * maintain the previously stored value.
- * @param $batch_process
- * A boolean flagging whether batch processing is being performed.
- *
- * @reutrn
- * TRUE if the operation succeeded, FALSE otherwise.
- */
- function facetapi_save_facet_enabled(FacetapiAdapter $adapter, array $realm, array $facet, $weight = FALSE, $batch_process = FALSE) {
- return facetapi_save_facet_status($adapter, $realm, $facet, TRUE, $weight, $batch_process);
- }
- /**
- * Disables a facet in a given realm, stores settings in the database.
- *
- * @param FacetapiAdapter $adapter
- * The adapter object of the searcher the settings are being modified for.
- * @param array $realm
- * The realm definition as returned by facetapi_realm_load().
- * @param array $facet
- * The facet definition as returned by facetapi_facet_load().
- * @param $weight
- * If the realm is sortable, allows the assigning of a weight. Pass FALSE to
- * maintain the previously stored value.
- * @param $batch_process
- * A boolean flagging whether batch processing is being performed.
- *
- * @reutrn
- * TRUE if the operation succeeded, FALSE otherwise.
- */
- function facetapi_save_facet_disabled(FacetapiAdapter $adapter, array $realm, array $facet, $weight = FALSE, $batch_process = FALSE) {
- return facetapi_save_facet_status($adapter, $realm, $facet, FALSE, $weight, $batch_process);
- }
|