security updates of unpatched modules

This commit is contained in:
Bachir Soussi Chiadmi
2016-10-25 16:23:00 +02:00
parent 610760bedf
commit f6f7fd575f
133 changed files with 5598 additions and 2574 deletions

View File

@@ -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.

View File

@@ -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'],

View File

@@ -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];

View File

@@ -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();

View File

@@ -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"

View File

@@ -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');
}

View File

@@ -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);
}

View File

@@ -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'),

View File

@@ -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}
*/

View File

@@ -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']),
);
}
/**

View File

@@ -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.

View File

@@ -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"

View File

@@ -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]);
}

View File

@@ -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 ...

View File

@@ -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;

View File

@@ -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)) {

View File

@@ -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);

View File

@@ -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.

View File

@@ -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.
*

View File

@@ -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"

View File

@@ -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.
*

View File

@@ -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>: &lt;a href="URL" title="MOUSEOVER TEXT"&gt;TEXT&lt;/a&gt;.
&lt; signs can be <A HREF="http://example.com/topic/html-escapes" TITLE = 'HTML &quot;escapes&quot;'
TARGET = '_blank'>escaped</A> with "&amp;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),

View File

@@ -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"