security updates of unpatched modules
This commit is contained in:
@@ -1,5 +1,52 @@
|
||||
Search API 1.x, dev (xxxx-xx-xx):
|
||||
---------------------------------
|
||||
Search API 1.20 (2016-07-21):
|
||||
-----------------------------
|
||||
- #2731103 by drunken monkey: Fixed the default value for the taxonomy term
|
||||
filter "multiple" setting.
|
||||
- #1818572 by morningtime, drunken monkey, lodey, guillaumev: Added pretty
|
||||
paths support to the Views facets block.
|
||||
- #2753441 by Johnny vd Laar: Fixed translated field names in
|
||||
language-independent cache.
|
||||
|
||||
Search API 1.19 (2016-07-05):
|
||||
-----------------------------
|
||||
- #2724687 by StefanPr, drunken monkey: Fixed failed sanitization of NULL field
|
||||
values.
|
||||
- #2744189 by nikolabintev, drunken monkey: Fixed highlighting for single-word
|
||||
fields.
|
||||
- #2744995 by John Cook, drunken monkey: Fixed search views without pager.
|
||||
- #2742053 by tunic: Fixed change notification on node access records change.
|
||||
- #2733447 by jsacksick: Fixed translatability of our Views taxonomy term
|
||||
filter.
|
||||
- #2720465 by drunken monkey: Fixed bundle filter's handling of entity types
|
||||
with no bundles on multi-type indexes.
|
||||
- #2710893 by alan-ps, drunken monkey: Fixed creation of comment indexes when
|
||||
no nodes exist.
|
||||
- #2707039 by alan-ps: Fixed indexes of flag entities with "bundles" setting.
|
||||
- #2700879 by drunken monkey: Fixed breadcrumbs on index tabs.
|
||||
- #1889940 by cspurk, Yaron Tal: Fixed "HTML filter" processor to recognize all
|
||||
valid HTML tags.
|
||||
- #2700011 by drunken monkey: Fixed compatibility issues of facets from
|
||||
different indexes.
|
||||
- #2665970 by andrei.colesnic, drunken monkey: Added "Limit list to selected
|
||||
items" exposed option support for Views taxonomy term filters.
|
||||
- #2703675 by drunken monkey, heykarthikwithu: Fixed accidental assumption that
|
||||
all facets are taxonomy terms.
|
||||
- #2419853 by drunken monkey: Fixed HTML filter leaves escaped entities in
|
||||
field values sometimes.
|
||||
|
||||
Search API 1.18 (2016-04-20):
|
||||
-----------------------------
|
||||
- Various security fixes – see https://www.drupal.org/node/2710063.
|
||||
- #2693425 by jojyja: Fixed a typo in search_api.info.
|
||||
|
||||
Search API 1.17 (2016-03-14):
|
||||
-----------------------------
|
||||
- #2665586 by recrit, drunken monkey: Fixed parsing of invalid date facet
|
||||
filters.
|
||||
- #2677900 by stefan.r, drunken monkey: Added the possibility to change date
|
||||
facet formats.
|
||||
- #2678856 by stefan.r, drunken monkey: Fixed date facets showing wrong month
|
||||
on certain days.
|
||||
- #2667872 by Les Lim: Added "0" to field boost options.
|
||||
- #2654328 by drunken monkey, donquixote: Fixed use of "<" and ">" for open
|
||||
facet ranges.
|
||||
|
@@ -61,6 +61,10 @@ class SearchApiFacetapiAdapter extends FacetapiAdapter {
|
||||
public function initActiveFilters($query) {
|
||||
$search_id = $query->getOption('search id');
|
||||
$index_id = $this->info['instance'];
|
||||
// Only act on queries from the right index.
|
||||
if ($index_id != $query->getIndex()->machine_name) {
|
||||
return;
|
||||
}
|
||||
$facets = facetapi_get_enabled_facets($this->info['name']);
|
||||
$this->fields = array();
|
||||
|
||||
@@ -83,13 +87,16 @@ class SearchApiFacetapiAdapter extends FacetapiAdapter {
|
||||
|
||||
if (array_search($search_id, $facet_search_ids) === FALSE) {
|
||||
if (!$default_true) {
|
||||
continue; // We are only to show facets for explicitly named search ids.
|
||||
// We are only to show facets for explicitly named search ids.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
elseif ($default_true) {
|
||||
continue; // The 'facet_search_ids' in the settings are to be excluded.
|
||||
// The 'facet_search_ids' in the settings are to be excluded.
|
||||
continue;
|
||||
}
|
||||
$active[$facet['name']] = $search_id;
|
||||
$facet_key = $facet['name'] . '@' . $this->getSearcher();
|
||||
$active[$facet_key] = $search_id;
|
||||
$this->fields[$facet['name']] = array(
|
||||
'field' => $facet['field'],
|
||||
'limit' => $options['hard_limit'],
|
||||
|
@@ -58,27 +58,36 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
|
||||
$item = end($active);
|
||||
$field = $this->facet['field'];
|
||||
$filter = $this->createRangeFilter($item['value']);
|
||||
$this->addFacetFilter($query, $field, $filter);
|
||||
if ($filter) {
|
||||
$this->addFacetFilter($query, $field, $filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites the handler-specific date range syntax to the normal facet syntax.
|
||||
*
|
||||
* @param $value
|
||||
* @param string $value
|
||||
* The user-facing facet value.
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
* A facet to add as a filter, in the format used internally in this module.
|
||||
* Or NULL if the raw facet in $value is not valid.
|
||||
*/
|
||||
protected function createRangeFilter($value) {
|
||||
// Gets the granularity. Ignore any filters passed directly from the server
|
||||
// (range or missing). We always create filters starting with a year.
|
||||
if (!$value || !ctype_digit($value[0])) {
|
||||
return $value;
|
||||
// Ignore any filters passed directly from the server (range or missing).
|
||||
if (!$value || $value == '!' || (!ctype_digit($value[0]) && preg_match('/^[\[(][^ ]+ [^ ]+[])]$/', $value))) {
|
||||
return $value ? $value : NULL;
|
||||
}
|
||||
|
||||
// Parse into date parts.
|
||||
$parts = $this->parseRangeFilter($value);
|
||||
|
||||
// Return NULL if the date parts are invalid or none were found.
|
||||
if (empty($parts)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$parts = explode('-', $value);
|
||||
$date = new DateTime();
|
||||
switch (count($parts)) {
|
||||
case 1:
|
||||
@@ -140,6 +149,48 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
|
||||
return "[$lower TO $upper]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the date range filter value into parts.
|
||||
*
|
||||
* @param string $value
|
||||
* The user-facing facet value.
|
||||
*
|
||||
* @return int[]|null
|
||||
* An array of date parts, or NULL if an invalid value was provided.
|
||||
*/
|
||||
protected static function parseRangeFilter($value) {
|
||||
$parts = explode('-', $value);
|
||||
|
||||
foreach ($parts as $i => $part) {
|
||||
// Invalidate if part is not an integer.
|
||||
if ($part === '' || !is_numeric($part) || intval($part) != $part) {
|
||||
return NULL;
|
||||
}
|
||||
$parts[$i] = (int) $part;
|
||||
// Depending on the position, negative numbers or 0 are invalid.
|
||||
switch ($i) {
|
||||
case 0:
|
||||
// Years can contain anything – negative values are unlikely, but
|
||||
// technically possible.
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
// Days and months have to be positive.
|
||||
if ($part <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// All others can be 0, but not negative.
|
||||
if ($part < 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replacement callback for replacing ISO dates with timestamps.
|
||||
*
|
||||
@@ -159,10 +210,11 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
|
||||
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']])) {
|
||||
$facet_key = $facet['name'] . '@' . $this->adapter->getSearcher();
|
||||
if (empty($search_ids[$facet_key]) || !search_api_current_search($search_ids[$facet_key])) {
|
||||
return array();
|
||||
}
|
||||
$search_id = $search_ids[$facet['name']];
|
||||
$search_id = $search_ids[$facet_key];
|
||||
$build = array();
|
||||
$search = search_api_current_search($search_id);
|
||||
$results = $search[1];
|
||||
|
@@ -57,21 +57,27 @@ class SearchApiFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTy
|
||||
// When the operator is OR, remove parent terms from the active ones if
|
||||
// children are active. If we don't do this, sending a term and its
|
||||
// parent will produce the same results as just sending the parent.
|
||||
if ($settings['flatten'] == '0') {
|
||||
if (is_callable($this->facet['hierarchy callback']) && !$settings['flatten']) {
|
||||
// Check the filters in reverse order, to avoid checking parents that
|
||||
// will afterwards be removed anyways.
|
||||
foreach (array_reverse(array_keys($active)) as $filter) {
|
||||
$values = array_keys($active);
|
||||
$parents = call_user_func($this->facet['hierarchy callback'], $values);
|
||||
foreach (array_reverse($values) as $filter) {
|
||||
// Skip this filter if it was already removed, or if it is the
|
||||
// "missing value" filter ("!").
|
||||
if (!isset($active[$filter]) || !is_numeric($filter)) {
|
||||
continue;
|
||||
}
|
||||
$parents = taxonomy_get_parents_all($filter);
|
||||
// The return value of taxonomy_get_parents_all() includes the term
|
||||
// itself at index 0. Remove that to only get the term's ancestors.
|
||||
unset($parents[0]);
|
||||
foreach ($parents as $parent) {
|
||||
unset($active[$parent->tid]);
|
||||
// Go through the entire hierarchy of the value and remove all its
|
||||
// ancestors.
|
||||
while (!empty($parents[$filter])) {
|
||||
$ancestor = array_shift($parents[$filter]);
|
||||
if (isset($active[$ancestor])) {
|
||||
unset($active[$ancestor]);
|
||||
if (!empty($parents[$ancestor])) {
|
||||
$parents[$filter] = array_merge($parents[$filter], $parents[$ancestor]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,10 +171,11 @@ class SearchApiFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTy
|
||||
// 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']])) {
|
||||
$facet_key = $facet['name'] . '@' . $this->adapter->getSearcher();
|
||||
if (empty($search_ids[$facet_key]) || !search_api_current_search($search_ids[$facet_key])) {
|
||||
return array();
|
||||
}
|
||||
$search_id = $search_ids[$facet['name']];
|
||||
$search_id = $search_ids[$facet_key];
|
||||
list(, $results) = search_api_current_search($search_id);
|
||||
$build = array();
|
||||
|
||||
|
@@ -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 2016-02-26
|
||||
version = "7.x-1.16+29-dev"
|
||||
; Information added by Drupal.org packaging script on 2016-07-21
|
||||
version = "7.x-1.20"
|
||||
core = "7.x"
|
||||
project = "search_api"
|
||||
datestamp = "1456500713"
|
||||
datestamp = "1469117342"
|
||||
|
||||
|
@@ -5,9 +5,39 @@
|
||||
* Install, update and uninstall functions for the Search facets module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function search_api_facetapi_install() {
|
||||
variable_set('date_format_search_api_facetapi_' . FACETAPI_DATE_YEAR, 'Y');
|
||||
variable_set('date_format_search_api_facetapi_' . FACETAPI_DATE_MONTH, 'F Y');
|
||||
variable_set('date_format_search_api_facetapi_' . FACETAPI_DATE_DAY, 'F j, Y');
|
||||
variable_set('date_format_search_api_facetapi_' . FACETAPI_DATE_HOUR, 'H:__');
|
||||
variable_set('date_format_search_api_facetapi_' . FACETAPI_DATE_MINUTE, 'H:i');
|
||||
variable_set('date_format_search_api_facetapi_' . FACETAPI_DATE_SECOND, 'H:i:S');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function search_api_facetapi_uninstall() {
|
||||
variable_del('search_api_facets_search_ids');
|
||||
}
|
||||
variable_del('date_format_search_api_facetapi_' . FACETAPI_DATE_YEAR);
|
||||
variable_del('date_format_search_api_facetapi_' . FACETAPI_DATE_MONTH);
|
||||
variable_del('date_format_search_api_facetapi_' . FACETAPI_DATE_DAY);
|
||||
variable_del('date_format_search_api_facetapi_' . FACETAPI_DATE_HOUR);
|
||||
variable_del('date_format_search_api_facetapi_' . FACETAPI_DATE_MINUTE);
|
||||
variable_del('date_format_search_api_facetapi_' . FACETAPI_DATE_SECOND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up date formats.
|
||||
*/
|
||||
function search_api_facetapi_update_7101() {
|
||||
variable_set('date_format_search_api_facetapi_' . FACETAPI_DATE_YEAR, 'Y');
|
||||
variable_set('date_format_search_api_facetapi_' . FACETAPI_DATE_MONTH, 'F Y');
|
||||
variable_set('date_format_search_api_facetapi_' . FACETAPI_DATE_DAY, 'F j, Y');
|
||||
variable_set('date_format_search_api_facetapi_' . FACETAPI_DATE_HOUR, 'H:__');
|
||||
variable_set('date_format_search_api_facetapi_' . FACETAPI_DATE_MINUTE, 'H:i');
|
||||
variable_set('date_format_search_api_facetapi_' . FACETAPI_DATE_SECOND, 'H:i:S');
|
||||
}
|
||||
|
@@ -211,6 +211,58 @@ function search_api_facetapi_search_api_query_alter($query) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_date_formats().
|
||||
*/
|
||||
function search_api_facetapi_date_formats() {
|
||||
return array(
|
||||
array(
|
||||
'type' => 'search_api_facetapi_' . FACETAPI_DATE_YEAR,
|
||||
'format' => 'Y',
|
||||
'locales' => array(),
|
||||
),
|
||||
array(
|
||||
'type' => 'search_api_facetapi_' . FACETAPI_DATE_MONTH,
|
||||
'format' => 'F Y',
|
||||
'locales' => array(),
|
||||
),
|
||||
array(
|
||||
'type' => 'search_api_facetapi_' . FACETAPI_DATE_DAY,
|
||||
'format' => 'F j, Y',
|
||||
'locales' => array(),
|
||||
),
|
||||
array(
|
||||
'type' => 'search_api_facetapi_' . FACETAPI_DATE_HOUR,
|
||||
'format' => 'H:__',
|
||||
'locales' => array(),
|
||||
),
|
||||
array(
|
||||
'type' => 'search_api_facetapi_' . FACETAPI_DATE_MINUTE,
|
||||
'format' => 'H:i',
|
||||
'locales' => array(),
|
||||
),
|
||||
array(
|
||||
'type' => 'search_api_facetapi_' . FACETAPI_DATE_SECOND,
|
||||
'format' => 'H:i:s',
|
||||
'locales' => array(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_date_format_types().
|
||||
*/
|
||||
function search_api_facetapi_date_format_types() {
|
||||
return array(
|
||||
'search_api_facetapi_' . FACETAPI_DATE_YEAR => t('Search facets - Years'),
|
||||
'search_api_facetapi_' . FACETAPI_DATE_MONTH => t('Search facets - Months'),
|
||||
'search_api_facetapi_' . FACETAPI_DATE_DAY => t('Search facets - Days'),
|
||||
'search_api_facetapi_' . FACETAPI_DATE_HOUR => t('Search facets - Hours'),
|
||||
'search_api_facetapi_' . FACETAPI_DATE_MINUTE => t('Search facets - Minutes'),
|
||||
'search_api_facetapi_' . FACETAPI_DATE_SECOND => t('Search facets - Seconds'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback for the facet settings page.
|
||||
*/
|
||||
@@ -532,16 +584,12 @@ function search_api_facetapi_map_date(array $values, array $options = array()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// For years, the URL value is already the label.
|
||||
if ($granularity == FACETAPI_DATE_YEAR) {
|
||||
$map[$value] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, parse the timestamp from the known format and format it as a
|
||||
// label.
|
||||
$format = search_api_facetapi_date_get_granularity_format($granularity);
|
||||
$date = DateTime::createFromFormat($format, $value);
|
||||
// Use the "!" modifier to make the date parsing independent of the current
|
||||
// date/time. (See #2678856.)
|
||||
$date = DateTime::createFromFormat('!' . $format, $value);
|
||||
if (!$date) {
|
||||
continue;
|
||||
}
|
||||
@@ -568,17 +616,16 @@ function search_api_facetapi_map_date(array $values, array $options = array()) {
|
||||
*/
|
||||
function search_api_facetapi_format_timestamp($timestamp, $precision = FACETAPI_DATE_YEAR) {
|
||||
$formats = array(
|
||||
FACETAPI_DATE_YEAR => 'Y',
|
||||
FACETAPI_DATE_MONTH => 'F Y',
|
||||
FACETAPI_DATE_DAY => 'F j, Y',
|
||||
FACETAPI_DATE_HOUR => 'H:__',
|
||||
FACETAPI_DATE_MINUTE => 'H:i',
|
||||
FACETAPI_DATE_SECOND => 'H:i:s',
|
||||
FACETAPI_DATE_YEAR,
|
||||
FACETAPI_DATE_MONTH,
|
||||
FACETAPI_DATE_DAY,
|
||||
FACETAPI_DATE_HOUR,
|
||||
FACETAPI_DATE_MINUTE,
|
||||
FACETAPI_DATE_SECOND,
|
||||
);
|
||||
|
||||
if (!isset($formats[$precision])) {
|
||||
if (!in_array($precision, $formats)) {
|
||||
$precision = FACETAPI_DATE_YEAR;
|
||||
}
|
||||
|
||||
return format_date($timestamp, 'custom', $formats[$precision]);
|
||||
return format_date($timestamp, 'search_api_facetapi_' . $precision);
|
||||
}
|
||||
|
@@ -247,6 +247,31 @@ class SearchApiViewsFacetsBlockDisplay extends views_plugin_display_block {
|
||||
),
|
||||
);
|
||||
|
||||
// Override the $variables['#path'] if facetapi_pretty_paths is enabled.
|
||||
if (module_exists('facetapi_pretty_paths')) {
|
||||
// Get the appropriate facet adapter.
|
||||
$adapter = facetapi_adapter_load('search_api@' . $index->machine_name);
|
||||
|
||||
// Get the URL processor and check if it uses pretty paths.
|
||||
$urlProcessor = $adapter->getUrlProcessor();
|
||||
if ($urlProcessor instanceof FacetapiUrlProcessorPrettyPaths) {
|
||||
// Retrieve the pretty path alias from the URL processor.
|
||||
$facet = facetapi_facet_load($facet_field, 'search_api@' . $index->machine_name);
|
||||
$values = array(trim($term['filter'], '"'));
|
||||
|
||||
// Get the pretty path for the facet and remove the current search's
|
||||
// base path from it.
|
||||
$base_path_current = $urlProcessor->getBasePath();
|
||||
$pretty_path = $urlProcessor->getFacetPath($facet, $values, FALSE);
|
||||
$pretty_path = str_replace($base_path_current, '', $pretty_path);
|
||||
|
||||
// Set the new, pretty path for the facet and remove the "f" query
|
||||
// parameter.
|
||||
$variables['path'] = $variables['path'] . $pretty_path;
|
||||
unset($variables['options']['query']['f']);
|
||||
}
|
||||
}
|
||||
|
||||
// Themes the link, adds row to facets.
|
||||
$facets[] = array(
|
||||
'class' => array('leaf'),
|
||||
|
@@ -67,17 +67,6 @@ abstract class SearchApiViewsHandlerFilterEntity extends SearchApiViewsHandlerFi
|
||||
return $operators;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function option_definition() {
|
||||
$options = parent::option_definition();
|
||||
|
||||
$options['expose']['multiple']['default'] = TRUE;
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@@ -27,6 +27,7 @@ class SearchApiViewsHandlerFilterTaxonomyTerm extends SearchApiViewsHandlerFilte
|
||||
|
||||
$options['type'] = array('default' => !empty($this->definition['vocabulary']) ? 'textfield' : 'select');
|
||||
$options['hierarchy'] = array('default' => 0);
|
||||
$options['expose']['contains']['reduce'] = array('default' => FALSE);
|
||||
$options['error_message'] = array('default' => TRUE, 'bool' => TRUE);
|
||||
|
||||
return $options;
|
||||
@@ -72,13 +73,13 @@ class SearchApiViewsHandlerFilterTaxonomyTerm extends SearchApiViewsHandlerFilte
|
||||
}
|
||||
else {
|
||||
if ($vocabulary && !empty($this->options['hierarchy'])) {
|
||||
$tree = taxonomy_get_tree($vocabulary->vid);
|
||||
$tree = taxonomy_get_tree($vocabulary->vid, 0, NULL, TRUE);
|
||||
$options = array();
|
||||
|
||||
if ($tree) {
|
||||
foreach ($tree as $term) {
|
||||
$choice = new stdClass();
|
||||
$choice->option = array($term->tid => str_repeat('-', $term->depth) . $term->name);
|
||||
$choice->option = array($term->tid => str_repeat('-', $term->depth) . check_plain(entity_label('taxonomy_term', $term)));
|
||||
$options[] = $choice;
|
||||
}
|
||||
}
|
||||
@@ -97,8 +98,15 @@ class SearchApiViewsHandlerFilterTaxonomyTerm extends SearchApiViewsHandlerFilte
|
||||
$query->condition('tv.machine_name', $vocabulary->machine_name);
|
||||
}
|
||||
$result = $query->execute();
|
||||
$tids = array();
|
||||
|
||||
foreach ($result as $term) {
|
||||
$options[$term->tid] = $term->name;
|
||||
$tids[] = $term->tid;
|
||||
}
|
||||
$terms = taxonomy_term_load_multiple($tids);
|
||||
|
||||
foreach ($terms as $term) {
|
||||
$options[$term->tid] = check_plain(entity_label('taxonomy_term', $term));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,6 +237,14 @@ class SearchApiViewsHandlerFilterTaxonomyTerm extends SearchApiViewsHandlerFilte
|
||||
parent::exposed_validate($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function expose_options() {
|
||||
parent::expose_options();
|
||||
$this->options['expose']['reduce'] = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -282,15 +298,23 @@ class SearchApiViewsHandlerFilterTaxonomyTerm extends SearchApiViewsHandlerFilte
|
||||
*/
|
||||
public function expose_form(&$form, &$form_state) {
|
||||
parent::expose_form($form, $form_state);
|
||||
if ($this->options['type'] != 'select') {
|
||||
unset($form['expose']['reduce']);
|
||||
|
||||
if ($this->options['type'] == 'select') {
|
||||
$form['expose']['reduce'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Limit list to selected items'),
|
||||
'#description' => t('If checked, the only items presented to the user will be the ones selected here.'),
|
||||
'#default_value' => $this->options['expose']['reduce'],
|
||||
);
|
||||
}
|
||||
else {
|
||||
$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' => $this->options['error_message'],
|
||||
);
|
||||
}
|
||||
$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']),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -344,13 +344,15 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
// 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);
|
||||
$skip_result_count = !$this->pager || (!$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);
|
||||
if ($this->pager) {
|
||||
$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
|
||||
@@ -385,7 +387,9 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
$view->execute_time = microtime(TRUE) - $start;
|
||||
|
||||
// Trigger pager post_execute().
|
||||
$this->pager->post_execute($view->result);
|
||||
if ($this->pager) {
|
||||
$this->pager->post_execute($view->result);
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$this->errors[] = $e->getMessage();
|
||||
@@ -444,7 +448,7 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
|
||||
// Gather any fields from the search results.
|
||||
if (!empty($result['fields'])) {
|
||||
$row['_entity_properties'] += $result['fields'];
|
||||
$row['_entity_properties'] += search_api_get_sanitized_field_values($result['fields']);
|
||||
}
|
||||
|
||||
// Check whether we need to extract any properties from the result item.
|
||||
|
@@ -27,9 +27,9 @@ files[] = includes/handler_sort.inc
|
||||
files[] = includes/plugin_cache.inc
|
||||
files[] = includes/query.inc
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-02-26
|
||||
version = "7.x-1.16+29-dev"
|
||||
; Information added by Drupal.org packaging script on 2016-07-21
|
||||
version = "7.x-1.20"
|
||||
core = "7.x"
|
||||
project = "search_api"
|
||||
datestamp = "1456500713"
|
||||
datestamp = "1469117342"
|
||||
|
||||
|
@@ -34,7 +34,8 @@ class SearchApiAlterBundleFilter extends SearchApiAbstractAlterCallback {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isMultiEntityIndex()) {
|
||||
$multi_entity = $this->isMultiEntityIndex();
|
||||
if ($multi_entity) {
|
||||
$bundle_prop = 'item_bundle';
|
||||
}
|
||||
else {
|
||||
@@ -46,6 +47,10 @@ class SearchApiAlterBundleFilter extends SearchApiAbstractAlterCallback {
|
||||
$default = (bool) $this->options['default'];
|
||||
|
||||
foreach ($items as $id => $item) {
|
||||
// Ignore types that have no bundles.
|
||||
if ($multi_entity && !self::hasBundles(entity_get_info($item->item_type))) {
|
||||
continue;
|
||||
}
|
||||
if (isset($bundles[$item->$bundle_prop]) == $default) {
|
||||
unset($items[$id]);
|
||||
}
|
||||
|
@@ -166,6 +166,10 @@ class SearchApiEntityDataSourceController extends SearchApiAbstractDataSourceCon
|
||||
$bundle_column = 'vid';
|
||||
$bundles = db_query('SELECT vid FROM {taxonomy_vocabulary} WHERE machine_name IN (:bundles)', array(':bundles' => $bundles))->fetchCol();
|
||||
}
|
||||
elseif ($this->entityType == 'flagging') {
|
||||
$bundle_column = 'fid';
|
||||
$bundles = db_query('SELECT fid FROM {flag} WHERE name IN (:bundles)', array(':bundles' => $bundles))->fetchCol();
|
||||
}
|
||||
elseif ($this->entityType == 'comment') {
|
||||
// Comments are significantly more complicated, since they don't
|
||||
// store their bundle explicitly in their database table. Instead,
|
||||
@@ -182,14 +186,17 @@ class SearchApiEntityDataSourceController extends SearchApiAbstractDataSourceCon
|
||||
$bundles = db_query('SELECT nid FROM {node} WHERE type IN (:bundles)', array(':bundles' => $node_types))->fetchCol();
|
||||
}
|
||||
else {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->startTrackingFallback(array($index->machine_name => $index));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$query->condition($bundle_column, $bundles);
|
||||
if ($bundles) {
|
||||
$query->condition($bundle_column, $bundles);
|
||||
}
|
||||
}
|
||||
|
||||
// INSERT ... SELECT ...
|
||||
|
@@ -764,12 +764,14 @@ class SearchApiIndex extends Entity {
|
||||
* "additional fields" key.
|
||||
*/
|
||||
public function getFields($only_indexed = TRUE, $get_additional = FALSE) {
|
||||
global $language;
|
||||
|
||||
$only_indexed = $only_indexed ? 1 : 0;
|
||||
$get_additional = $get_additional ? 1 : 0;
|
||||
|
||||
// First, try the static cache and the persistent cache bin.
|
||||
if (empty($this->fields[$only_indexed][$get_additional])) {
|
||||
$cid = $this->getCacheId() . "-$only_indexed-$get_additional";
|
||||
$cid = $this->getCacheId() . "-$only_indexed-$get_additional-{$language->language}";
|
||||
$cache = cache_get($cid);
|
||||
if ($cache) {
|
||||
$this->fields[$only_indexed][$get_additional] = $cache->data;
|
||||
|
@@ -158,13 +158,14 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
|
||||
if ($this->options['highlight'] != 'never') {
|
||||
$fields = $this->getFulltextFields($response['results'], $id, $fulltext_fields, $this->options['highlight'] == 'always');
|
||||
foreach ($fields as $field => $data) {
|
||||
$result['fields'][$field] = array('#sanitize_callback' => FALSE);
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $i => $text) {
|
||||
$result['fields'][$field][$i] = $this->highlightField($text, $keys);
|
||||
$result['fields'][$field]['#value'][$i] = $this->highlightField($text, $keys);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$result['fields'][$field] = $this->highlightField($data, $keys);
|
||||
$result['fields'][$field]['#value'] = $this->highlightField($data, $keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,9 +201,10 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
|
||||
// We only need detailed fields data if $load is TRUE.
|
||||
$fields = $load ? $this->index->getFields() : array();
|
||||
$needs_extraction = array();
|
||||
$returned_fields = search_api_get_sanitized_field_values(array_intersect_key($result['fields'], array_flip($fulltext_fields)));
|
||||
foreach ($fulltext_fields as $field) {
|
||||
if (array_key_exists($field, $result['fields'])) {
|
||||
$data[$field] = $result['fields'][$field];
|
||||
if (array_key_exists($field, $returned_fields)) {
|
||||
$data[$field] = $returned_fields[$field];
|
||||
}
|
||||
elseif ($load) {
|
||||
$needs_extraction[$field] = $fields[$field];
|
||||
@@ -225,7 +227,7 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
|
||||
}
|
||||
$wrapper = $this->index->entityWrapper($result['entity'], FALSE);
|
||||
$wrapper->language($language->language);
|
||||
$extracted = search_api_extract_fields($wrapper, $needs_extraction);
|
||||
$extracted = search_api_extract_fields($wrapper, $needs_extraction, array('sanitize' => TRUE));
|
||||
|
||||
foreach ($extracted as $field => $info) {
|
||||
if (isset($info['value'])) {
|
||||
@@ -448,12 +450,12 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
|
||||
* @param array $array
|
||||
* The array to flatten.
|
||||
* @param string $glue
|
||||
* The separator to insert between individual array items.
|
||||
* (optional) The separator to insert between individual array items.
|
||||
*
|
||||
* @return string
|
||||
* The glued string.
|
||||
*/
|
||||
protected function flattenArrayValues(array $array, $glue = "\n\n") {
|
||||
protected function flattenArrayValues(array $array, $glue = " \n\n ") {
|
||||
$ret = array();
|
||||
foreach ($array as $item) {
|
||||
if (is_array($item)) {
|
||||
|
@@ -101,7 +101,7 @@ class SearchApiHtmlFilter extends SearchApiAbstractProcessor {
|
||||
$value = $this->parseText($text);
|
||||
}
|
||||
else {
|
||||
$value = strip_tags($text);
|
||||
$value = html_entity_decode(strip_tags($text));
|
||||
// Remove any multiple or leading/trailing spaces we might have introduced.
|
||||
$value = preg_replace('/\s\s+/', ' ', trim($value));
|
||||
}
|
||||
@@ -120,7 +120,7 @@ class SearchApiHtmlFilter extends SearchApiAbstractProcessor {
|
||||
);
|
||||
}
|
||||
$text = substr($text, $pos + 1);
|
||||
if (!preg_match('#^(/?)([-:_a-zA-Z]+)#', $text, $m)) {
|
||||
if (!preg_match('#^(/?)([:_a-zA-Z][-:_a-zA-Z0-9.]*)#', $text, $m)) {
|
||||
continue;
|
||||
}
|
||||
$text = substr($text, strpos($text, '>') + 1);
|
||||
|
@@ -201,7 +201,16 @@ interface SearchApiQueryInterface {
|
||||
* already ready-to-use. This allows search engines (or postprocessors)
|
||||
* to store extracted fields so other modules don't have to extract them
|
||||
* again. This fields should always be checked by modules that want to
|
||||
* use field contents of the result items.
|
||||
* use field contents of the result items. The format of the array is
|
||||
* field IDs (as used by the Search API internally) mapped to either the
|
||||
* raw value of the field (scalar or array value), or an associative
|
||||
* array with the following keys:
|
||||
* - #value: The raw field value.
|
||||
* - #sanitize_callback: The callback to use for sanitizing the field
|
||||
* value for HTML output, or FALSE to state that the field value is
|
||||
* already sanitized.
|
||||
* In the simple form, it's assumed the field value should be sanitized
|
||||
* with check_plain().
|
||||
* - entity: (optional) If set, the fully loaded result item. This field
|
||||
* should always be used by modules using search results, to avoid
|
||||
* duplicate item loads.
|
||||
|
@@ -337,13 +337,6 @@ function search_api_admin_add_server_submit(array $form, array &$form_state) {
|
||||
drupal_set_message(t('The server was successfully created.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Title callback for viewing or editing a server or index.
|
||||
*/
|
||||
function search_api_admin_item_title($object) {
|
||||
return $object->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Page callback: Displays information about a server.
|
||||
*
|
||||
|
@@ -1,5 +1,5 @@
|
||||
name = Search API
|
||||
description = "Provides a generic API for modules offering search capabilites."
|
||||
description = "Provides a generic API for modules offering search capabilities."
|
||||
dependencies[] = entity
|
||||
core = 7.x
|
||||
package = Search
|
||||
@@ -36,9 +36,9 @@ files[] = includes/service.inc
|
||||
|
||||
configure = admin/config/search/search_api
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-02-26
|
||||
version = "7.x-1.16+29-dev"
|
||||
; Information added by Drupal.org packaging script on 2016-07-21
|
||||
version = "7.x-1.20"
|
||||
core = "7.x"
|
||||
project = "search_api"
|
||||
datestamp = "1456500713"
|
||||
datestamp = "1469117342"
|
||||
|
||||
|
@@ -216,10 +216,13 @@ function search_api_hook_info() {
|
||||
'search_api_data_type_info' => $hook_info,
|
||||
'search_api_data_type_info_alter' => $hook_info,
|
||||
'search_api_alter_callback_info' => $hook_info,
|
||||
'search_api_alter_callback_info_alter' => $hook_info,
|
||||
'search_api_processor_info' => $hook_info,
|
||||
'search_api_processor_info_alter' => $hook_info,
|
||||
'search_api_index_items_alter' => $hook_info,
|
||||
'search_api_items_indexed' => $hook_info,
|
||||
'search_api_query_alter' => $hook_info,
|
||||
'search_api_results_alter' => $hook_info,
|
||||
'search_api_server_load' => $hook_info,
|
||||
'search_api_server_insert' => $hook_info,
|
||||
'search_api_server_update' => $hook_info,
|
||||
@@ -920,6 +923,38 @@ function search_api_entity_delete($entity, $type) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_access_records_alter().
|
||||
*
|
||||
* Marks the node as "changed" in indexes that use the "Node access" data
|
||||
* alteration. Also marks the node's comments as changed in indexes that use the
|
||||
* "Comment access" data alteration.
|
||||
*/
|
||||
function search_api_node_access_records_alter(&$grants, $node) {
|
||||
foreach (search_api_index_load_multiple(FALSE) as $index) {
|
||||
$item_ids = array();
|
||||
if (!empty($index->options['data_alter_callbacks']['search_api_alter_node_access']['status'])) {
|
||||
$item_id = $index->datasource()->getItemId($node);
|
||||
if ($item_id !== NULL) {
|
||||
$item_ids = array($item_id);
|
||||
}
|
||||
}
|
||||
elseif (!empty($index->options['data_alter_callbacks']['search_api_alter_comment_access']['status'])) {
|
||||
if (!isset($comments)) {
|
||||
$comments = comment_load_multiple(FALSE, array('nid' => $node->nid));
|
||||
}
|
||||
foreach ($comments as $comment) {
|
||||
$item_ids[] = $index->datasource()->getItemId($comment);
|
||||
}
|
||||
}
|
||||
|
||||
if ($item_ids) {
|
||||
$indexes = array($index->machine_name => $index);
|
||||
search_api_track_item_change_for_indexes($index->item_type, $item_ids, $indexes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_attach_rename_bundle().
|
||||
*
|
||||
@@ -1186,6 +1221,20 @@ function search_api_track_item_change($type, array $item_ids) {
|
||||
if (!$indexes) {
|
||||
return;
|
||||
}
|
||||
search_api_track_item_change_for_indexes($type, $item_ids, $indexes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the items with the specified IDs as "dirty" for the given indexes.
|
||||
*
|
||||
* @param string $type
|
||||
* The item type of the items.
|
||||
* @param array $item_ids
|
||||
* The item IDs.
|
||||
* @param SearchApiIndex[] $indexes
|
||||
* The indexes for which to mark the items as "dirty".
|
||||
*/
|
||||
function search_api_track_item_change_for_indexes($type, array $item_ids, $indexes) {
|
||||
try {
|
||||
$returned_indexes = search_api_get_datasource_controller($type)->trackItemChange($item_ids, $indexes);
|
||||
if (isset($returned_indexes)) {
|
||||
@@ -1207,7 +1256,6 @@ function search_api_track_item_change($type, array $item_ids) {
|
||||
catch (SearchApiException $e) {
|
||||
$vars['%item_type'] = $type;
|
||||
watchdog_exception('search_api', $e, '%type while updating items of type %item_type: !message in %function (line %line of %file).', $vars);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2058,7 +2106,7 @@ function _search_api_query_add_node_access($account, SearchApiQueryInterface $qu
|
||||
}
|
||||
|
||||
// If the user cannot access content/comments at all, return no results.
|
||||
if (!user_access('access content', $account) || ($is_comment && !user_access('access content', $account))) {
|
||||
if (!user_access('access content', $account) || ($is_comment && !user_access('access comments', $account))) {
|
||||
// Simple hack for returning no results.
|
||||
$query->condition('status', 0);
|
||||
$query->condition('status', 1);
|
||||
@@ -2419,6 +2467,13 @@ function search_api_server_url(SearchApiServer $server) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Title callback for viewing or editing a server or index.
|
||||
*/
|
||||
function search_api_admin_item_title($object) {
|
||||
return $object->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Title callback for determining which title should be displayed for the
|
||||
* "delete" local task.
|
||||
@@ -2829,6 +2884,65 @@ function search_api_index_delete($id) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes field values returned from the server.
|
||||
*
|
||||
* @param array $values
|
||||
* The field values, as returned from the server. See
|
||||
* SearchApiQueryInterface::execute() for documentation on the structure.
|
||||
*
|
||||
* @return array
|
||||
* An associative array of field IDs mapped to their sanitized values (scalar
|
||||
* or array-valued).
|
||||
*/
|
||||
function search_api_get_sanitized_field_values(array $values) {
|
||||
// Sanitize the field values returned from the server. Usually we use
|
||||
// check_plain(), but this can be overridden by setting the field value to
|
||||
// an array with "#value" and "#sanitize_callback" keys.
|
||||
foreach ($values as $field_id => $field_value) {
|
||||
if (is_array($field_value)
|
||||
&& isset($field_value['#sanitize_callback'])
|
||||
&& ($field_value['#sanitize_callback'] === FALSE || is_callable($field_value['#sanitize_callback']))
|
||||
&& array_key_exists('#value', $field_value)
|
||||
) {
|
||||
$sanitize_callback = $field_value['#sanitize_callback'];
|
||||
$field_value = $field_value['#value'];
|
||||
}
|
||||
else {
|
||||
$sanitize_callback = 'check_plain';
|
||||
}
|
||||
if ($sanitize_callback !== FALSE) {
|
||||
$field_value = search_api_sanitize_field_value($field_value, $sanitize_callback);
|
||||
}
|
||||
$values[$field_id] = $field_value;
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes the given field value(s).
|
||||
*
|
||||
* @param mixed $field_value
|
||||
* A scalar field value, or an array of field values.
|
||||
* @param callable $sanitize_callback
|
||||
* (optional) The callback to use for sanitizing a scalar value.
|
||||
*
|
||||
* @return mixed
|
||||
* The sanitized field value(s).
|
||||
*/
|
||||
function search_api_sanitize_field_value($field_value, $sanitize_callback = 'check_plain') {
|
||||
if ($field_value === NULL) {
|
||||
return $field_value;
|
||||
}
|
||||
if (is_scalar($field_value)) {
|
||||
return call_user_func($sanitize_callback, $field_value);
|
||||
}
|
||||
foreach ($field_value as &$nested_value) {
|
||||
$nested_value = search_api_sanitize_field_value($nested_value, $sanitize_callback);
|
||||
}
|
||||
return $field_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options list callback for search indexes.
|
||||
*
|
||||
|
@@ -1062,7 +1062,7 @@ class SearchApiUnitTest extends DrupalWebTestCase {
|
||||
protected function checkHtmlFilter() {
|
||||
$orig = <<<END
|
||||
This is <em lang="en" title =
|
||||
"something">a test</em>.
|
||||
"something">a test</em>.<h3>Header</h3>
|
||||
How to write <strong>links to <em>other sites</em></strong>: <a href="URL" title="MOUSEOVER TEXT">TEXT</a>.
|
||||
< signs can be <A HREF="http://example.com/topic/html-escapes" TITLE = 'HTML "escapes"'
|
||||
TARGET = '_blank'>escaped</A> with "&lt;".
|
||||
@@ -1071,6 +1071,7 @@ END;
|
||||
$tags = <<<END
|
||||
em = 1.5
|
||||
strong = 2
|
||||
h3 = 3
|
||||
END;
|
||||
$processed1 = array(
|
||||
array('value' => 'This', 'score' => 1),
|
||||
@@ -1078,6 +1079,7 @@ END;
|
||||
array('value' => 'something', 'score' => 1.5),
|
||||
array('value' => 'a', 'score' => 1.5),
|
||||
array('value' => 'test', 'score' => 1.5),
|
||||
array('value' => 'Header', 'score' => 3),
|
||||
array('value' => 'How', 'score' => 1),
|
||||
array('value' => 'to', 'score' => 1),
|
||||
array('value' => 'write', 'score' => 1),
|
||||
|
@@ -10,9 +10,9 @@ files[] = search_api_test.module
|
||||
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-02-26
|
||||
version = "7.x-1.16+29-dev"
|
||||
; Information added by Drupal.org packaging script on 2016-07-21
|
||||
version = "7.x-1.20"
|
||||
core = "7.x"
|
||||
project = "search_api"
|
||||
datestamp = "1456500713"
|
||||
datestamp = "1469117342"
|
||||
|
||||
|
Reference in New Issue
Block a user