updated to 7.x-1.11
This commit is contained in:
@@ -109,7 +109,12 @@ class SearchApiFacetapiAdapter extends FacetapiAdapter {
|
||||
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']];
|
||||
$facet_info = $this->fields[$facet['name']];
|
||||
if (!empty($facet['query_options'])) {
|
||||
// Let facet-specific query options override the set options.
|
||||
$facet_info = $facet['query_options'] + $facet_info;
|
||||
}
|
||||
$options['search_api_facets'][$facet['name']] = $facet_info;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +144,7 @@ class SearchApiFacetapiAdapter extends FacetapiAdapter {
|
||||
// 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;
|
||||
list($query) = $search;
|
||||
if ($query->getIndex()->machine_name == $index_id) {
|
||||
$this->current_search = $search;
|
||||
}
|
||||
@@ -196,7 +201,6 @@ class SearchApiFacetapiAdapter extends FacetapiAdapter {
|
||||
*/
|
||||
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());
|
||||
@@ -205,6 +209,7 @@ class SearchApiFacetapiAdapter extends FacetapiAdapter {
|
||||
$form['global']['default_true'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Display for searches'),
|
||||
'#prefix' => '<div class="facetapi-global-setting">',
|
||||
'#options' => array(
|
||||
TRUE => t('For all except the selected'),
|
||||
FALSE => t('Only for the selected'),
|
||||
@@ -214,6 +219,7 @@ class SearchApiFacetapiAdapter extends FacetapiAdapter {
|
||||
$form['global']['facet_search_ids'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Search IDs'),
|
||||
'#suffix' => '</div>',
|
||||
'#options' => $search_ids,
|
||||
'#size' => min(4, count($search_ids)),
|
||||
'#multiple' => TRUE,
|
||||
@@ -246,9 +252,25 @@ class SearchApiFacetapiAdapter extends FacetapiAdapter {
|
||||
'#type' => 'select',
|
||||
'#title' => t('Granularity'),
|
||||
'#description' => t('Determine the maximum drill-down level'),
|
||||
'#prefix' => '<div class="facetapi-global-setting">',
|
||||
'#suffix' => '</div>',
|
||||
'#options' => $granularity_options,
|
||||
'#default_value' => isset($options['date_granularity']) ? $options['date_granularity'] : FACETAPI_DATE_MINUTE,
|
||||
);
|
||||
}
|
||||
|
||||
// Add an "Exclude" option for terms.
|
||||
if(!empty($facet['query types']) && in_array('term', $facet['query types'])) {
|
||||
$form['global']['operator']['#weight'] = -2;
|
||||
unset($form['global']['operator']['#suffix']);
|
||||
$form['global']['exclude'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Exclude'),
|
||||
'#description' => t('Make the search exclude selected facets, instead of restricting it to them.'),
|
||||
'#suffix' => '</div>',
|
||||
'#weight' => -1,
|
||||
'#default_value' => !empty($options['exclude']),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -37,6 +37,17 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
|
||||
public function execute($query) {
|
||||
// Return terms for this facet.
|
||||
$this->adapter->addFacet($this->facet, $query);
|
||||
|
||||
$settings = $this->adapter->getFacet($this->facet)->getSettings()->settings;
|
||||
|
||||
// First check if the facet is enabled for this search.
|
||||
$default_true = isset($settings['default_true']) ? $settings['default_true'] : TRUE;
|
||||
$facet_search_ids = isset($settings['facet_search_ids']) ? $settings['facet_search_ids'] : array();
|
||||
if ($default_true != empty($facet_search_ids[$query->getOption('search id')])) {
|
||||
// Facet is not enabled for this search ID.
|
||||
return;
|
||||
}
|
||||
|
||||
// Change limit to "unlimited" (-1).
|
||||
$options = &$query->getOptions();
|
||||
if (!empty($options['search_api_facets'][$this->facet['name']])) {
|
||||
@@ -121,7 +132,8 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
|
||||
|
||||
// Gets active facets, starts building hierarchy.
|
||||
$parent = $gap = NULL;
|
||||
foreach ($this->adapter->getActiveItems($this->facet) as $value => $item) {
|
||||
$active_items = $this->adapter->getActiveItems($this->facet);
|
||||
foreach ($active_items as $value => $item) {
|
||||
// If the item is active, the count is the result set count.
|
||||
$build[$value] = array('#count' => $total);
|
||||
|
||||
@@ -199,7 +211,9 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
|
||||
if (!isset($build[$new_value])) {
|
||||
$build[$new_value] = array('#count' => $count);
|
||||
}
|
||||
else {
|
||||
// Active items already have their value set because it's the current
|
||||
// result count.
|
||||
elseif (!isset($active_items[$new_value])) {
|
||||
$build[$new_value]['#count'] += $count;
|
||||
}
|
||||
|
||||
|
@@ -30,53 +30,66 @@ class SearchApiFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTy
|
||||
// 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'];
|
||||
$settings = $this->adapter->getFacet($this->facet)->getSettings()->settings;
|
||||
|
||||
// Add active facet filters.
|
||||
// First check if the facet is enabled for this search.
|
||||
$default_true = isset($settings['default_true']) ? $settings['default_true'] : TRUE;
|
||||
$facet_search_ids = isset($settings['facet_search_ids']) ? $settings['facet_search_ids'] : array();
|
||||
if ($default_true != empty($facet_search_ids[$query->getOption('search id')])) {
|
||||
// Facet is not enabled for this search ID.
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the 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');
|
||||
// Create the facet filter, and add a tag to it so that it can be easily
|
||||
// identified down the line by services when they need to exclude facets.
|
||||
$operator = $settings['operator'];
|
||||
if ($operator == FACETAPI_OPERATOR_AND) {
|
||||
$conjunction = 'AND';
|
||||
}
|
||||
elseif ($operator == FACETAPI_OPERATOR_OR) {
|
||||
$conjunction = 'OR';
|
||||
}
|
||||
else {
|
||||
// Otherwise we set the conditions directly on the query.
|
||||
$facet_filter = $query;
|
||||
throw new SearchApiException(t('Unknown facet operator %operator.', array('%operator' => $operator)));
|
||||
}
|
||||
$tags = array('facet:' . $this->facet['field']);
|
||||
$facet_filter = $query->createFilter($conjunction, $tags);
|
||||
|
||||
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);
|
||||
}
|
||||
// Now add the filter to the query.
|
||||
$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) {
|
||||
// Test if this filter should be negated.
|
||||
$settings = $this->adapter->getFacet($this->facet)->getSettings();
|
||||
$exclude = !empty($settings->settings['exclude']);
|
||||
// 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);
|
||||
$query_filter->condition($field, NULL, $exclude ? '<>' : '=');
|
||||
}
|
||||
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, '<>');
|
||||
$query_filter->condition($field, NULL, $exclude ? '=' : '<>');
|
||||
}
|
||||
else {
|
||||
elseif (!$exclude) {
|
||||
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
|
||||
@@ -92,9 +105,22 @@ class SearchApiFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTy
|
||||
$query_filter->condition($field, $upper, '<=');
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Same as above, but with inverted logic.
|
||||
if ($lower != '*') {
|
||||
if ($upper != '*' && ($query_filter instanceof SearchApiQueryInterface || $query_filter->getConjunction() === 'AND')) {
|
||||
$original_query_filter = $query_filter;
|
||||
$query_filter = new SearchApiQueryFilter('OR');
|
||||
}
|
||||
$query_filter->condition($field, $lower, '<');
|
||||
}
|
||||
if ($upper != '*') {
|
||||
$query_filter->condition($field, $upper, '>');
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$query_filter->condition($field, $filter);
|
||||
$query_filter->condition($field, $filter, $exclude ? '<>' : '=');
|
||||
}
|
||||
if (isset($original_query_filter)) {
|
||||
$original_query_filter->filter($query_filter);
|
||||
|
@@ -9,9 +9,9 @@ files[] = plugins/facetapi/adapter.inc
|
||||
files[] = plugins/facetapi/query_type_term.inc
|
||||
files[] = plugins/facetapi/query_type_date.inc
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-09-01
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2013-12-25
|
||||
version = "7.x-1.11"
|
||||
core = "7.x"
|
||||
project = "search_api"
|
||||
datestamp = "1378025826"
|
||||
datestamp = "1387965506"
|
||||
|
||||
|
@@ -92,7 +92,7 @@ function search_api_facetapi_facetapi_facet_info(array $searcher_info) {
|
||||
// other modules.
|
||||
$type_settings = array(
|
||||
'taxonomy_term' => array(
|
||||
'hierarchy callback' => 'facetapi_get_taxonomy_hierarchy',
|
||||
'hierarchy callback' => 'search_api_facetapi_get_taxonomy_hierarchy',
|
||||
),
|
||||
'date' => array(
|
||||
'query type' => 'date',
|
||||
@@ -226,6 +226,26 @@ function search_api_facetapi_settings($realm_name, SearchApiIndex $index) {
|
||||
return drupal_get_form('facetapi_realm_settings_form', $searcher_name, $realm_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets hierarchy information for taxonomy terms.
|
||||
*
|
||||
* Used as a hierarchy callback in search_api_facetapi_facetapi_facet_info().
|
||||
*
|
||||
* Internally just uses facetapi_get_taxonomy_hierarchy(), but makes sure that
|
||||
* our special "!" value is not passed.
|
||||
*
|
||||
* @param array $values
|
||||
* An array containing the term IDs.
|
||||
*
|
||||
* @return array
|
||||
* An associative array mapping term IDs to parent IDs (where parents could be
|
||||
* found).
|
||||
*/
|
||||
function search_api_facetapi_get_taxonomy_hierarchy(array $values) {
|
||||
$values = array_filter($values, 'is_numeric');
|
||||
return $values ? facetapi_get_taxonomy_hierarchy($values) : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map callback for all search_api facet fields.
|
||||
*
|
||||
|
@@ -151,11 +151,9 @@ class SearchApiViewsFacetsBlockDisplay extends views_plugin_display_block {
|
||||
}
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
if (substr($this->view->base_table, 0, 17) != 'search_api_index_') {
|
||||
form_set_error('', t('The "Facets block" display can only be used with base tables based on Search API indexes.'));
|
||||
return NULL;
|
||||
}
|
||||
public function query(){
|
||||
parent::query();
|
||||
|
||||
$facet_field = $this->get_option('facet_field');
|
||||
if (!$facet_field) {
|
||||
return NULL;
|
||||
@@ -165,7 +163,7 @@ class SearchApiViewsFacetsBlockDisplay extends views_plugin_display_block {
|
||||
if (!$base_path) {
|
||||
$base_path = $_GET['q'];
|
||||
}
|
||||
$this->view->build();
|
||||
|
||||
$limit = empty($this->view->query->pager->options['items_per_page']) ? 10 : $this->view->query->pager->options['items_per_page'];
|
||||
$query_options = &$this->view->query->getOptions();
|
||||
if (!$this->get_option('hide_block')) {
|
||||
@@ -179,6 +177,17 @@ class SearchApiViewsFacetsBlockDisplay extends views_plugin_display_block {
|
||||
}
|
||||
$query_options['search_api_base_path'] = $base_path;
|
||||
$this->view->query->range(0, 0);
|
||||
}
|
||||
|
||||
public function render() {
|
||||
if (substr($this->view->base_table, 0, 17) != 'search_api_index_') {
|
||||
form_set_error('', t('The "Facets block" display can only be used with base tables based on Search API indexes.'));
|
||||
return NULL;
|
||||
}
|
||||
$facet_field = $this->get_option('facet_field');
|
||||
if (!$facet_field) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$this->view->execute();
|
||||
|
||||
@@ -229,7 +238,7 @@ class SearchApiViewsFacetsBlockDisplay extends views_plugin_display_block {
|
||||
// Initializes variables passed to theme hook.
|
||||
$variables = array(
|
||||
'text' => $name,
|
||||
'path' => $base_path,
|
||||
'path' => $this->view->query->getOption('search_api_base_path'),
|
||||
'count' => $term['count'],
|
||||
'options' => array(
|
||||
'attributes' => array('class' => 'facetapi-inactive'),
|
||||
@@ -249,10 +258,16 @@ class SearchApiViewsFacetsBlockDisplay extends views_plugin_display_block {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$info['content']['facets'] = array(
|
||||
return array(
|
||||
'facets' => array(
|
||||
'#theme' => 'item_list',
|
||||
'#items' => $facets,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function execute(){
|
||||
$info['content'] = $this->render();
|
||||
$info['content']['more'] = $this->render_more_link();
|
||||
$info['subject'] = filter_xss_admin($this->view->get_title());
|
||||
return $info;
|
||||
|
@@ -1,5 +1,10 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains SearchApiViewsHandlerArgument.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Views argument handler class for handling all non-fulltext types.
|
||||
*/
|
||||
|
161
contrib/search_api_views/includes/handler_argument_date.inc
Normal file
161
contrib/search_api_views/includes/handler_argument_date.inc
Normal file
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the SearchApiViewsHandlerArgumentDate class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines a contextual filter searching for a date or date range.
|
||||
*/
|
||||
class SearchApiViewsHandlerArgumentDate extends SearchApiViewsHandlerArgument {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query($group_by = FALSE) {
|
||||
if (empty($this->value)) {
|
||||
$this->fillValue();
|
||||
if ($this->value === FALSE) {
|
||||
$this->abort();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$outer_conjunction = strtoupper($this->operator);
|
||||
|
||||
if (empty($this->options['not'])) {
|
||||
$operator = '=';
|
||||
$inner_conjunction = 'OR';
|
||||
}
|
||||
else {
|
||||
$operator = '<>';
|
||||
$inner_conjunction = 'AND';
|
||||
}
|
||||
|
||||
if (!empty($this->value)) {
|
||||
if (!empty($this->value)) {
|
||||
$outer_filter = $this->query->createFilter($outer_conjunction);
|
||||
foreach ($this->value as $value) {
|
||||
$value_filter = $this->query->createFilter($inner_conjunction);
|
||||
$values = explode(';', $value);
|
||||
$values = array_map(array($this, 'getTimestamp'), $values);
|
||||
if (in_array(FALSE, $values, TRUE)) {
|
||||
$this->abort();
|
||||
return;
|
||||
}
|
||||
$is_range = (count($values) > 1);
|
||||
|
||||
$inner_filter = ($is_range ? $this->query->createFilter('AND') : $value_filter);
|
||||
$range_op = (empty($this->options['not']) ? '>=' : '<');
|
||||
$inner_filter->condition($this->real_field, $values[0], $is_range ? $range_op : $operator);
|
||||
if ($is_range) {
|
||||
$range_op = (empty($this->options['not']) ? '<=' : '>');
|
||||
$inner_filter->condition($this->real_field, $values[1], $range_op);
|
||||
$value_filter->filter($inner_filter);
|
||||
}
|
||||
$outer_filter->filter($value_filter);
|
||||
}
|
||||
|
||||
$this->query->filter($outer_filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a value to a timestamp, if it isn't one already.
|
||||
*
|
||||
* @param string|int $value
|
||||
* The value to convert. Either a timestamp, or a date/time string as
|
||||
* recognized by strtotime().
|
||||
*
|
||||
* @return int|false
|
||||
* The parsed timestamp, or FALSE if an illegal string was passed.
|
||||
*/
|
||||
public function getTimestamp($value) {
|
||||
if (is_numeric($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return strtotime($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills $this->value with data from the argument.
|
||||
*/
|
||||
protected function fillValue() {
|
||||
if (!empty($this->options['break_phrase'])) {
|
||||
// Set up defaults:
|
||||
if (!isset($this->value)) {
|
||||
$this->value = array();
|
||||
}
|
||||
|
||||
if (!isset($this->operator)) {
|
||||
$this->operator = 'OR';
|
||||
}
|
||||
|
||||
if (empty($this->argument)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (preg_match('/^([-\d;:\s]+\+)*[-\d;:\s]+$/', $this->argument)) {
|
||||
// The '+' character in a query string may be parsed as ' '.
|
||||
$this->value = explode('+', $this->argument);
|
||||
}
|
||||
elseif (preg_match('/^([-\d;:\s]+,)*[-\d;:\s]+$/', $this->argument)) {
|
||||
$this->operator = 'AND';
|
||||
$this->value = explode(',', $this->argument);
|
||||
}
|
||||
|
||||
// Keep an 'error' value if invalid strings were given.
|
||||
if (!empty($this->argument) && (empty($this->value) || !is_array($this->value))) {
|
||||
$this->value = FALSE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->value = array($this->argument);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aborts the associated query due to an illegal argument.
|
||||
*/
|
||||
protected function abort() {
|
||||
$variables['!field'] = $this->definition['group'] . ': ' . $this->definition['title'];
|
||||
$this->query->abort(t('Illegal argument passed to !field contextual filter.', $variables));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the title this argument will assign the view, given the argument.
|
||||
*
|
||||
* @return string
|
||||
* A title fitting for the passed argument.
|
||||
*/
|
||||
public function title() {
|
||||
if (!empty($this->argument)) {
|
||||
if (empty($this->value)) {
|
||||
$this->fillValue();
|
||||
}
|
||||
$dates = array();
|
||||
foreach ($this->value as $date) {
|
||||
$date_parts = explode(';', $date);
|
||||
|
||||
$ts = $this->getTimestamp($date_parts[0]);
|
||||
$datestr = format_date($ts, 'short');
|
||||
if (count($date_parts) > 1) {
|
||||
$ts = $this->getTimestamp($date_parts[1]);
|
||||
$datestr .= ' - ' . format_date($ts, 'short');
|
||||
}
|
||||
|
||||
if ($datestr) {
|
||||
$dates[] = $datestr;
|
||||
}
|
||||
}
|
||||
|
||||
return $dates ? implode(', ', $dates) : check_plain($this->argument);
|
||||
}
|
||||
|
||||
return check_plain($this->argument);
|
||||
}
|
||||
|
||||
}
|
@@ -1,5 +1,10 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains SearchApiViewsHandlerArgumentFulltext.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Views argument handler class for handling fulltext fields.
|
||||
*/
|
||||
|
@@ -1,5 +1,10 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains SearchApiViewsHandlerArgumentMoreLikeThis.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Views argument handler providing a list of related items for search servers
|
||||
* supporting the "search_api_mlt" feature.
|
||||
|
@@ -1,5 +1,10 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains SearchApiViewsHandlerArgumentString.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Views argument handler class for handling string fields.
|
||||
*/
|
||||
|
@@ -1,5 +1,10 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains SearchApiViewsHandlerFilter.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Views filter handler base class for handling all "normal" cases.
|
||||
*/
|
||||
@@ -31,8 +36,8 @@ class SearchApiViewsHandlerFilter extends views_handler_filter {
|
||||
*/
|
||||
public function operator_options() {
|
||||
return array(
|
||||
'<' => t('Is smaller than'),
|
||||
'<=' => t('Is smaller than or equal to'),
|
||||
'<' => t('Is less than'),
|
||||
'<=' => t('Is less than or equal to'),
|
||||
'=' => t('Is equal to'),
|
||||
'<>' => t('Is not equal to'),
|
||||
'>=' => t('Is greater than or equal to'),
|
||||
@@ -46,8 +51,8 @@ class SearchApiViewsHandlerFilter extends views_handler_filter {
|
||||
* Provide a form for setting the filter value.
|
||||
*/
|
||||
public function value_form(&$form, &$form_state) {
|
||||
while (is_array($this->value)) {
|
||||
$this->value = $this->value ? array_shift($this->value) : NULL;
|
||||
while (is_array($this->value) && count($this->value) < 2) {
|
||||
$this->value = $this->value ? reset($this->value) : NULL;
|
||||
}
|
||||
$form['value'] = array(
|
||||
'#type' => 'textfield',
|
||||
@@ -58,10 +63,19 @@ class SearchApiViewsHandlerFilter extends views_handler_filter {
|
||||
|
||||
// Hide the value box if the operator is 'empty' or 'not empty'.
|
||||
// Radios share the same selector so we have to add some dummy selector.
|
||||
$form['value']['#states']['visible'] = array(
|
||||
':input[name="options[operator]"],dummy-empty' => array('!value' => 'empty'),
|
||||
':input[name="options[operator]"],dummy-not-empty' => array('!value' => 'not empty'),
|
||||
);
|
||||
if (empty($form_state['exposed'])) {
|
||||
$form['value']['#states']['visible'] = array(
|
||||
':input[name="options[operator]"],dummy-empty' => array('!value' => 'empty'),
|
||||
':input[name="options[operator]"],dummy-not-empty' => array('!value' => 'not empty'),
|
||||
);
|
||||
}
|
||||
elseif (!empty($this->options['expose']['use_operator'])) {
|
||||
$name = $this->options['expose']['operator_id'];
|
||||
$form['value']['#states']['visible'] = array(
|
||||
':input[name="' . $name . '"],dummy-empty' => array('!value' => 'empty'),
|
||||
':input[name="' . $name . '"],dummy-not-empty' => array('!value' => 'not empty'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,5 +1,10 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains SearchApiViewsHandlerFilterBoolean.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Views filter handler class for handling fulltext fields.
|
||||
*/
|
||||
|
@@ -1,5 +1,10 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains SearchApiViewsHandlerFilterDate.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Views filter handler base class for handling all "normal" cases.
|
||||
*/
|
||||
|
211
contrib/search_api_views/includes/handler_filter_entity.inc
Normal file
211
contrib/search_api_views/includes/handler_filter_entity.inc
Normal file
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains SearchApiViewsHandlerFilterEntity.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Views filter handler class for entities.
|
||||
*
|
||||
* Should be extended for specific entity types, such as
|
||||
* SearchApiViewsHandlerFilterUser and SearchApiViewsHandlerFilterTaxonomyTerm.
|
||||
*
|
||||
* Based on views_handler_filter_term_node_tid.
|
||||
*/
|
||||
abstract class SearchApiViewsHandlerFilterEntity extends SearchApiViewsHandlerFilter {
|
||||
|
||||
/**
|
||||
* If exposed form input was successfully validated, the entered entity IDs.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $validated_exposed_input;
|
||||
|
||||
/**
|
||||
* Validates entered entity labels and converts them to entity IDs.
|
||||
*
|
||||
* Since this can come from either the form or the exposed filter, this is
|
||||
* abstracted out a bit so it can handle the multiple input sources.
|
||||
*
|
||||
* @param array $form
|
||||
* The form or form element for which any errors should be set.
|
||||
* @param array $values
|
||||
* The entered user names to validate.
|
||||
*
|
||||
* @return array
|
||||
* The entity IDs corresponding to all entities that could be found.
|
||||
*/
|
||||
abstract protected function validate_entity_strings(array &$form, array $values);
|
||||
|
||||
/**
|
||||
* Transforms an array of entity IDs into a comma-separated list of labels.
|
||||
*
|
||||
* @param array $ids
|
||||
* The entity IDs to transform.
|
||||
*
|
||||
* @return string
|
||||
* A string containing the labels corresponding to the IDs, separated by
|
||||
* commas.
|
||||
*/
|
||||
abstract protected function ids_to_strings(array $ids);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function operator_options() {
|
||||
$operators = array(
|
||||
'=' => $this->isMultiValued() ? t('Is one of') : t('Is'),
|
||||
'all of' => t('Is all of'),
|
||||
'<>' => $this->isMultiValued() ? t('Is not one of') : t('Is not'),
|
||||
'empty' => t('Is empty'),
|
||||
'not empty' => t('Is not empty'),
|
||||
);
|
||||
if (!$this->isMultiValued()) {
|
||||
unset($operators['all of']);
|
||||
}
|
||||
return $operators;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function option_definition() {
|
||||
$options = parent::option_definition();
|
||||
|
||||
$options['expose']['multiple']['default'] = TRUE;
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function value_form(&$form, &$form_state) {
|
||||
parent::value_form($form, $form_state);
|
||||
|
||||
if (!is_array($this->value)) {
|
||||
$this->value = $this->value ? array($this->value) : array();
|
||||
}
|
||||
|
||||
// Set the correct default value in case the admin-set value is used (and a
|
||||
// value is present). The value is used if the form is either not exposed,
|
||||
// or the exposed form wasn't submitted yet (there is
|
||||
if ($this->value && (empty($form_state['input']) || !empty($form_state['input']['live_preview']))) {
|
||||
$form['value']['#default_value'] = $this->ids_to_strings($this->value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function value_validate($form, &$form_state) {
|
||||
if (!empty($form['value'])) {
|
||||
$value = &$form_state['values']['options']['value'];
|
||||
$values = $this->isMultiValued($form_state['values']['options']) ? drupal_explode_tags($value) : array($value);
|
||||
$ids = $this->validate_entity_strings($form['value'], $values);
|
||||
|
||||
if ($ids) {
|
||||
$value = $ids;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function accept_exposed_input($input) {
|
||||
$rc = parent::accept_exposed_input($input);
|
||||
|
||||
if ($rc) {
|
||||
// If we have previously validated input, override.
|
||||
if ($this->validated_exposed_input) {
|
||||
$this->value = $this->validated_exposed_input;
|
||||
}
|
||||
}
|
||||
|
||||
return $rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function exposed_validate(&$form, &$form_state) {
|
||||
if (empty($this->options['exposed']) || empty($this->options['expose']['identifier'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$identifier = $this->options['expose']['identifier'];
|
||||
$input = $form_state['values'][$identifier];
|
||||
|
||||
if ($this->options['is_grouped'] && isset($this->options['group_info']['group_items'][$input])) {
|
||||
$this->operator = $this->options['group_info']['group_items'][$input]['operator'];
|
||||
$input = $this->options['group_info']['group_items'][$input]['value'];
|
||||
}
|
||||
|
||||
$values = $this->isMultiValued() ? drupal_explode_tags($input) : array($input);
|
||||
|
||||
if (!$this->options['is_grouped'] || ($this->options['is_grouped'] && ($input != 'All'))) {
|
||||
$this->validated_exposed_input = $this->validate_entity_strings($form[$identifier], $values);
|
||||
}
|
||||
else {
|
||||
$this->validated_exposed_input = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether multiple user names can be entered into this filter.
|
||||
*
|
||||
* This is either the case if the form isn't exposed, or if the " Allow
|
||||
* multiple selections" option is enabled.
|
||||
*
|
||||
* @param array $options
|
||||
* (optional) The options array to use. If not supplied, the options set on
|
||||
* this filter will be used.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if multiple values can be entered for this filter, FALSE otherwise.
|
||||
*/
|
||||
protected function isMultiValued(array $options = array()) {
|
||||
$options = $options ? $options : $this->options;
|
||||
return empty($options['exposed']) || !empty($options['expose']['multiple']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function admin_summary() {
|
||||
$value = $this->value;
|
||||
$this->value = empty($value) ? '' : $this->ids_to_strings($value);
|
||||
$ret = parent::admin_summary();
|
||||
$this->value = $value;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
if ($this->operator === 'empty') {
|
||||
$this->query->condition($this->real_field, NULL, '=', $this->options['group']);
|
||||
}
|
||||
elseif ($this->operator === 'not empty') {
|
||||
$this->query->condition($this->real_field, NULL, '<>', $this->options['group']);
|
||||
}
|
||||
elseif (is_array($this->value)) {
|
||||
$all_of = $this->operator === 'all of';
|
||||
$operator = $all_of ? '=' : $this->operator;
|
||||
if (count($this->value) == 1) {
|
||||
$this->query->condition($this->real_field, reset($this->value), $operator, $this->options['group']);
|
||||
}
|
||||
else {
|
||||
$filter = $this->query->createFilter($operator === '<>' || $all_of ? 'AND' : 'OR');
|
||||
foreach ($this->value as $value) {
|
||||
$filter->condition($this->real_field, $value, $operator);
|
||||
}
|
||||
$this->query->filter($filter, $this->options['group']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,5 +1,10 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains SearchApiViewsHandlerFilterFulltext.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Views filter handler class for handling fulltext fields.
|
||||
*/
|
||||
@@ -33,6 +38,7 @@ class SearchApiViewsHandlerFilterFulltext extends SearchApiViewsHandlerFilterTex
|
||||
$options['operator']['default'] = 'AND';
|
||||
|
||||
$options['mode'] = array('default' => 'keys');
|
||||
$options['min_length'] = array('default' => '');
|
||||
$options['fields'] = array('default' => array());
|
||||
|
||||
return $options;
|
||||
@@ -75,6 +81,55 @@ class SearchApiViewsHandlerFilterFulltext extends SearchApiViewsHandlerFilterTex
|
||||
if (isset($form['expose'])) {
|
||||
$form['expose']['#weight'] = -5;
|
||||
}
|
||||
|
||||
$form['min_length'] = array(
|
||||
'#title' => t('Minimum keyword length'),
|
||||
'#description' => t('Minimum length of each word in the search keys. Leave empty to allow all words.'),
|
||||
'#type' => 'textfield',
|
||||
'#element_validate' => array('element_validate_integer_positive'),
|
||||
'#default_value' => $this->options['min_length'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function exposed_validate(&$form, &$form_state) {
|
||||
// Only validate exposed input.
|
||||
if (empty($this->options['exposed']) || empty($this->options['expose']['identifier'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We only need to validate if there is a minimum word length set.
|
||||
if ($this->options['min_length'] < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
$identifier = $this->options['expose']['identifier'];
|
||||
$input = &$form_state['values'][$identifier];
|
||||
|
||||
if ($this->options['is_grouped'] && isset($this->options['group_info']['group_items'][$input])) {
|
||||
$this->operator = $this->options['group_info']['group_items'][$input]['operator'];
|
||||
$input = &$this->options['group_info']['group_items'][$input]['value'];
|
||||
}
|
||||
|
||||
// If there is no input, we're fine.
|
||||
if (!trim($input)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$words = preg_split('/\s+/', $input);
|
||||
foreach ($words as $i => $word) {
|
||||
if (drupal_strlen($word) < $this->options['min_length']) {
|
||||
unset($words[$i]);
|
||||
}
|
||||
}
|
||||
if (!$words) {
|
||||
$vars['@count'] = $this->options['min_length'];
|
||||
$msg = t('You must include at least one positive keyword with @count characters or more.', $vars);
|
||||
form_error($form[$identifier], $msg);
|
||||
}
|
||||
$input = implode(' ', $words);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,9 +163,9 @@ class SearchApiViewsHandlerFilterFulltext extends SearchApiViewsHandlerFilterTex
|
||||
return;
|
||||
}
|
||||
|
||||
// If the operator was set to OR, set it as the conjunction. (AND is set by
|
||||
// default.)
|
||||
if ($this->operator === 'OR') {
|
||||
// If the operator was set to OR or NOT, set OR as the conjunction. (It is
|
||||
// also set for NOT since otherwise it would be "not all of these words".)
|
||||
if ($this->operator != 'AND') {
|
||||
$this->query->setOption('conjunction', $this->operator);
|
||||
}
|
||||
|
||||
|
@@ -41,6 +41,10 @@ class SearchApiViewsHandlerFilterLanguage extends SearchApiViewsHandlerFilterOpt
|
||||
*/
|
||||
public function query() {
|
||||
global $language_content;
|
||||
|
||||
if (!is_array($this->value)) {
|
||||
$this->value = $this->value ? array($this->value) : array();
|
||||
}
|
||||
foreach ($this->value as $i => $v) {
|
||||
if ($v == 'current') {
|
||||
$this->value[$i] = $language_content->language;
|
||||
|
@@ -1,16 +1,82 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Views filter handler class for handling fields with a limited set of possible
|
||||
* values.
|
||||
*
|
||||
* Definition items:
|
||||
* - options: An array of possible values for this field.
|
||||
* @file
|
||||
* Contains the SearchApiViewsHandlerFilterOptions class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Views filter handler for fields with a limited set of possible values.
|
||||
*/
|
||||
class SearchApiViewsHandlerFilterOptions extends SearchApiViewsHandlerFilter {
|
||||
|
||||
/**
|
||||
* Stores the values which are available on the form.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $value_options = NULL;
|
||||
|
||||
/**
|
||||
* The type of form element used to display the options.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $value_form_type = 'checkboxes';
|
||||
|
||||
/**
|
||||
* Retrieves a wrapper for this filter's field.
|
||||
*
|
||||
* @return EntityMetadataWrapper|null
|
||||
* A wrapper for the field which this filter uses.
|
||||
*/
|
||||
protected function get_wrapper() {
|
||||
if ($this->query) {
|
||||
$index = $this->query->getIndex();
|
||||
}
|
||||
elseif (substr($this->view->base_table, 0, 17) == 'search_api_index_') {
|
||||
$index = search_api_index_load(substr($this->view->base_table, 17));
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
$wrapper = $index->entityWrapper(NULL, TRUE);
|
||||
$parts = explode(':', $this->real_field);
|
||||
foreach ($parts as $i => $part) {
|
||||
if (!isset($wrapper->$part)) {
|
||||
return NULL;
|
||||
}
|
||||
$wrapper = $wrapper->$part;
|
||||
$info = $wrapper->info();
|
||||
if ($i < count($parts) - 1) {
|
||||
// Unwrap lists.
|
||||
$level = search_api_list_nesting_level($info['type']);
|
||||
for ($j = 0; $j < $level; ++$j) {
|
||||
$wrapper = $wrapper[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the value_options property with all possible options.
|
||||
*/
|
||||
protected function get_value_options() {
|
||||
if (isset($this->value_options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$wrapper = $this->get_wrapper();
|
||||
if ($wrapper) {
|
||||
$this->value_options = $wrapper->optionsList('view');
|
||||
}
|
||||
else {
|
||||
$this->value_options = array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a list of options for the operator form.
|
||||
*/
|
||||
@@ -63,13 +129,12 @@ class SearchApiViewsHandlerFilterOptions extends SearchApiViewsHandlerFilter {
|
||||
* Reduce the options according to the selection.
|
||||
*/
|
||||
protected function reduce_value_options() {
|
||||
$options = array();
|
||||
foreach ($this->definition['options'] as $id => $option) {
|
||||
if (isset($this->options['value'][$id])) {
|
||||
$options[$id] = $option;
|
||||
foreach ($this->value_options as $id => $option) {
|
||||
if (!isset($this->options['value'][$id])) {
|
||||
unset($this->value_options[$id]);
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
return $this->value_options;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,27 +157,38 @@ class SearchApiViewsHandlerFilterOptions extends SearchApiViewsHandlerFilter {
|
||||
* Provide a form for setting options.
|
||||
*/
|
||||
public function value_form(&$form, &$form_state) {
|
||||
$options = array();
|
||||
$this->get_value_options();
|
||||
if (!empty($this->options['expose']['reduce']) && !empty($form_state['exposed'])) {
|
||||
$options += $this->reduce_value_options($form_state);
|
||||
$options = $this->reduce_value_options();
|
||||
}
|
||||
else {
|
||||
$options += $this->definition['options'];
|
||||
$options = $this->value_options;
|
||||
}
|
||||
|
||||
$form['value'] = array(
|
||||
'#type' => $this->value_form_type,
|
||||
'#title' => empty($form_state['exposed']) ? t('Value') : '',
|
||||
'#options' => $options,
|
||||
'#multiple' => TRUE,
|
||||
'#size' => min(4, count($this->definition['options'])),
|
||||
'#size' => min(4, count($options)),
|
||||
'#default_value' => is_array($this->value) ? $this->value : array(),
|
||||
);
|
||||
// Hide the value box if operator is 'empty' or 'not empty'.
|
||||
|
||||
// Hide the value box if the operator is 'empty' or 'not empty'.
|
||||
// Radios share the same selector so we have to add some dummy selector.
|
||||
$form['value']['#states']['visible'] = array(
|
||||
':input[name="options[operator]"],dummy-empty' => array('!value' => 'empty'),
|
||||
':input[name="options[operator]"],dummy-not-empty' => array('!value' => 'not empty'),
|
||||
);
|
||||
if (empty($form_state['exposed'])) {
|
||||
$form['value']['#states']['visible'] = array(
|
||||
':input[name="options[operator]"],dummy-empty' => array('!value' => 'empty'),
|
||||
':input[name="options[operator]"],dummy-not-empty' => array('!value' => 'not empty'),
|
||||
);
|
||||
}
|
||||
elseif (!empty($this->options['expose']['use_operator'])) {
|
||||
$name = $this->options['expose']['operator_id'];
|
||||
$form['value']['#states']['visible'] = array(
|
||||
':input[name="' . $name . '"],dummy-empty' => array('!value' => 'empty'),
|
||||
':input[name="' . $name . '"],dummy-not-empty' => array('!value' => 'not empty'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,8 +215,9 @@ class SearchApiViewsHandlerFilterOptions extends SearchApiViewsHandlerFilter {
|
||||
$values = '';
|
||||
|
||||
// Remove every element which is not known.
|
||||
$this->get_value_options();
|
||||
foreach ($this->value as $i => $value) {
|
||||
if (!isset($this->definition['options'][$value])) {
|
||||
if (!isset($this->value_options[$value])) {
|
||||
unset($this->value[$i]);
|
||||
}
|
||||
}
|
||||
@@ -161,7 +238,7 @@ class SearchApiViewsHandlerFilterOptions extends SearchApiViewsHandlerFilter {
|
||||
}
|
||||
// If there is only a single value, use just the plain operator, = or <>.
|
||||
$operator = check_plain($operator);
|
||||
$values = check_plain($this->definition['options'][reset($this->value)]);
|
||||
$values = check_plain($this->value_options[reset($this->value)]);
|
||||
}
|
||||
else {
|
||||
foreach ($this->value as $value) {
|
||||
@@ -172,7 +249,7 @@ class SearchApiViewsHandlerFilterOptions extends SearchApiViewsHandlerFilter {
|
||||
$values .= '…';
|
||||
break;
|
||||
}
|
||||
$values .= check_plain($this->definition['options'][$value]);
|
||||
$values .= check_plain($this->value_options[$value]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,28 +274,24 @@ class SearchApiViewsHandlerFilterOptions extends SearchApiViewsHandlerFilter {
|
||||
$this->value = reset($this->value);
|
||||
}
|
||||
|
||||
// Determine operator and conjunction.
|
||||
// Determine operator and conjunction. The defaults are already right for
|
||||
// "all of".
|
||||
$operator = '=';
|
||||
$conjunction = 'AND';
|
||||
switch ($this->operator) {
|
||||
case '=':
|
||||
$operator = '=';
|
||||
$conjunction = 'OR';
|
||||
break;
|
||||
|
||||
case 'all of':
|
||||
$operator = '=';
|
||||
$conjunction = 'AND';
|
||||
break;
|
||||
|
||||
case '<>':
|
||||
$operator = '<>';
|
||||
$conjunction = 'AND';
|
||||
break;
|
||||
}
|
||||
|
||||
// If the value is an empty array, we either want no filter at all (for
|
||||
// "is none of", or want to find only items with no value for the field.
|
||||
// "is none of"), or want to find only items with no value for the field.
|
||||
if ($this->value === array()) {
|
||||
if ($this->operator != '<>') {
|
||||
if ($operator != '<>') {
|
||||
$this->query->condition($this->real_field, NULL, '=', $this->options['group']);
|
||||
}
|
||||
return;
|
||||
|
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains SearchApiViewsHandlerFilterTaxonomyTerm.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Views filter handler class for taxonomy term entities.
|
||||
*
|
||||
* Based on views_handler_filter_term_node_tid.
|
||||
*/
|
||||
class SearchApiViewsHandlerFilterTaxonomyTerm extends SearchApiViewsHandlerFilterEntity {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function has_extra_options() {
|
||||
return !empty($this->definition['vocabulary']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function option_definition() {
|
||||
$options = parent::option_definition();
|
||||
|
||||
$options['type'] = array('default' => !empty($this->definition['vocabulary']) ? 'textfield' : 'select');
|
||||
$options['hierarchy'] = array('default' => 0);
|
||||
$options['error_message'] = array('default' => TRUE, 'bool' => TRUE);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function extra_options_form(&$form, &$form_state) {
|
||||
$form['type'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Selection type'),
|
||||
'#options' => array('select' => t('Dropdown'), 'textfield' => t('Autocomplete')),
|
||||
'#default_value' => $this->options['type'],
|
||||
);
|
||||
|
||||
$form['hierarchy'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Show hierarchy in dropdown'),
|
||||
'#default_value' => !empty($this->options['hierarchy']),
|
||||
);
|
||||
$form['hierarchy']['#states']['visible'][':input[name="options[type]"]']['value'] = 'select';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function value_form(&$form, &$form_state) {
|
||||
parent::value_form($form, $form_state);
|
||||
|
||||
if (!empty($this->definition['vocabulary'])) {
|
||||
$vocabulary = taxonomy_vocabulary_machine_name_load($this->definition['vocabulary']);
|
||||
$title = t('Select terms from vocabulary @voc', array('@voc' => $vocabulary->name));
|
||||
}
|
||||
else {
|
||||
$vocabulary = FALSE;
|
||||
$title = t('Select terms');
|
||||
}
|
||||
$form['value']['#title'] = $title;
|
||||
|
||||
if ($vocabulary && $this->options['type'] == 'textfield') {
|
||||
$form['value']['#autocomplete_path'] = 'admin/views/ajax/autocomplete/taxonomy/' . $vocabulary->vid;
|
||||
}
|
||||
else {
|
||||
if ($vocabulary && !empty($this->options['hierarchy'])) {
|
||||
$tree = taxonomy_get_tree($vocabulary->vid);
|
||||
$options = array();
|
||||
|
||||
if ($tree) {
|
||||
foreach ($tree as $term) {
|
||||
$choice = new stdClass();
|
||||
$choice->option = array($term->tid => str_repeat('-', $term->depth) . $term->name);
|
||||
$options[] = $choice;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$options = array();
|
||||
$query = db_select('taxonomy_term_data', 'td');
|
||||
$query->innerJoin('taxonomy_vocabulary', 'tv', 'td.vid = tv.vid');
|
||||
$query->fields('td');
|
||||
$query->orderby('tv.weight');
|
||||
$query->orderby('tv.name');
|
||||
$query->orderby('td.weight');
|
||||
$query->orderby('td.name');
|
||||
$query->addTag('term_access');
|
||||
if ($vocabulary) {
|
||||
$query->condition('tv.machine_name', $vocabulary->machine_name);
|
||||
}
|
||||
$result = $query->execute();
|
||||
foreach ($result as $term) {
|
||||
$options[$term->tid] = $term->name;
|
||||
}
|
||||
}
|
||||
|
||||
$default_value = (array) $this->value;
|
||||
|
||||
if (!empty($form_state['exposed'])) {
|
||||
$identifier = $this->options['expose']['identifier'];
|
||||
|
||||
if (!empty($this->options['expose']['reduce'])) {
|
||||
$options = $this->reduce_value_options($options);
|
||||
|
||||
if (!empty($this->options['expose']['multiple']) && empty($this->options['expose']['required'])) {
|
||||
$default_value = array();
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($this->options['expose']['multiple'])) {
|
||||
if (empty($this->options['expose']['required']) && (empty($default_value) || !empty($this->options['expose']['reduce']))) {
|
||||
$default_value = 'All';
|
||||
}
|
||||
elseif (empty($default_value)) {
|
||||
$keys = array_keys($options);
|
||||
$default_value = array_shift($keys);
|
||||
}
|
||||
// Due to #1464174 there is a chance that array('') was saved in the
|
||||
// admin ui. Let's choose a safe default value.
|
||||
elseif ($default_value == array('')) {
|
||||
$default_value = 'All';
|
||||
}
|
||||
else {
|
||||
$copy = $default_value;
|
||||
$default_value = array_shift($copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
$form['value']['#type'] = 'select';
|
||||
$form['value']['#multiple'] = TRUE;
|
||||
$form['value']['#options'] = $options;
|
||||
$form['value']['#size'] = min(9, count($options));
|
||||
$form['value']['#default_value'] = $default_value;
|
||||
|
||||
if (!empty($form_state['exposed']) && isset($identifier) && !isset($form_state['input'][$identifier])) {
|
||||
$form_state['input'][$identifier] = $default_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduces the available exposed options according to the selection.
|
||||
*/
|
||||
protected function reduce_value_options(array $options) {
|
||||
foreach ($options as $id => $option) {
|
||||
if (empty($this->options['value'][$id])) {
|
||||
unset($options[$id]);
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function value_validate($form, &$form_state) {
|
||||
// We only validate if they've chosen the text field style.
|
||||
if ($this->options['type'] != 'textfield') {
|
||||
return;
|
||||
}
|
||||
|
||||
parent::value_validate($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function accept_exposed_input($input) {
|
||||
if (empty($this->options['exposed'])) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// If view is an attachment and is inheriting exposed filters, then assume
|
||||
// exposed input has already been validated.
|
||||
if (!empty($this->view->is_attachment) && $this->view->display_handler->uses_exposed()) {
|
||||
$this->validated_exposed_input = (array) $this->view->exposed_raw_input[$this->options['expose']['identifier']];
|
||||
}
|
||||
|
||||
// If it's non-required and there's no value don't bother filtering.
|
||||
if (!$this->options['expose']['required'] && empty($this->validated_exposed_input)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return parent::accept_exposed_input($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function exposed_validate(&$form, &$form_state) {
|
||||
if (empty($this->options['exposed']) || empty($this->options['expose']['identifier'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We only validate if they've chosen the text field style.
|
||||
if ($this->options['type'] != 'textfield') {
|
||||
$input = $form_state['values'][$this->options['expose']['identifier']];
|
||||
if ($this->options['is_grouped'] && isset($this->options['group_info']['group_items'][$input])) {
|
||||
$input = $this->options['group_info']['group_items'][$input]['value'];
|
||||
}
|
||||
|
||||
if ($input != 'All') {
|
||||
$this->validated_exposed_input = (array) $input;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
parent::exposed_validate($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function validate_entity_strings(array &$form, array $values) {
|
||||
if (empty($values)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$tids = array();
|
||||
$names = array();
|
||||
$missing = array();
|
||||
foreach ($values as $value) {
|
||||
$missing[strtolower($value)] = TRUE;
|
||||
$names[] = $value;
|
||||
}
|
||||
|
||||
if (!$names) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$query = db_select('taxonomy_term_data', 'td');
|
||||
$query->innerJoin('taxonomy_vocabulary', 'tv', 'td.vid = tv.vid');
|
||||
$query->fields('td');
|
||||
$query->condition('td.name', $names);
|
||||
if (!empty($this->definition['vocabulary'])) {
|
||||
$query->condition('tv.machine_name', $this->definition['vocabulary']);
|
||||
}
|
||||
$query->addTag('term_access');
|
||||
$result = $query->execute();
|
||||
foreach ($result as $term) {
|
||||
unset($missing[strtolower($term->name)]);
|
||||
$tids[] = $term->tid;
|
||||
}
|
||||
|
||||
if ($missing) {
|
||||
if (!empty($this->options['error_message'])) {
|
||||
form_error($form, format_plural(count($missing), 'Unable to find term: @terms', 'Unable to find terms: @terms', array('@terms' => implode(', ', array_keys($missing)))));
|
||||
}
|
||||
else {
|
||||
// Add a bogus TID which will show an empty result for a positive filter
|
||||
// and be ignored for an excluding one.
|
||||
$tids[] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $tids;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function expose_form(&$form, &$form_state) {
|
||||
parent::expose_form($form, $form_state);
|
||||
if ($this->options['type'] != 'select') {
|
||||
unset($form['expose']['reduce']);
|
||||
}
|
||||
$form['error_message'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Display error message'),
|
||||
'#description' => t('Display an error message if one of the entered terms could not be found.'),
|
||||
'#default_value' => !empty($this->options['error_message']),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function ids_to_strings(array $ids) {
|
||||
return implode(', ', db_select('taxonomy_term_data', 'td')
|
||||
->fields('td', array('name'))
|
||||
->condition('td.tid', array_filter($ids))
|
||||
->execute()
|
||||
->fetchCol());
|
||||
}
|
||||
|
||||
}
|
@@ -1,5 +1,10 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains SearchApiViewsHandlerFilterText.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Views filter handler class for handling fulltext fields.
|
||||
*/
|
||||
|
77
contrib/search_api_views/includes/handler_filter_user.inc
Normal file
77
contrib/search_api_views/includes/handler_filter_user.inc
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains SearchApiViewsHandlerFilterUser.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Views filter handler class for handling user entities.
|
||||
*
|
||||
* Based on views_handler_filter_user_name.
|
||||
*/
|
||||
class SearchApiViewsHandlerFilterUser extends SearchApiViewsHandlerFilterEntity {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function value_form(&$form, &$form_state) {
|
||||
parent::value_form($form, $form_state);
|
||||
|
||||
// Set autocompletion.
|
||||
$path = $this->isMultiValued() ? 'admin/views/ajax/autocomplete/user' : 'user/autocomplete';
|
||||
$form['value']['#autocomplete_path'] = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function ids_to_strings(array $ids) {
|
||||
$names = array();
|
||||
$args[':uids'] = array_filter($ids);
|
||||
$result = db_query("SELECT uid, name FROM {users} u WHERE uid IN (:uids)", $args);
|
||||
$result = $result->fetchAllKeyed();
|
||||
foreach ($ids as $uid) {
|
||||
if (!$uid) {
|
||||
$names[] = variable_get('anonymous', t('Anonymous'));
|
||||
}
|
||||
elseif (isset($result[$uid])) {
|
||||
$names[] = $result[$uid];
|
||||
}
|
||||
}
|
||||
return implode(', ', $names);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function validate_entity_strings(array &$form, array $values) {
|
||||
$uids = array();
|
||||
$missing = array();
|
||||
foreach ($values as $value) {
|
||||
if (drupal_strtolower($value) === drupal_strtolower(variable_get('anonymous', t('Anonymous')))) {
|
||||
$uids[] = 0;
|
||||
}
|
||||
else {
|
||||
$missing[strtolower($value)] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$missing) {
|
||||
return $uids;
|
||||
}
|
||||
|
||||
$result = db_query("SELECT * FROM {users} WHERE name IN (:names)", array(':names' => array_values($missing)));
|
||||
foreach ($result as $account) {
|
||||
unset($missing[strtolower($account->name)]);
|
||||
$uids[] = $account->uid;
|
||||
}
|
||||
|
||||
if ($missing) {
|
||||
form_error($form, format_plural(count($missing), 'Unable to find user: @users', 'Unable to find users: @users', array('@users' => implode(', ', $missing))));
|
||||
}
|
||||
|
||||
return $uids;
|
||||
}
|
||||
|
||||
}
|
@@ -1,5 +1,10 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains SearchApiViewsHandlerSort.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class for sorting results according to a specified field.
|
||||
*/
|
||||
|
@@ -98,7 +98,7 @@ class SearchApiViewsCache extends views_plugin_cache_time {
|
||||
// other parameters used in the parent method are already reflected in the
|
||||
// Search API query object we use.
|
||||
if (isset($_GET['exposed_info'])) {
|
||||
$key_data[$key] = $_GET[$key];
|
||||
$key_data['exposed_info'] = $_GET['exposed_info'];
|
||||
}
|
||||
|
||||
$this->_results_key = $this->view->name . ':' . $this->display->id . ':results:' . md5(serialize($key_data));
|
||||
|
@@ -1,5 +1,10 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains SearchApiViewsQuery.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Views query class using a Search API index as the data source.
|
||||
*/
|
||||
@@ -180,7 +185,6 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
'#options' => array(),
|
||||
'#default_value' => $this->options['parse_mode'],
|
||||
);
|
||||
$modes = array();
|
||||
foreach ($this->query->parseModes() as $key => $mode) {
|
||||
$form['parse_mode']['#options'][$key] = $mode['name'];
|
||||
if (!empty($mode['description'])) {
|
||||
@@ -243,16 +247,6 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
$view->init_pager();
|
||||
$this->pager->query();
|
||||
|
||||
// Views passes sometimes NULL and sometimes the integer 0 for "All" in a
|
||||
// pager. If set to 0 items, a string "0" is passed. Therefore, we unset
|
||||
// the limit if an empty value OTHER than a string "0" was passed.
|
||||
if (!$this->limit && $this->limit !== '0') {
|
||||
$this->limit = NULL;
|
||||
}
|
||||
// Set the range. (We always set this, as there might even be an offset if
|
||||
// all items are shown.)
|
||||
$this->query->range($this->offset, $this->limit);
|
||||
|
||||
// Set the search ID, if it was not already set.
|
||||
if ($this->query->getOption('search id') == get_class($this->query)) {
|
||||
$this->query->setOption('search id', 'search_api_views:' . $view->name . ':' . $view->current_display);
|
||||
@@ -262,6 +256,20 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
if (!empty($this->options['search_api_bypass_access'])) {
|
||||
$this->query->setOption('search_api_bypass_access', TRUE);
|
||||
}
|
||||
|
||||
// If the View and the Panel conspire to provide an overridden path then
|
||||
// pass that through as the base path.
|
||||
if (!empty($this->view->override_path) && strpos(current_path(), $this->view->override_path) !== 0) {
|
||||
$this->query->setOption('search_api_base_path', $this->view->override_path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function alter(&$view) {
|
||||
parent::alter($view);
|
||||
drupal_alter('search_api_views_query', $view, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,7 +292,28 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the "skip result count" option, if it wasn't already set to
|
||||
// FALSE.
|
||||
$skip_result_count = $this->query->getOption('skip result count', TRUE);
|
||||
if ($skip_result_count) {
|
||||
$skip_result_count = !$this->pager->use_count_query() && empty($view->get_total_rows);
|
||||
$this->query->setOption('skip result count', $skip_result_count);
|
||||
}
|
||||
|
||||
try {
|
||||
// Trigger pager pre_execute().
|
||||
$this->pager->pre_execute($this->query);
|
||||
|
||||
// Views passes sometimes NULL and sometimes the integer 0 for "All" in a
|
||||
// pager. If set to 0 items, a string "0" is passed. Therefore, we unset
|
||||
// the limit if an empty value OTHER than a string "0" was passed.
|
||||
if (!$this->limit && $this->limit !== '0') {
|
||||
$this->limit = NULL;
|
||||
}
|
||||
// Set the range. (We always set this, as there might even be an offset if
|
||||
// all items are shown.)
|
||||
$this->query->range($this->offset, $this->limit);
|
||||
|
||||
$start = microtime(TRUE);
|
||||
|
||||
// Execute the search.
|
||||
@@ -292,11 +321,13 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
$this->search_api_results = $results;
|
||||
|
||||
// Store the results.
|
||||
$this->pager->total_items = $view->total_rows = $results['result count'];
|
||||
if (!empty($this->pager->options['offset'])) {
|
||||
$this->pager->total_items -= $this->pager->options['offset'];
|
||||
if (!$skip_result_count) {
|
||||
$this->pager->total_items = $view->total_rows = $results['result count'];
|
||||
if (!empty($this->pager->options['offset'])) {
|
||||
$this->pager->total_items -= $this->pager->options['offset'];
|
||||
}
|
||||
$this->pager->update_page_info();
|
||||
}
|
||||
$this->pager->update_page_info();
|
||||
$view->result = array();
|
||||
if (!empty($results['results'])) {
|
||||
$this->addResults($results['results'], $view);
|
||||
@@ -304,6 +335,9 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
// We shouldn't use $results['performance']['complete'] here, since
|
||||
// extracting the results probably takes considerable time as well.
|
||||
$view->execute_time = microtime(TRUE) - $start;
|
||||
|
||||
// Trigger pager post_execute().
|
||||
$this->pager->post_execute($view->result);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$this->errors[] = $e->getMessage();
|
||||
@@ -317,8 +351,14 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
*
|
||||
* Used by handlers to flag a fatal error which shouldn't be displayed but
|
||||
* still lead to the view returning empty and the search not being executed.
|
||||
*
|
||||
* @param string|null $msg
|
||||
* Optionally, a translated, unescaped error message to display.
|
||||
*/
|
||||
public function abort() {
|
||||
public function abort($msg = NULL) {
|
||||
if ($msg) {
|
||||
$this->errors[] = $msg;
|
||||
}
|
||||
$this->abort = TRUE;
|
||||
}
|
||||
|
||||
@@ -520,9 +560,9 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
// Query interface methods (proxy to $this->query)
|
||||
//
|
||||
|
||||
public function createFilter($conjunction = 'AND') {
|
||||
public function createFilter($conjunction = 'AND', $tags = array()) {
|
||||
if (!$this->errors) {
|
||||
return $this->query->createFilter($conjunction);
|
||||
return $this->query->createFilter($conjunction, $tags);
|
||||
}
|
||||
}
|
||||
|
||||
|
34
contrib/search_api_views/search_api_views.api.php
Normal file
34
contrib/search_api_views/search_api_views.api.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Search Views module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Alter the query before executing the query.
|
||||
*
|
||||
* @param view $view
|
||||
* The view object about to be processed.
|
||||
* @param SearchApiViewsQuery $query
|
||||
* The Search API Views query to be altered.
|
||||
*
|
||||
* @see hook_views_query_alter()
|
||||
*/
|
||||
function hook_search_api_views_query_alter(view &$view, SearchApiViewsQuery &$query) {
|
||||
// (Example assuming a view with an exposed filter on node title.)
|
||||
// If the input for the title filter is a positive integer, filter against
|
||||
// node ID instead of node title.
|
||||
if ($view->name == 'my_view' && is_numeric($view->exposed_raw_input['title']) && $view->exposed_raw_input['title'] > 0) {
|
||||
// Traverse through the 'where' part of the query.
|
||||
foreach ($query->where as &$condition_group) {
|
||||
foreach ($condition_group['conditions'] as &$condition) {
|
||||
// If this is the part of the query filtering on title, chang the
|
||||
// condition to filter on node ID.
|
||||
if (reset($condition) == 'node.title') {
|
||||
$condition = array('node.nid', $view->exposed_raw_input['title'],'=');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,3 @@
|
||||
|
||||
name = Search views
|
||||
description = Integrates the Search API with Views, enabling users to create views with searches as filters or arguments.
|
||||
dependencies[] = search_api
|
||||
@@ -12,21 +11,25 @@ files[] = includes/handler_argument.inc
|
||||
files[] = includes/handler_argument_fulltext.inc
|
||||
files[] = includes/handler_argument_more_like_this.inc
|
||||
files[] = includes/handler_argument_string.inc
|
||||
files[] = includes/handler_argument_date.inc
|
||||
files[] = includes/handler_argument_taxonomy_term.inc
|
||||
files[] = includes/handler_filter.inc
|
||||
files[] = includes/handler_filter_boolean.inc
|
||||
files[] = includes/handler_filter_date.inc
|
||||
files[] = includes/handler_filter_entity.inc
|
||||
files[] = includes/handler_filter_fulltext.inc
|
||||
files[] = includes/handler_filter_language.inc
|
||||
files[] = includes/handler_filter_options.inc
|
||||
files[] = includes/handler_filter_taxonomy_term.inc
|
||||
files[] = includes/handler_filter_text.inc
|
||||
files[] = includes/handler_filter_user.inc
|
||||
files[] = includes/handler_sort.inc
|
||||
files[] = includes/plugin_cache.inc
|
||||
files[] = includes/query.inc
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-09-01
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2013-12-25
|
||||
version = "7.x-1.11"
|
||||
core = "7.x"
|
||||
project = "search_api"
|
||||
datestamp = "1378025826"
|
||||
datestamp = "1387965506"
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the search_api_views module.
|
||||
@@ -24,7 +25,7 @@ function search_api_views_update_7101() {
|
||||
if (!$table_fields) {
|
||||
return;
|
||||
}
|
||||
foreach (views_get_all_views() as $name => $view) {
|
||||
foreach (views_get_all_views() as $view) {
|
||||
if (empty($view->base_table) || empty($table_fields[$view->base_table])) {
|
||||
continue;
|
||||
}
|
||||
@@ -32,7 +33,7 @@ function search_api_views_update_7101() {
|
||||
$fields = $table_fields[$view->base_table];
|
||||
$change |= _search_api_views_update_7101_helper($view->base_field, $fields);
|
||||
if (!empty($view->display)) {
|
||||
foreach ($view->display as $key => &$display) {
|
||||
foreach ($view->display as &$display) {
|
||||
$options = &$display->display_options;
|
||||
if (isset($options['style_options']['grouping'])) {
|
||||
$change |= _search_api_views_update_7101_helper($options['style_options']['grouping'], $fields);
|
||||
@@ -66,8 +67,15 @@ function search_api_views_update_7101() {
|
||||
/**
|
||||
* Helper function for replacing field identifiers.
|
||||
*
|
||||
* @return
|
||||
* TRUE iff the identifier was changed.
|
||||
* @param $field
|
||||
* Some data to be searched for field names that should be altered. Passed by
|
||||
* reference.
|
||||
* @param array $fields
|
||||
* An array mapping Search API field identifiers (as previously used by Views)
|
||||
* to the new, sanitized Views field identifiers.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if any data was changed, FALSE otherwise.
|
||||
*/
|
||||
function _search_api_views_update_7101_helper(&$field, array $fields) {
|
||||
if (is_array($field)) {
|
||||
|
@@ -1,5 +1,10 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Integrates the Search API with Views.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_views_api().
|
||||
*/
|
||||
@@ -12,7 +17,7 @@ function search_api_views_views_api() {
|
||||
/**
|
||||
* Implements hook_search_api_index_insert().
|
||||
*/
|
||||
function search_api_views_search_api_index_insert(SearchApiIndex $index) {
|
||||
function search_api_views_search_api_index_insert() {
|
||||
// Make the new index available for views.
|
||||
views_invalidate_cache();
|
||||
}
|
||||
|
@@ -1,5 +1,10 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Views hook implementations for the Search API Views module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_views_data().
|
||||
*/
|
||||
@@ -28,7 +33,7 @@ function search_api_views_views_data() {
|
||||
}
|
||||
|
||||
try {
|
||||
$wrapper = $index->entityWrapper(NULL, TRUE);
|
||||
$wrapper = $index->entityWrapper(NULL, FALSE);
|
||||
}
|
||||
catch (EntityMetadataWrapperException $e) {
|
||||
watchdog_exception('search_api_views', $e, "%type while retrieving metadata for index %index: !message in %function (line %line of %file).", array('%index' => $index->name), WATCHDOG_WARNING);
|
||||
@@ -43,6 +48,14 @@ function search_api_views_views_data() {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$wrapper = $index->entityWrapper(NULL);
|
||||
}
|
||||
catch (EntityMetadataWrapperException $e) {
|
||||
watchdog_exception('search_api_views', $e, "%type while retrieving metadata for index %index: !message in %function (line %line of %file).", array('%index' => $index->name), WATCHDOG_WARNING);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add handlers for all indexed fields.
|
||||
foreach ($index->getFields() as $key => $field) {
|
||||
$tmp = $wrapper;
|
||||
@@ -69,7 +82,7 @@ function search_api_views_views_data() {
|
||||
if ($group) {
|
||||
// @todo Entity type label instead of $group?
|
||||
$table[$id]['group'] = $group;
|
||||
$name = t('@field (indexed)', array('@field' => $name));
|
||||
$name = t('!field (indexed)', array('!field' => $name));
|
||||
}
|
||||
$table[$id]['title'] = $name;
|
||||
$table[$id]['help'] = empty($info['description']) ? t('(No information available)') : $info['description'];
|
||||
@@ -145,8 +158,18 @@ function search_api_views_views_data() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that returns an array of handler definitions to add to a
|
||||
* views field definition.
|
||||
* Adds handler definitions for a field to a Views data table definition.
|
||||
*
|
||||
* Helper method for search_api_views_views_data().
|
||||
*
|
||||
* @param $id
|
||||
* The internal identifier of the field.
|
||||
* @param array $field
|
||||
* Information about the field.
|
||||
* @param EntityMetadataWrapper $wrapper
|
||||
* A wrapper providing further metadata about the field.
|
||||
* @param array $table
|
||||
* The existing Views data table definition, as a reference.
|
||||
*/
|
||||
function _search_api_views_add_handlers($id, array $field, EntityMetadataWrapper $wrapper, array &$table) {
|
||||
$type = $field['type'];
|
||||
@@ -170,9 +193,9 @@ function _search_api_views_add_handlers($id, array $field, EntityMetadataWrapper
|
||||
return;
|
||||
}
|
||||
|
||||
if ($options = $wrapper->optionsList('view')) {
|
||||
$info = $wrapper->info();
|
||||
if (isset($info['options list']) && is_callable($info['options list'])) {
|
||||
$table[$id]['filter']['handler'] = 'SearchApiViewsHandlerFilterOptions';
|
||||
$table[$id]['filter']['options'] = $options;
|
||||
$table[$id]['filter']['multi-valued'] = search_api_is_list_type($type);
|
||||
}
|
||||
elseif ($inner_type == 'boolean') {
|
||||
@@ -181,6 +204,27 @@ function _search_api_views_add_handlers($id, array $field, EntityMetadataWrapper
|
||||
elseif ($inner_type == 'date') {
|
||||
$table[$id]['filter']['handler'] = 'SearchApiViewsHandlerFilterDate';
|
||||
}
|
||||
elseif (isset($field['entity_type']) && $field['entity_type'] === 'user') {
|
||||
$table[$id]['filter']['handler'] = 'SearchApiViewsHandlerFilterUser';
|
||||
}
|
||||
elseif (isset($field['entity_type']) && $field['entity_type'] === 'taxonomy_term') {
|
||||
$table[$id]['filter']['handler'] = 'SearchApiViewsHandlerFilterTaxonomyTerm';
|
||||
$info = $wrapper->info();
|
||||
$field_info = field_info_field($info['name']);
|
||||
// For the "Parent terms" and "All parent terms" properties, we can
|
||||
// extrapolate the vocabulary from the parent in the selector. (E.g.,
|
||||
// for "field_tags:parent" we can use the information of "field_tags".)
|
||||
// Otherwise, we can't include any vocabulary information.
|
||||
if (!$field_info && ($info['name'] == 'parent' || $info['name'] == 'parents_all')) {
|
||||
if (!empty($table[$id]['real field'])) {
|
||||
$parts = explode(':', $table[$id]['real field']);
|
||||
$field_info = field_info_field($parts[count($parts) - 2]);
|
||||
}
|
||||
}
|
||||
if (isset($field_info['settings']['allowed_values'][0]['vocabulary'])) {
|
||||
$table[$id]['filter']['vocabulary'] = $field_info['settings']['allowed_values'][0]['vocabulary'];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$table[$id]['filter']['handler'] = 'SearchApiViewsHandlerFilter';
|
||||
}
|
||||
@@ -188,6 +232,9 @@ function _search_api_views_add_handlers($id, array $field, EntityMetadataWrapper
|
||||
if ($inner_type == 'string' || $inner_type == 'uri') {
|
||||
$table[$id]['argument']['handler'] = 'SearchApiViewsHandlerArgumentString';
|
||||
}
|
||||
elseif ($inner_type == 'date') {
|
||||
$table[$id]['argument']['handler'] = 'SearchApiViewsHandlerArgumentDate';
|
||||
}
|
||||
else {
|
||||
$table[$id]['argument']['handler'] = 'SearchApiViewsHandlerArgument';
|
||||
}
|
||||
|
Reference in New Issue
Block a user