first import 7.x-1.4

This commit is contained in:
bachy
2013-03-15 17:32:30 +01:00
commit 9cc5ba4cfa
68 changed files with 17899 additions and 0 deletions

View File

@@ -0,0 +1,242 @@
<?php
/**
* @file
* Classes used by the Facet API module.
*/
/**
* Facet API adapter for the Search API module.
*/
class SearchApiFacetapiAdapter extends FacetapiAdapter {
/**
* Cached value for the current search for this searcher, if any.
*
* @see getCurrentSearch()
*
* @var array
*/
protected $current_search;
/**
* The active facet fields for the current search.
*
* @var array
*/
protected $fields = array();
/**
* Returns the path to the admin settings for a given realm.
*
* @param $realm_name
* The name of the realm.
*
* @return
* The path to the admin settings.
*/
public function getPath($realm_name) {
$base_path = 'admin/config/search/search_api';
$index_id = $this->info['instance'];
return $base_path . '/index/' . $index_id . '/facets/' . $realm_name;
}
/**
* Overrides FacetapiAdapter::getSearchPath().
*/
public function getSearchPath() {
$search = $this->getCurrentSearch();
if ($search && $search[0]->getOption('search_api_base_path')) {
return $search[0]->getOption('search_api_base_path');
}
return $_GET['q'];
}
/**
* Allows the backend to initialize its query object before adding the facet filters.
*
* @param mixed $query
* The backend's native object.
*/
public function initActiveFilters($query) {
$search_id = $query->getOption('search id');
$index_id = $this->info['instance'];
$facets = facetapi_get_enabled_facets($this->info['name']);
$this->fields = array();
// We statically store the current search per facet so that we can correctly
// assign it when building the facets. See the build() method in the query
// type plugin classes.
$active = &drupal_static('search_api_facetapi_active_facets', array());
foreach ($facets as $facet) {
$options = $this->getFacet($facet)->getSettings()->settings;
// The 'default_true' option is a choice between "show on all but the
// selected searches" (TRUE) and "show for only the selected searches".
$default_true = isset($options['default_true']) ? $options['default_true'] : TRUE;
// The 'facet_search_ids' option is the list of selected searches that
// will either be excluded or for which the facet will exclusively be
// displayed.
$facet_search_ids = isset($options['facet_search_ids']) ? $options['facet_search_ids'] : array();
if (array_search($search_id, $facet_search_ids) === FALSE) {
$search_ids = variable_get('search_api_facets_search_ids', array());
if (empty($search_ids[$index_id][$search_id])) {
// Remember this search ID.
$search_ids[$index_id][$search_id] = $search_id;
variable_set('search_api_facets_search_ids', $search_ids);
}
if (!$default_true) {
continue; // We are only to show facets for explicitly named search ids.
}
}
elseif ($default_true) {
continue; // The 'facet_search_ids' in the settings are to be excluded.
}
$active[$facet['name']] = $search_id;
$this->fields[$facet['name']] = array(
'field' => $facet['field'],
'limit' => $options['hard_limit'],
'operator' => $options['operator'],
'min_count' => $options['facet_mincount'],
'missing' => $options['facet_missing'],
);
}
}
/**
* Add the given facet to the query.
*/
public function addFacet(array $facet, SearchApiQueryInterface $query) {
if (isset($this->fields[$facet['name']])) {
$options = &$query->getOptions();
$options['search_api_facets'][$facet['name']] = $this->fields[$facet['name']];
}
}
/**
* Returns a boolean flagging whether $this->_searcher executed a search.
*/
public function searchExecuted() {
return (bool) $this->getCurrentSearch();
}
/**
* Helper method for getting a current search for this searcher.
*
* @return array
* The first matching current search, in the form specified by
* search_api_current_search(). Or NULL, if no match was found.
*/
public function getCurrentSearch() {
if (!isset($this->current_search)) {
$this->current_search = FALSE;
$index_id = $this->info['instance'];
// There is currently no way to configure the "current search" block to
// show on a per-searcher basis as we do with the facets. Therefore we
// cannot match it up to the correct "current search".
// I suspect that http://drupal.org/node/593658 would help.
// For now, just taking the first current search for this index. :-/
foreach (search_api_current_search() as $search) {
list($query, $results) = $search;
if ($query->getIndex()->machine_name == $index_id) {
$this->current_search = $search;
}
}
}
return $this->current_search ? $this->current_search : NULL;
}
/**
* Returns a boolean flagging whether facets in a realm shoud be displayed.
*
* Useful, for example, for suppressing sidebar blocks in some cases.
*
* @return
* A boolean flagging whether to display a given realm.
*/
public function suppressOutput($realm_name) {
// Not sure under what circumstances the output will need to be suppressed?
return FALSE;
}
/**
* Returns the search keys.
*/
public function getSearchKeys() {
$search = $this->getCurrentSearch();
$keys = $search[0]->getOriginalKeys();
if (is_array($keys)) {
// This will happen nearly never when displaying the search keys to the
// user, so go with a simple work-around.
// If someone complains, we can easily add a method for printing them
// properly.
$keys = '[' . t('complex query') . ']';
}
elseif (!$keys) {
// If a base path other than the current one is set, we assume that we
// shouldn't report on the current search. Highly hack-y, of course.
if ($search[0]->getOption('search_api_base_path', $_GET['q']) !== $_GET['q']) {
return NULL;
}
// Work-around since Facet API won't show the "Current search" block
// without keys.
$keys = '[' . t('all items') . ']';
}
drupal_alter('search_api_facetapi_keys', $keys, $search[0]);
return $keys;
}
/**
* Returns the number of total results found for the current search.
*/
public function getResultCount() {
$search = $this->getCurrentSearch();
// Each search is an array with the query as the first element and the results
// array as the second.
if (isset($search[1])) {
return $search[1]['result count'];
}
return 0;
}
/**
* Allows for backend specific overrides to the settings form.
*/
public function settingsForm(&$form, &$form_state) {
$facet = $form['#facetapi']['facet'];
$realm = $form['#facetapi']['realm'];
$facet_settings = $this->getFacet($facet)->getSettings();
$options = $facet_settings->settings;
$search_ids = variable_get('search_api_facets_search_ids', array());
$search_ids = isset($search_ids[$this->info['instance']]) ? $search_ids[$this->info['instance']] : array();
if (count($search_ids) > 1) {
$form['global']['default_true'] = array(
'#type' => 'select',
'#title' => t('Display for searches'),
'#options' => array(
TRUE => t('For all except the selected'),
FALSE => t('Only for the selected'),
),
'#default_value' => isset($options['default_true']) ? $options['default_true'] : TRUE,
);
$form['global']['facet_search_ids'] = array(
'#type' => 'select',
'#title' => t('Search IDs'),
'#options' => $search_ids,
'#size' => min(4, count($search_ids)),
'#multiple' => TRUE,
'#default_value' => isset($options['facet_search_ids']) ? $options['facet_search_ids'] : array(),
);
}
else {
$form['global']['default_true'] = array(
'#type' => 'value',
'#value' => TRUE,
);
$form['global']['facet_search_ids'] = array(
'#type' => 'value',
'#value' => array(),
);
}
}
}

View File

@@ -0,0 +1,196 @@
<?php
/**
* @file
* Date query type plugin for the Search API adapter.
*/
/**
* Plugin for "date" query types.
*/
class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQueryTypeInterface {
/**
* Loads the include file containing the date API functions.
*/
public function __construct(FacetapiAdapter $adapter, array $facet) {
module_load_include('date.inc', 'facetapi');
parent::__construct($adapter, $facet);
}
/**
* Returns the query type associated with the plugin.
*
* @return string
* The query type.
*/
static public function getType() {
return 'date';
}
/**
* Adds the filter to the query object.
*
* @param $query
* An object containing the query in the backend's native API.
*/
public function execute($query) {
// Return terms for this facet.
$this->adapter->addFacet($this->facet, $query);
// Change limit to "unlimited" (-1).
$options = &$query->getOptions();
if (!empty($options['search_api_facets'][$this->facet['name']])) {
$options['search_api_facets'][$this->facet['name']]['limit'] = -1;
}
if ($active = $this->adapter->getActiveItems($this->facet)) {
$item = end($active);
$field = $this->facet['field'];
$regex = str_replace(array('^', '$'), '', FACETAPI_REGEX_DATE);
$filter = preg_replace_callback($regex, array($this, 'replaceDateString'), $item['value']);
$this->addFacetFilter($query, $field, $filter);
}
}
/**
* Replacement callback for replacing ISO dates with timestamps.
*/
public function replaceDateString($matches) {
return strtotime($matches[0]);
}
/**
* Initializes the facet's build array.
*
* @return array
* The initialized render array.
*/
public function build() {
$facet = $this->adapter->getFacet($this->facet);
$search_ids = drupal_static('search_api_facetapi_active_facets', array());
if (empty($search_ids[$facet['name']]) || !search_api_current_search($search_ids[$facet['name']])) {
return array();
}
$search_id = $search_ids[$facet['name']];
$build = array();
$search = search_api_current_search($search_id);
$results = $search[1];
if (!$results['result count']) {
return array();
}
// Gets total number of documents matched in search.
$total = $results['result count'];
// Most of the code below is copied from search_facetapi's implementation of
// this method.
// Executes query, iterates over results.
if (isset($results['search_api_facets']) && isset($results['search_api_facets'][$this->facet['field']])) {
$values = $results['search_api_facets'][$this->facet['field']];
foreach ($values as $value) {
if ($value['count']) {
$filter = $value['filter'];
// We only process single values further. The "missing" filter and
// range filters will be passed on unchanged.
if ($filter == '!') {
$build[$filter]['#count'] = $value['count'];
}
elseif ($filter[0] == '"') {
$filter = substr($value['filter'], 1, -1);
if ($filter) {
$raw_values[$filter] = $value['count'];
}
}
else {
$filter = substr($value['filter'], 1, -1);
$pos = strpos($filter, ' ');
if ($pos !== FALSE) {
$lower = facetapi_isodate(substr($filter, 0, $pos), FACETAPI_DATE_DAY);
$upper = facetapi_isodate(substr($filter, $pos + 1), FACETAPI_DATE_DAY);
$filter = '[' . $lower . ' TO ' . $upper . ']';
}
$build[$filter]['#count'] = $value['count'];
}
}
}
}
// Gets active facets, starts building hierarchy.
$parent = $gap = NULL;
foreach ($this->adapter->getActiveItems($this->facet) as $value => $item) {
// If the item is active, the count is the result set count.
$build[$value] = array('#count' => $total);
// Gets next "gap" increment, minute being the lowest we can go.
if ($value[0] != '[' || $value[strlen($value) - 1] != ']' || !($pos = strpos($value, ' TO '))) {
continue;
}
$start = substr($value, 1, $pos);
$end = substr($value, $pos + 4, -1);
$date_gap = facetapi_get_date_gap($start, $end);
$gap = facetapi_get_next_date_gap($date_gap, FACETAPI_DATE_MINUTE);
// If there is a previous item, there is a parent, uses a reference so the
// arrays are populated when they are updated.
if (NULL !== $parent) {
$build[$parent]['#item_children'][$value] = &$build[$value];
$build[$value]['#item_parents'][$parent] = $parent;
}
// Stores the last value iterated over.
$parent = $value;
}
if (empty($raw_values)) {
return $build;
}
ksort($raw_values);
// Mind the gap! Calculates gap from min and max timestamps.
$timestamps = array_keys($raw_values);
if (NULL === $parent) {
if (count($raw_values) > 1) {
$gap = facetapi_get_timestamp_gap(min($timestamps), max($timestamps));
}
else {
$gap = FACETAPI_DATE_HOUR;
}
}
// Converts all timestamps to dates in ISO 8601 format.
$dates = array();
foreach ($timestamps as $timestamp) {
$dates[$timestamp] = facetapi_isodate($timestamp, $gap);
}
// Treat each date as the range start and next date as the range end.
$range_end = array();
$previous = NULL;
foreach (array_unique($dates) as $date) {
if (NULL !== $previous) {
$range_end[$previous] = facetapi_get_next_date_increment($previous, $gap);
}
$previous = $date;
}
$range_end[$previous] = facetapi_get_next_date_increment($previous, $gap);
// Groups dates by the range they belong to, builds the $build array
// with the facet counts and formatted range values.
foreach ($raw_values as $value => $count) {
$new_value = '[' . $dates[$value] . ' TO ' . $range_end[$dates[$value]] . ']';
if (!isset($build[$new_value])) {
$build[$new_value] = array('#count' => $count);
}
else {
$build[$new_value]['#count'] += $count;
}
// Adds parent information if not already set.
if (NULL !== $parent && $parent != $new_value) {
$build[$parent]['#item_children'][$new_value] = &$build[$new_value];
$build[$new_value]['#item_parents'][$parent] = $parent;
}
}
return $build;
}
}

View File

@@ -0,0 +1,149 @@
<?php
/**
* @file
* Term query type plugin for the Apache Solr adapter.
*/
/**
* Plugin for "term" query types.
*/
class SearchApiFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTypeInterface {
/**
* Returns the query type associated with the plugin.
*
* @return string
* The query type.
*/
static public function getType() {
return 'term';
}
/**
* Adds the filter to the query object.
*
* @param SearchApiQueryInterface $query
* An object containing the query in the backend's native API.
*/
public function execute($query) {
// Return terms for this facet.
$this->adapter->addFacet($this->facet, $query);
$settings = $this->adapter->getFacet($this->facet)->getSettings();
// Adds the operator parameter.
$operator = $settings->settings['operator'];
// Add active facet filters.
$active = $this->adapter->getActiveItems($this->facet);
if (empty($active)) {
return;
}
if (FACETAPI_OPERATOR_OR == $operator) {
// If we're dealing with an OR facet, we need to use a nested filter.
$facet_filter = $query->createFilter('OR');
}
else {
// Otherwise we set the conditions directly on the query.
$facet_filter = $query;
}
foreach ($active as $filter => $filter_array) {
$field = $this->facet['field'];
$this->addFacetFilter($facet_filter, $field, $filter);
}
// For OR facets, we now have to add the filter to the query.
if (FACETAPI_OPERATOR_OR == $operator) {
$query->filter($facet_filter);
}
}
/**
* Helper method for setting a facet filter on a query or query filter object.
*/
protected function addFacetFilter($query_filter, $field, $filter) {
// Integer (or other nun-string) filters might mess up some of the following
// comparison expressions.
$filter = (string) $filter;
if ($filter == '!') {
$query_filter->condition($field, NULL);
}
elseif ($filter[0] == '[' && $filter[strlen($filter) - 1] == ']' && ($pos = strpos($filter, ' TO '))) {
$lower = trim(substr($filter, 1, $pos));
$upper = trim(substr($filter, $pos + 4, -1));
if ($lower == '*' && $upper == '*') {
$query_filter->condition($field, NULL, '<>');
}
else {
if ($lower != '*') {
// Iff we have a range with two finite boundaries, we set two
// conditions (larger than the lower bound and less than the upper
// bound) and therefore have to make sure that we have an AND
// conjunction for those.
if ($upper != '*' && !($query_filter instanceof SearchApiQueryInterface || $query_filter->getConjunction() === 'AND')) {
$original_query_filter = $query_filter;
$query_filter = new SearchApiQueryFilter('AND');
}
$query_filter->condition($field, $lower, '>=');
}
if ($upper != '*') {
$query_filter->condition($field, $upper, '<=');
}
}
}
else {
$query_filter->condition($field, $filter);
}
if (isset($original_query_filter)) {
$original_query_filter->filter($query_filter);
}
}
/**
* Initializes the facet's build array.
*
* @return array
* The initialized render array.
*/
public function build() {
$facet = $this->adapter->getFacet($this->facet);
// The current search per facet is stored in a static variable (during
// initActiveFilters) so that we can retrieve it here and get the correct
// current search for this facet.
$search_ids = drupal_static('search_api_facetapi_active_facets', array());
if (empty($search_ids[$facet['name']]) || !search_api_current_search($search_ids[$facet['name']])) {
return array();
}
$search_id = $search_ids[$facet['name']];
$search = search_api_current_search($search_id);
$build = array();
$results = $search[1];
if (isset($results['search_api_facets']) && isset($results['search_api_facets'][$this->facet['field']])) {
$values = $results['search_api_facets'][$this->facet['field']];
foreach ($values as $value) {
$filter = $value['filter'];
// As Facet API isn't really suited for our native facet filter
// representations, convert the format here. (The missing facet can
// stay the same.)
if ($filter[0] == '"') {
$filter = substr($filter, 1, -1);
}
elseif ($filter != '!') {
// This is a range filter.
$filter = substr($filter, 1, -1);
$pos = strpos($filter, ' ');
if ($pos !== FALSE) {
$filter = '[' . substr($filter, 0, $pos) . ' TO ' . substr($filter, $pos + 1) . ']';
}
}
$build[$filter] = array(
'#count' => $value['count'],
);
}
}
return $build;
}
}