diff --git a/sites/all/modules/contrib/files/imce/imce/imce.info b/sites/all/modules/contrib/files/imce/imce/imce.info index ac23626d..c812d4fc 100644 --- a/sites/all/modules/contrib/files/imce/imce/imce.info +++ b/sites/all/modules/contrib/files/imce/imce/imce.info @@ -4,9 +4,9 @@ core = "7.x" package = "Media" configure = "admin/config/media/imce" -; Information added by Drupal.org packaging script on 2016-03-30 -version = "7.x-1.10" +; Information added by Drupal.org packaging script on 2017-05-27 +version = "7.x-1.11" core = "7.x" project = "imce" -datestamp = "1459346870" +datestamp = "1495890787" diff --git a/sites/all/modules/contrib/files/imce/imce/imce.install b/sites/all/modules/contrib/files/imce/imce/imce.install index 40ddcd3f..9062e88c 100644 --- a/sites/all/modules/contrib/files/imce/imce/imce.install +++ b/sites/all/modules/contrib/files/imce/imce/imce.install @@ -25,6 +25,7 @@ function imce_uninstall() { variable_del('imce_settings_replace'); variable_del('imce_settings_thumb_method'); variable_del('imce_settings_disable_private'); + variable_del('imce_settings_admin_theme'); variable_del('imce_custom_content'); variable_del('imce_custom_process'); variable_del('imce_custom_init'); diff --git a/sites/all/modules/contrib/files/imce/imce/imce.module b/sites/all/modules/contrib/files/imce/imce/imce.module index f3dc4bf7..e2b6baea 100644 --- a/sites/all/modules/contrib/files/imce/imce/imce.module +++ b/sites/all/modules/contrib/files/imce/imce/imce.module @@ -46,6 +46,20 @@ function imce_menu() { return $items; } +/** + * Implements hook_admin_paths(). + */ +function imce_admin_paths() { + if (variable_get('imce_settings_admin_theme', FALSE)) { + return array( + 'imce' => TRUE, + 'imce/*' => TRUE, + 'file/imce/*' => TRUE, + 'imce-filefield/*' => TRUE, + ); + } +} + /** * Implements hook_permission(). */ diff --git a/sites/all/modules/contrib/files/imce/imce/inc/imce.admin.inc b/sites/all/modules/contrib/files/imce/imce/inc/imce.admin.inc index 587aa474..23529c22 100644 --- a/sites/all/modules/contrib/files/imce/imce/inc/imce.admin.inc +++ b/sites/all/modules/contrib/files/imce/imce/inc/imce.admin.inc @@ -109,6 +109,12 @@ function imce_admin_form($form, &$form_state) { '#default_value' => variable_get('imce_settings_disable_private', 1), '#description' => t('IMCE serves all files under private files directory without applying any access restrictions. This allows anonymous access to any file(/system/files/filename) unless there is a module restricting access to the files. Here you can disable this feature.'), ); + $form['common']['admin_theme'] = array( + '#type' => 'checkbox', + '#title' => t('Use admin theme for IMCE paths'), + '#default_value' => variable_get('imce_settings_admin_theme', FALSE), + '#description' => t('If you have user interface issues with the active theme you may consider switching to admin theme.'), + ); $form['submit'] = array('#type' => 'submit', '#value' => t('Save configuration')); $form['#theme'] = 'imce_admin'; @@ -183,6 +189,7 @@ function imce_admin_submit($form, &$form_state) { variable_set('imce_settings_replace', $form_state['values']['replace']); variable_set('imce_settings_thumb_method', $form_state['values']['thumb_method']); variable_set('imce_settings_disable_private', $form_state['values']['disable_private']); + variable_set('imce_settings_admin_theme', $form_state['values']['admin_theme']); drupal_set_message(t('Changes have been saved.')); } diff --git a/sites/all/modules/contrib/files/imce/imce/js/imce.js b/sites/all/modules/contrib/files/imce/imce/js/imce.js index 49cc1569..17c8f7c6 100644 --- a/sites/all/modules/contrib/files/imce/imce/js/imce.js +++ b/sites/all/modules/contrib/files/imce/imce/js/imce.js @@ -801,12 +801,17 @@ updateUI: function() { if (furl.charAt(furl.length - 1) != '/') { furl = imce.conf.furl = furl + '/'; } - imce.conf.modfix = imce.conf.clean && furl.indexOf(host + '/system/') > -1; + imce.conf.modfix = imce.conf.clean && furl.split('/')[3] === 'system'; if (absurls && !isabs) { imce.conf.furl = baseurl + furl; } else if (!absurls && isabs && furl.indexOf(baseurl) == 0) { - imce.conf.furl = furl.substr(baseurl.length); + furl = furl.substr(baseurl.length); + // Server base url is defined with a port which is missing in current page url. + if (furl.charAt(0) === ':') { + furl = furl.replace(/^:\d*/, ''); + } + imce.conf.furl = furl; } //convert button elements to input elements. imce.convertButtons(imce.FW); diff --git a/sites/all/modules/contrib/search/search_api/CHANGELOG.txt b/sites/all/modules/contrib/search/search_api/CHANGELOG.txt index 0084fe0d..c1e66d96 100644 --- a/sites/all/modules/contrib/search/search_api/CHANGELOG.txt +++ b/sites/all/modules/contrib/search/search_api/CHANGELOG.txt @@ -1,3 +1,83 @@ +Search API 1.26 (2019-03-11): +----------------------------- +- #2324023 by drumm, drunken monkey: Changed Views field definition for to + float. +- #3008849 by pamatt, drunken monkey: Fixed non-exposed numeric and date + filters in Views. +- #3009744 by evgeny.chernyavskiy, drunken monkey: Fixed wrong "continue" in + search_api_server_tasks_check(). +- #3003742 by Jelle_S, drunken monkey: Fixed problems with Views date filters. +- #3002043 by alonaoneill, drunken monkey: Fixed module name capitalization and + dependency namespacing in .info files. +- #2990940 by drunken monkey: Fixed multi-byte handling of Highlight processor. +- #3001424 by drunken monkey: Fixed notice when configuring the More Like This + contextual filter. + +Search API 1.25 (2018-09-17): +----------------------------- +- #2408727 by swim, drunken monkey: Added a batch operation for executing + pending tasks. +- #2325917 by guillaumev, drunken monkey: Added a Views cache plugin based on + Views Content Cache. +- #2989578 by KarlShea, drunken monkey: Fixed Views exposed form fields for + "not between" operator. +- #2982167 by osopolar, drunken monkey: Added a Drush command for re-indexing + specific entities. +- #1783746 by das-peter, sammys, SpadXIII, drunken monkey, ruloweb, KarlShea, + heshanlk, Anas_maw, pinkonomy, Damien Tournoud, rudiedirkx: Added support + for the "(not) between" operator. +- #2408727 by drunken monkey, OliverColeman: Fixed out-of-memory errors when + executing pending tasks. +- Issue #2948820 by capysara, drunken monkey: Added a link to the "need to + reindex" message on the Filters tab. +- #2828883 by JorgenSandstrom, drunken monkey: Fixed property type for + string-typed aggregated fields. +- #2949899 by drunken monkey, DamienMcKenna: Added a warning against using + particular processors with Solr servers to the "Workflow" tab. + +Search API 1.24 (2018-04-05): +----------------------------- +- #2958201 by jcnventura, drunken monkey: Reverted issue #2566529: Added + support for the "Content access" processor for "Multiple types" indexes. + +Search API 1.23 (2018-03-31): +----------------------------- +- #2949562 by DamienMcKenna, drunken monkey: Fixed stemming of multi-word + tokens. +- #1903004 by AndyF, joseph.olstad, drunken monkey: Fixed errors at feature + module installation in certain edge cases. +- #2889989 by kevineinarsson, drunken monkey, kristofferwiklund: Fixed + highlighting for text with multi-byte characters. +- #1393064 by xlyz, drunken monkey, jannis: Fixed handling of empty facet + filters. +- #2927692 by drunken monkey, Kristi Wachter: Fixed exposed grouped Views + options filters. +- #2928769 by jannis, drunken monkey: Fixed Views cache not being cleared when + enabling indexes. +- #2566529 by Dylan Donkersgoed, drunken monkey, joachim, swirt: Added support + for the "Content access" processor for "Multiple types" indexes. +- #2905445 by ciss, drunken monkey: Fixed error handling in Views term filter + handler. +- #2904268 by pobster, drunken monkey: Added support for language hierarchy in + Views. + +Search API 1.22 (2017-07-18): +----------------------------- +- #1710212 by drunken monkey: Added a data alteration for indexing a user's + content. +- #2879892 by blacklabel_tom, drunken monkey: Fixed link in description of + "Stemmer" processor. +- #2788593 by drunken monkey: Fixed error in Views query settings for specific + setups. +- #2749963 by drunken monkey: Fixed "Index hierarchy" not having values + numerically indexed. +- #2875793 by drunken monkey: Fixed buggy error handling in Views. +- #2860624 by drunken monkey: Fixed problem with empty words in Views fulltext + filter. +- #2855447 by mparker17, drunken monkey: Added "Separator" option for + aggregated fields of type "Fulltext". +- #2863445 by dbjpanda, drunken monkey: Fixed phrasing in README.txt. + Search API 1.21 (2017-02-23): ----------------------------- - #2780341 by Berdir: Fixed passing of custom ranges to date facets. diff --git a/sites/all/modules/contrib/search/search_api/README.txt b/sites/all/modules/contrib/search/search_api/README.txt index 3943ceec..a6264e97 100644 --- a/sites/all/modules/contrib/search/search_api/README.txt +++ b/sites/all/modules/contrib/search/search_api/README.txt @@ -31,9 +31,9 @@ Terms as used in this module. Sphinx or any other professional or simple indexing mechanism. Takes care of the details of all operations, especially indexing or searching content. - Server: - One specific place for indexing data, using a set service class. Can - e.g. be some tables in a database, a connection to a Solr server or other - external services, etc. + One specific place for indexing data, using a specific service class. For + example this could be some tables in a database, a connection to a Solr server + or other external services, etc. - Index: A configuration object for indexing data of a specific type. What and how data is indexed is determined by its settings. Also keeps track of which items diff --git a/sites/all/modules/contrib/search/search_api/contrib/search_api_facetapi/plugins/facetapi/query_type_term.inc b/sites/all/modules/contrib/search/search_api/contrib/search_api_facetapi/plugins/facetapi/query_type_term.inc index 64d797a7..4c674a8a 100644 --- a/sites/all/modules/contrib/search/search_api/contrib/search_api_facetapi/plugins/facetapi/query_type_term.inc +++ b/sites/all/modules/contrib/search/search_api/contrib/search_api_facetapi/plugins/facetapi/query_type_term.inc @@ -115,7 +115,7 @@ class SearchApiFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTy if ($filter == '!') { $query_filter->condition($field, NULL, $exclude ? '<>' : '='); } - elseif ($filter[0] == '[' && $filter[strlen($filter) - 1] == ']' && ($pos = strpos($filter, ' TO '))) { + elseif ($filter && $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 == '*') { diff --git a/sites/all/modules/contrib/search/search_api/contrib/search_api_facetapi/search_api_facetapi.info b/sites/all/modules/contrib/search/search_api/contrib/search_api_facetapi/search_api_facetapi.info index 022eefc0..904a64dc 100644 --- a/sites/all/modules/contrib/search/search_api/contrib/search_api_facetapi/search_api_facetapi.info +++ b/sites/all/modules/contrib/search/search_api/contrib/search_api_facetapi/search_api_facetapi.info @@ -1,7 +1,7 @@ -name = Search facets +name = Search Facets description = "Integrate the Search API with the Facet API to provide facetted searches." -dependencies[] = search_api -dependencies[] = facetapi +dependencies[] = search_api:search_api +dependencies[] = facetapi:facetapi core = 7.x package = Search @@ -9,9 +9,8 @@ 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 2017-02-23 -version = "7.x-1.21" +; Information added by Drupal.org packaging script on 2019-03-11 +version = "7.x-1.26" core = "7.x" project = "search_api" -datestamp = "1487844493" - +datestamp = "1552334832" diff --git a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/README.txt b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/README.txt index b36a7b55..50cfce1c 100644 --- a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/README.txt +++ b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/README.txt @@ -40,6 +40,21 @@ in that position. If the query is sorted in this way, then the random sort, as an associative array with any of the following keys: - seed: A numeric seed value to use for the random sort. +"BETWEEN operator" feature +-------------------------- +This module defines the "BETWEEN operator" feature (feature key: +"search_api_between") that adds the "BETWEEN" and "NOT BETWEEN" filter +operators to search queries. If your search server supports this feature, you +can use the "Is between" and "Is not between" operators when adding Views +filters for numeric, string or date types. + +For developers: +A service class that wants to support this feature has to accept "BETWEEN" and +"NOT BETWEEN" as additional $operator values in query conditions. The value in +both cases is an array with the keys 0 and 1, with the value under key 0 being +the lower and the value under key 1 being the upper bound for the range in which +the field's value should ("BETWEEN") or should not ("NOT BETWEEN") be. + "Facets block" display ---------------------- Most features should be clear to users of Views. However, the module also diff --git a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_argument.inc b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_argument.inc index a11a662b..a31738ee 100644 --- a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_argument.inc +++ b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_argument.inc @@ -79,8 +79,8 @@ class SearchApiViewsHandlerArgument extends views_handler_argument { public function option_definition() { $options = parent::option_definition(); - $options['break_phrase'] = array('default' => FALSE); - $options['not'] = array('default' => FALSE); + $options['break_phrase'] = array('default' => FALSE, 'bool' => TRUE); + $options['not'] = array('default' => FALSE, 'bool' => TRUE); return $options; } diff --git a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_argument_more_like_this.inc b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_argument_more_like_this.inc index df526e4b..bfb20393 100644 --- a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_argument_more_like_this.inc +++ b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_argument_more_like_this.inc @@ -16,8 +16,6 @@ class SearchApiViewsHandlerArgumentMoreLikeThis extends SearchApiViewsHandlerArg */ public function option_definition() { $options = parent::option_definition(); - unset($options['break_phrase']); - unset($options['not']); $options['entity_type'] = array('default' => FALSE); $options['fields'] = array('default' => array()); return $options; diff --git a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_date.inc b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_date.inc index c7897245..c921e9f5 100644 --- a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_date.inc +++ b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_date.inc @@ -6,9 +6,9 @@ */ /** - * Views filter handler base class for handling all "normal" cases. + * Views filter handler base class for handling date fields. */ -class SearchApiViewsHandlerFilterDate extends SearchApiViewsHandlerFilter { +class SearchApiViewsHandlerFilterDate extends SearchApiViewsHandlerFilterNumeric { /** * Add a "widget type" option. @@ -88,9 +88,22 @@ class SearchApiViewsHandlerFilterDate extends SearchApiViewsHandlerFilter { public function value_form(&$form, &$form_state) { parent::value_form($form, $form_state); + $is_date_popup = ($this->options['widget_type'] == 'date_popup' && module_exists('date_popup')); + + // If the operator is between + if ($this->operator == 'between') { + if ($is_date_popup) { + $form['value']['min']['#type'] = 'date_popup'; + $form['value']['min']['#date_format'] = $this->options['date_popup_format']; + $form['value']['min']['#date_year_range'] = $this->options['year_range']; + $form['value']['max']['#type'] = 'date_popup'; + $form['value']['max']['#date_format'] = $this->options['date_popup_format']; + $form['value']['max']['#date_year_range'] = $this->options['year_range']; + } + } // If we are using the date popup widget, overwrite the settings of the form // according to what date_popup expects. - if ($this->options['widget_type'] == 'date_popup' && module_exists('date_popup')) { + elseif ($is_date_popup) { $form['value']['#type'] = 'date_popup'; $form['value']['#date_format'] = $this->options['date_popup_format']; $form['value']['#date_year_range'] = $this->options['year_range']; @@ -109,17 +122,38 @@ class SearchApiViewsHandlerFilterDate extends SearchApiViewsHandlerFilter { * Add this filter to the query. */ public function query() { + $this->normalizeValue(); + 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']); } - else { - while (is_array($this->value)) { - $this->value = $this->value ? reset($this->value) : NULL; + elseif (in_array($this->operator, array('between', 'not between'), TRUE)) { + $min = $this->value['min']; + if ($min !== '') { + $min = is_numeric($min) ? $min : strtotime($min, REQUEST_TIME); } - $v = is_numeric($this->value) ? $this->value : strtotime($this->value, REQUEST_TIME); + $max = $this->value['max']; + if ($max !== '') { + $max = is_numeric($max) ? $max : strtotime($max, REQUEST_TIME); + } + + if (is_numeric($min) && is_numeric($max)) { + $this->query->condition($this->real_field, array($min, $max), strtoupper($this->operator), $this->options['group']); + } + elseif (is_numeric($min)) { + $operator = $this->operator === 'between' ? '>=' : '<'; + $this->query->condition($this->real_field, $min, $operator, $this->options['group']); + } + elseif (is_numeric($max)) { + $operator = $this->operator === 'between' ? '<=' : '>'; + $this->query->condition($this->real_field, $min, $operator, $this->options['group']); + } + } + else { + $v = is_numeric($this->value['value']) ? $this->value['value'] : strtotime($this->value['value'], REQUEST_TIME); if ($v !== FALSE) { $this->query->condition($this->real_field, $v, $this->operator, $this->options['group']); } diff --git a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_fulltext.inc b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_fulltext.inc index 320adc36..dcb3e263 100644 --- a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_fulltext.inc +++ b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_fulltext.inc @@ -121,6 +121,11 @@ class SearchApiViewsHandlerFilterFulltext extends SearchApiViewsHandlerFilterTex $words = preg_split('/\s+/', $input); $quoted = FALSE; foreach ($words as $i => $word) { + $word_length = drupal_strlen($word); + if (!$word_length) { + unset($words[$i]); + continue; + } // Protect quoted strings. if ($quoted && $word[strlen($word) - 1] === '"') { $quoted = FALSE; @@ -130,7 +135,7 @@ class SearchApiViewsHandlerFilterFulltext extends SearchApiViewsHandlerFilterTex $quoted = TRUE; continue; } - if (drupal_strlen($word) < $this->options['min_length']) { + if ($word_length < $this->options['min_length']) { unset($words[$i]); } } diff --git a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_language.inc b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_language.inc index a7de5f94..3202cdbc 100644 --- a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_language.inc +++ b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_language.inc @@ -18,10 +18,13 @@ class SearchApiViewsHandlerFilterLanguage extends SearchApiViewsHandlerFilterOpt */ protected function get_value_options() { parent::get_value_options(); - $this->value_options = array( - 'current' => t("Current user's language"), - 'default' => t('Default site language'), - ) + $this->value_options; + $options = array(); + if (module_exists('language_hierarchy')) { + $options['fallback'] = t("Current user's language with fallback"); + } + $options['current'] = t("Current user's language"); + $options['default'] = t('Default site language'); + $this->value_options = $options + $this->value_options; } /** @@ -40,6 +43,11 @@ class SearchApiViewsHandlerFilterLanguage extends SearchApiViewsHandlerFilterOpt elseif ($v == 'default') { $this->value[$i] = language_default('language'); } + elseif ($v == 'fallback' && module_exists('language_hierarchy')) { + $fallbacks = array($language_content->language => $language_content->language); + $fallbacks += array_keys(language_hierarchy_get_ancestors($language_content->language)); + $this->value[$i] = drupal_map_assoc($fallbacks); + } } parent::query(); } diff --git a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_numeric.inc b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_numeric.inc new file mode 100644 index 00000000..b4c8ec19 --- /dev/null +++ b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_numeric.inc @@ -0,0 +1,248 @@ +normalizeValue(); + } + + /** + * {@inheritdoc} + */ + public function option_definition() { + $options = parent::option_definition(); + $options['value'] = array( + 'contains' => array( + 'value' => array('default' => ''), + 'min' => array('default' => ''), + 'max' => array('default' => ''), + ), + ); + + return $options; + } + + /** + * {@inheritdoc} + */ + public function operator_options() { + $operators = parent::operator_options(); + + $index = search_api_index_load(substr($this->table, 17)); + $server = NULL; + try { + if ($index) { + $server = $index->server(); + } + } + catch (SearchApiException $e) { + // Ignore. + } + if ($server && $server->supportsFeature('search_api_between')) { + $operators += array( + 'between' => t('Is between'), + 'not between' => t('Is not between'), + ); + } + + return $operators; + } + + /** + * Provides a form for setting the filter value. + * + * Heavily borrowed from views_handler_filter_numeric. + * + * @see views_handler_filter_numeric::value_form() + */ + public function value_form(&$form, &$form_state) { + $form['value']['#tree'] = TRUE; + + $single_field_operators = $this->operator_options(); + unset( + $single_field_operators['empty'], + $single_field_operators['not empty'], + $single_field_operators['between'], + $single_field_operators['not between'] + ); + $between_operators = array('between', 'not between'); + + // We have to make some choices when creating this as an exposed + // filter form. For example, if the operator is locked and thus + // not rendered, we can't render dependencies; instead we only + // render the form items we need. + $which = 'all'; + $source = NULL; + if (!empty($form['operator'])) { + $source = ($form['operator']['#type'] == 'radios') ? 'radio:options[operator]' : 'edit-options-operator'; + } + + $identifier = NULL; + if (!empty($form_state['exposed'])) { + $identifier = $this->options['expose']['identifier']; + if (empty($this->options['expose']['use_operator']) || empty($this->options['expose']['operator_id'])) { + // Exposed and locked. + $which = in_array($this->operator, $between_operators) ? 'minmax' : 'value'; + } + else { + $source = 'edit-' . drupal_html_id($this->options['expose']['operator_id']); + } + } + + // 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. + if ($which == 'all') { + $form['value']['value'] = array( + '#type' => 'textfield', + '#title' => empty($form_state['exposed']) ? t('Value') : '', + '#size' => 30, + '#default_value' => $this->value['value'], + '#dependency' => array($source => array_keys($single_field_operators)), + ); + if ($identifier && !isset($form_state['input'][$identifier]['value'])) { + $form_state['input'][$identifier]['value'] = $this->value['value']; + } + } + elseif ($which == 'value') { + // When exposed we drop the value-value and just do value if + // the operator is locked. + $form['value'] = array( + '#type' => 'textfield', + '#title' => empty($form_state['exposed']) ? t('Value') : '', + '#size' => 30, + '#default_value' => isset($this->value['value']) ? $this->value['value'] : '', + ); + if ($identifier && !isset($form_state['input'][$identifier])) { + $form_state['input'][$identifier] = isset($this->value['value']) ? $this->value['value'] : ''; + } + } + + if ($which == 'all' || $which == 'minmax') { + $form['value']['min'] = array( + '#type' => 'textfield', + '#title' => empty($form_state['exposed']) ? t('Min') : '', + '#size' => 30, + '#default_value' => $this->value['min'], + ); + $form['value']['max'] = array( + '#type' => 'textfield', + '#title' => empty($form_state['exposed']) ? t('And max') : t('And'), + '#size' => 30, + '#default_value' => $this->value['max'], + ); + + if ($which == 'all') { + $form['value']['min']['#dependency'] = array($source => $between_operators); + $form['value']['max']['#dependency'] = array($source => $between_operators); + } + + if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier]['min'])) { + $form_state['input'][$identifier]['min'] = $this->value['min']; + } + if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier]['max'])) { + $form_state['input'][$identifier]['max'] = $this->value['max']; + } + + if (!isset($form['value']['value'])) { + // Ensure there is something in the 'value'. + $form['value']['value'] = array( + '#type' => 'value', + '#value' => NULL, + ); + } + } + } + + /** + * {@inheritdoc} + */ + public function admin_summary() { + if (!empty($this->options['exposed'])) { + return t('exposed'); + } + + if ($this->operator === 'empty') { + return t('is empty'); + } + if ($this->operator === 'not empty') { + return t('is not empty'); + } + + if (in_array($this->operator, array('between', 'not between'), TRUE)) { + // This is of course wrong for translation purposes, but copied from + // views_handler_filter_numeric::admin_summary() so probably still better + // to re-use this than to do it correctly. + $operator = $this->operator === 'between' ? t('between') : t('not between'); + $vars = array( + '@min' => (string) $this->value['min'], + '@max' => (string) $this->value['max'], + ); + return $operator . ' ' . t('@min and @max', $vars); + } + + return check_plain((string) $this->operator) . ' ' . check_plain((string) $this->value['value']); + } + + /** + * {@inheritdoc} + */ + public function query() { + $this->normalizeValue(); + + if (in_array($this->operator, array('between', 'not between'), TRUE)) { + $min = $this->value['min']; + $max = $this->value['max']; + if ($min !== '' && $max !== '') { + $this->query->condition($this->real_field, array($min, $max), strtoupper($this->operator), $this->options['group']); + } + elseif ($min !== '') { + $operator = $this->operator === 'between' ? '>=' : '<'; + $this->query->condition($this->real_field, $min, $operator, $this->options['group']); + } + elseif ($max !== '') { + $operator = $this->operator === 'between' ? '<=' : '>'; + $this->query->condition($this->real_field, $min, $operator, $this->options['group']); + } + } + else { + // The parent handler doesn't expect our value structure, just pass the + // scalar value instead. + $this->value = $this->value['value']; + parent::query(); + } + } + + /** + * Sets $this->value to an array of options as defined by the filter. + * + * @see SearchApiViewsHandlerFilterNumeric::option_definition() + */ + protected function normalizeValue() { + $value = $this->value; + if (is_array($value) && isset($value[0])) { + $value = $value[0]; + } + if (!is_array($value)) { + $value = array('value' => $value); + } + $this->value = array( + 'value' => isset($value['value']) ? $value['value'] : '', + 'min' => isset($value['min']) ? $value['min'] : '', + 'max' => isset($value['max']) ? $value['max'] : '', + ); + } + +} diff --git a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_options.inc b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_options.inc index c63c07e7..2184fc85 100644 --- a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_options.inc +++ b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_options.inc @@ -121,6 +121,7 @@ class SearchApiViewsHandlerFilterOptions extends SearchApiViewsHandlerFilter { */ public function option_definition() { $options = parent::option_definition(); + $options['value'] = array('default' => ''); $options['expose']['contains']['reduce'] = array('default' => FALSE); return $options; } @@ -256,6 +257,32 @@ class SearchApiViewsHandlerFilterOptions extends SearchApiViewsHandlerFilter { return $operator . (($values !== '') ? ' ' . $values : ''); } + /** + * {@inheritdoc} + */ + function accept_exposed_input($input) { + $accepted = parent::accept_exposed_input($input); + + // Grouped filters will have the raw form values structure from the + // checkboxes as the value here. Convert that into the correct array of + // values instead. + if ($accepted && is_array($this->value) && $this->is_a_group()) { + // For some reason, Views thinks it's a good idea to nest the form values + // into a second array in some cases. That one will be numerically indexed + // with just a single entry, though, so it should be relatively easy to + // spot. + if (count($this->value) && isset($this->value[0])) { + $this->value = reset($this->value); + } + $this->value = array_keys(array_filter($this->value)); + if (!$this->value) { + return FALSE; + } + } + + return $accepted; + } + /** * Add this filter to the query. */ diff --git a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_taxonomy_term.inc b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_taxonomy_term.inc index f3317cfa..51982b0e 100644 --- a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_taxonomy_term.inc +++ b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/handler_filter_taxonomy_term.inc @@ -321,9 +321,13 @@ class SearchApiViewsHandlerFilterTaxonomyTerm extends SearchApiViewsHandlerFilte * {@inheritdoc} */ protected function ids_to_strings(array $ids) { + $ids = array_filter($ids); + if (!$ids) { + return ''; + } return implode(', ', db_select('taxonomy_term_data', 'td') ->fields('td', array('name')) - ->condition('td.tid', array_filter($ids)) + ->condition('td.tid', $ids) ->execute() ->fetchCol()); } diff --git a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/plugin_cache.inc b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/plugin_cache.inc index c6bd41d4..c63aed5e 100644 --- a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/plugin_cache.inc +++ b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/plugin_cache.inc @@ -35,11 +35,16 @@ class SearchApiViewsCache extends views_plugin_cache_time { } $cid = $this->get_results_key(); + $results = NULL; + $query_plugin = $this->view->query; + if ($query_plugin instanceof SearchApiViewsQuery) { + $results = $query_plugin->getSearchApiResults(); + } $data = array( 'result' => $this->view->result, 'total_rows' => isset($this->view->total_rows) ? $this->view->total_rows : 0, 'current_page' => $this->view->get_current_page(), - 'search_api results' => $this->view->query->getSearchApiResults(), + 'search_api results' => $results, ); cache_set($cid, $data, $this->table, $this->cache_set_expire($type)); } @@ -80,7 +85,7 @@ class SearchApiViewsCache extends views_plugin_cache_time { * Overrides views_plugin_cache::get_cache_key(). * * Use the Search API query as the main source for the key. Note that in - * Views < 3.8, this function does not exist. + * Views < 3.8, this method does not exist. */ public function get_cache_key($key_data = array()) { global $user; @@ -121,7 +126,7 @@ class SearchApiViewsCache extends views_plugin_cache_time { } /** - * Get the Search API query object associated with the current view. + * Retrieves the Search API query object associated with the current view. * * @return SearchApiQueryInterface|null * The Search API query object associated with the current view; or NULL if diff --git a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/plugin_content_cache.inc b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/plugin_content_cache.inc new file mode 100644 index 00000000..555fe89a --- /dev/null +++ b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/plugin_content_cache.inc @@ -0,0 +1,146 @@ +get_results_key(); + $results = NULL; + $query_plugin = $this->view->query; + if ($query_plugin instanceof SearchApiViewsQuery) { + $results = $query_plugin->getSearchApiResults(); + } + $data = array( + 'result' => $this->view->result, + 'total_rows' => isset($this->view->total_rows) ? $this->view->total_rows : 0, + 'current_page' => $this->view->get_current_page(), + 'search_api results' => $results, + ); + cache_set($cid, $data, $this->table, $this->cache_set_expire($type)); + } + + /** + * Overrides views_plugin_cache::cache_get(). + * + * Additionally stores successfully retrieved results with + * search_api_current_search(). + */ + public function cache_get($type) { + if ($type != 'results') { + return parent::cache_get($type); + } + + // Values to set: $view->result, $view->total_rows, $view->execute_time, + // $view->current_page. + if ($cache = cache_get($this->get_results_key(), $this->table)) { + $cutoff = $this->cache_expire($type); + if (!$cutoff || $cache->created > $cutoff) { + $this->view->result = $cache->data['result']; + $this->view->total_rows = $cache->data['total_rows']; + $this->view->set_current_page($cache->data['current_page']); + $this->view->execute_time = 0; + + // Trick Search API into believing a search happened, to make facetting + // et al. work. + $query = $this->getSearchApiQuery(); + search_api_current_search($query->getOption('search id'), $query, $cache->data['search_api results']); + + return TRUE; + } + } + return FALSE; + } + + /** + * Overrides views_plugin_cache::get_cache_key(). + * + * Use the Search API query as the main source for the key. Note that in + * Views < 3.8, this method does not exist. + */ + public function get_cache_key($key_data = array()) { + global $user; + + if (!isset($this->_results_key)) { + $query = $this->getSearchApiQuery(); + $query->preExecute(); + $key_data += array( + 'query' => $query, + 'roles' => array_keys($user->roles), + 'super-user' => $user->uid == 1, // special caching for super user. + 'language' => $GLOBALS['language']->language, + 'base_url' => $GLOBALS['base_url'], + 'offset' => $this->view->get_current_page() . '*' . $this->view->get_items_per_page() . '+' . $this->view->get_offset(), + ); + // Not sure what gets passed in exposed_info, so better include it. All + // 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['exposed_info'] = $_GET['exposed_info']; + } + } + $key = drupal_hash_base64(serialize($key_data)); + return $key; + } + + /** + * Overrides views_plugin_cache::get_results_key(). + * + * This is unnecessary for Views >= 3.8. + */ + public function get_results_key() { + if (!isset($this->_results_key)) { + $this->_results_key = $this->view->name . ':' . $this->display->id . ':results:' . $this->get_cache_key(); + } + + return $this->_results_key; + } + + /** + * Retrieves the Search API query object associated with the current view. + * + * @return SearchApiQueryInterface|null + * The Search API query object associated with the current view; or NULL if + * there is none. + */ + protected function getSearchApiQuery() { + if (!isset($this->search_api_query)) { + $this->search_api_query = FALSE; + if (isset($this->view->query) && $this->view->query instanceof SearchApiViewsQuery) { + $this->search_api_query = $this->view->query->getSearchApiQuery(); + } + } + + return $this->search_api_query ? $this->search_api_query : NULL; + } + +} diff --git a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/query.inc b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/query.inc index 4394c5f3..ae58b9a7 100644 --- a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/query.inc +++ b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/includes/query.inc @@ -135,7 +135,9 @@ class SearchApiViewsQuery extends views_plugin_query { * The order to sort items in - either 'ASC' or 'DESC'. Defaults to 'ASC'. */ public function add_selector_orderby($selector, $order = 'ASC') { - $this->query->sort($selector, $order); + if (!$this->errors) { + $this->query->sort($selector, $order); + } } /** @@ -213,7 +215,7 @@ class SearchApiViewsQuery extends views_plugin_query { '#default_value' => $this->options['search_api_bypass_access'], ); - if ($this->index->getEntityType()) { + if ($this->index && $this->index->getEntityType()) { $form['entity_access'] = array( '#type' => 'checkbox', '#title' => t('Additional access checks on result entities'), diff --git a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/search_api_views.info b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/search_api_views.info index 2ba7ccaa..2f0bfd72 100644 --- a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/search_api_views.info +++ b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/search_api_views.info @@ -1,7 +1,7 @@ -name = Search views +name = Search Views description = Integrates the Search API with Views, enabling users to create views with searches as filters or arguments. -dependencies[] = search_api -dependencies[] = views +dependencies[] = search_api:search_api +dependencies[] = views:views core = 7.x package = Search @@ -19,17 +19,18 @@ 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_numeric.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/plugin_content_cache.inc files[] = includes/query.inc -; Information added by Drupal.org packaging script on 2017-02-23 -version = "7.x-1.21" +; Information added by Drupal.org packaging script on 2019-03-11 +version = "7.x-1.26" core = "7.x" project = "search_api" -datestamp = "1487844493" - +datestamp = "1552334832" diff --git a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/search_api_views.module b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/search_api_views.module index 62fcb944..a927f082 100644 --- a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/search_api_views.module +++ b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/search_api_views.module @@ -27,8 +27,11 @@ function search_api_views_search_api_index_insert() { */ function search_api_views_search_api_index_update(SearchApiIndex $index) { // Check whether index was disabled. - if (!$index->enabled && $index->original->enabled) { + $is_enabled = $index->enabled; + $was_enabled = $index->original->enabled; + if (!$is_enabled && $was_enabled) { _search_api_views_index_unavailable($index); + return; } // Check whether the indexed fields changed. @@ -36,7 +39,9 @@ function search_api_views_search_api_index_update(SearchApiIndex $index) { $old_fields = $old_fields['fields']; $new_fields = $index->options + array('fields' => array()); $new_fields = $new_fields['fields']; - if ($old_fields != $new_fields) { + + // If the index was enabled or its fields changed, invalidate the Views cache. + if ($is_enabled != $was_enabled || $old_fields != $new_fields) { views_invalidate_cache(); } } diff --git a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/search_api_views.views.inc b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/search_api_views.views.inc index ff52d692..afec2d86 100644 --- a/sites/all/modules/contrib/search/search_api/contrib/search_api_views/search_api_views.views.inc +++ b/sites/all/modules/contrib/search/search_api/contrib/search_api_views/search_api_views.views.inc @@ -99,6 +99,7 @@ function search_api_views_views_data() { $table['search_api_relevance']['title'] = t('Relevance'); $table['search_api_relevance']['help'] = t('The relevance of this search result with respect to the query.'); $table['search_api_relevance']['field']['type'] = 'decimal'; + $table['search_api_relevance']['field']['float'] = TRUE; $table['search_api_relevance']['field']['handler'] = 'entity_views_handler_field_numeric'; $table['search_api_relevance']['field']['click sortable'] = TRUE; $table['search_api_relevance']['sort']['handler'] = 'SearchApiViewsHandlerSort'; @@ -219,6 +220,9 @@ function _search_api_views_add_handlers($id, array $field, EntityMetadataWrapper $table[$id]['filter']['vocabulary'] = $vocabulary; } } + elseif (in_array($inner_type, array('integer', 'decimal', 'duration', 'string'))) { + $table[$id]['filter']['handler'] = 'SearchApiViewsHandlerFilterNumeric'; + } else { $table[$id]['filter']['handler'] = 'SearchApiViewsHandlerFilter'; } @@ -285,6 +289,16 @@ function search_api_views_views_plugins() { ); } + if (module_exists('views_content_cache')) { + $ret['cache']['search_api_views_content_cache'] = array( + 'title' => t('Search-specific content-based'), + 'help' => t("Cache Search API views based on content updates. (Requires Views Content Cache)"), + 'base' => $bases, + 'handler' => 'SearchApiViewsContentCache', + 'uses options' => TRUE, + ); + } + return $ret; } diff --git a/sites/all/modules/contrib/search/search_api/includes/callback_add_aggregation.inc b/sites/all/modules/contrib/search/search_api/includes/callback_add_aggregation.inc index 069c4107..55ed611c 100644 --- a/sites/all/modules/contrib/search/search_api/includes/callback_add_aggregation.inc +++ b/sites/all/modules/contrib/search/search_api/includes/callback_add_aggregation.inc @@ -20,11 +20,23 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback { */ protected $reductionType; + /** + * A separator to use when the aggregation type is 'fulltext'. + * + * Used to temporarily store a string separator when the aggregation type is + * "fulltext", for use in SearchApiAlterAddAggregation::reduce() with + * array_reduce(). + * + * @var string + */ + protected $fulltextReductionSeparator; + public function configurationForm() { $form['#attached']['css'][] = drupal_get_path('module', 'search_api') . '/search_api.admin.css'; $fields = $this->index->getFields(FALSE); $field_options = array(); + $field_properties = array(); foreach ($fields as $name => $field) { $field_options[$name] = check_plain($field['name']); $field_properties[$name] = array( @@ -79,9 +91,23 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback { '#required' => TRUE, ); $form['fields'][$name]['type_descriptions'] = $type_descriptions; + $type_selector = ':input[name="callbacks[search_api_alter_add_aggregation][settings][fields][' . $name . '][type]"]'; foreach (array_keys($types) as $type) { - $form['fields'][$name]['type_descriptions'][$type]['#states']['visible'][':input[name="callbacks[search_api_alter_add_aggregation][settings][fields][' . $name . '][type]"]']['value'] = $type; + $form['fields'][$name]['type_descriptions'][$type]['#states']['visible'][$type_selector]['value'] = $type; } + $form['fields'][$name]['separator'] = array( + '#type' => 'textfield', + '#title' => t('Fulltext separator'), + '#description' => t('For aggregation type "Fulltext", set the text that should be used to separate the aggregated field values. Use "\t" for tabs and "\n" for newline characters.'), + '#default_value' => addcslashes(isset($field['separator']) ? $field['separator'] : "\n\n", "\0..\37\\"), + '#states' => array( + 'visible' => array( + $type_selector => array( + 'value' => 'fulltext', + ), + ), + ), + ); $form['fields'][$name]['fields'] = array_merge($field_properties, array( '#type' => 'checkboxes', '#title' => t('Contained fields'), @@ -125,11 +151,12 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback { return; } foreach ($values['fields'] as $name => $field) { - $fields = $values['fields'][$name]['fields'] = array_values(array_filter($field['fields'])); unset($values['fields'][$name]['actions']); + $fields = $values['fields'][$name]['fields'] = array_values(array_filter($field['fields'])); if ($field['name'] && !$fields) { form_error($form['fields'][$name]['fields'], t('You have to select at least one field to aggregate. If you want to remove an aggregated field, please delete its name.')); } + $values['fields'][$name]['separator'] = stripcslashes($field['separator']); } } @@ -176,6 +203,7 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback { $values = $this->flattenArray($values); $this->reductionType = $field['type']; + $this->fulltextReductionSeparator = isset($field['separator']) ? $field['separator'] : "\n\n"; $item->$name = array_reduce($values, array($this, 'reduce'), NULL); if ($field['type'] == 'count' && !$item->$name) { $item->$name = 0; @@ -192,7 +220,7 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback { public function reduce($a, $b) { switch ($this->reductionType) { case 'fulltext': - return isset($a) ? $a . "\n\n" . $b : $b; + return isset($a) ? $a . $this->fulltextReductionSeparator . $b : $b; case 'sum': return $a + $b; case 'count': @@ -300,10 +328,10 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback { 'count' => 'integer', 'max' => 'integer', 'min' => 'integer', - 'first' => 'string', - 'first_char' => 'string', - 'last' => 'string', - 'list' => 'list', + 'first' => 'token', + 'first_char' => 'token', + 'last' => 'token', + 'list' => 'list', ); case 'description': return array( diff --git a/sites/all/modules/contrib/search/search_api/includes/callback_add_hierarchy.inc b/sites/all/modules/contrib/search/search_api/includes/callback_add_hierarchy.inc index d69badb6..b9aada53 100644 --- a/sites/all/modules/contrib/search/search_api/includes/callback_add_hierarchy.inc +++ b/sites/all/modules/contrib/search/search_api/includes/callback_add_hierarchy.inc @@ -108,7 +108,7 @@ class SearchApiAlterAddHierarchy extends SearchApiAbstractAlterCallback { $this->extractHierarchy($child, $prop, $values[$key]); } foreach ($values as $key => $value) { - $item->$key = $value; + $item->$key = array_values($value); } } } diff --git a/sites/all/modules/contrib/search/search_api/includes/callback_user_content.inc b/sites/all/modules/contrib/search/search_api/includes/callback_user_content.inc new file mode 100644 index 00000000..168f3ae1 --- /dev/null +++ b/sites/all/modules/contrib/search/search_api/includes/callback_user_content.inc @@ -0,0 +1,57 @@ +getEntityType() === 'user'; + } + + /** + * {@inheritdoc} + */ + public function propertyInfo() { + return array( + 'search_api_user_content' => array( + 'label' => t('User content'), + 'description' => t('The nodes created by this user'), + 'type' => 'list', + ), + ); + } + + /** + * {@inheritdoc} + */ + public function alterItems(array &$items) { + $uids = array(); + foreach ($items as $item) { + $uids[] = $item->uid; + } + + $sql = 'SELECT nid, uid FROM {node} WHERE uid IN (:uids)'; + $nids = db_query($sql, array(':uids' => $uids)); + $user_nodes = array(); + foreach ($nids as $row) { + $user_nodes[$row->uid][] = $row->nid; + } + + foreach ($items as $item) { + $item->search_api_user_content = array(); + if (!empty($user_nodes[$item->uid])) { + $item->search_api_user_content = $user_nodes[$item->uid]; + } + } + } + +} diff --git a/sites/all/modules/contrib/search/search_api/includes/processor_highlight.inc b/sites/all/modules/contrib/search/search_api/includes/processor_highlight.inc index 5b419491..2ac502a1 100644 --- a/sites/all/modules/contrib/search/search_api/includes/processor_highlight.inc +++ b/sites/all/modules/contrib/search/search_api/includes/processor_highlight.inc @@ -315,99 +315,141 @@ class SearchApiHighlight extends SearchApiAbstractProcessor { * @param array $keys * Search keywords entered by the user. * - * @return string - * A string containing HTML for the excerpt. + * @return string|null + * A string containing HTML for the excerpt, or NULL if none could be + * created. */ protected function createExcerpt($text, array $keys) { // Prepare text by stripping HTML tags and decoding HTML entities. $text = strip_tags(str_replace(array('<', '>'), array(' <', '> '), $text)); - $text = ' ' . decode_entities($text); + $text = decode_entities($text); + $text = preg_replace('/\s+/', ' ', $text); + $text = trim($text, ' '); + $text_length = strlen($text); - // Extract fragments around keywords. - // First we collect ranges of text around each keyword, starting/ending - // at spaces, trying to get to the requested length. - // If the sum of all fragments is too short, we look for second occurrences. + // Try to reach the requested excerpt length with about two fragments (each + // with a keyword and some context). $ranges = array(); - $included = array(); $length = 0; - $work_keys = $keys; - while ($length < $this->options['excerpt_length'] && $work_keys) { - foreach ($work_keys as $k => $key) { - if ($length >= $this->options['excerpt_length']) { + $look_start = array(); + $remaining_keys = $keys; + + // Get the set excerpt length from the configuration. If the length is too + // small, only use one fragment. + $excerpt_length = $this->options['excerpt_length']; + $context_length = round($excerpt_length / 4) - 3; + if ($context_length < 32) { + $context_length = round($excerpt_length / 2) - 1; + } + + while ($length < $excerpt_length && !empty($remaining_keys)) { + $found_keys = array(); + foreach ($remaining_keys as $key) { + if ($length >= $excerpt_length) { break; } - // Remember occurrence of key so we can skip over it if more occurrences - // are desired. - if (!isset($included[$key])) { - $included[$key] = 0; + + // Remember where we last found $key, in case we are coming through a + // second time. + if (!isset($look_start[$key])) { + $look_start[$key] = 0; } - // Locate a keyword (position $p, always >0 because $text starts with a - // space). - $p = 0; - if (empty($this->options['highlight_partial'])) { - $regex = '/' . self::$boundary . preg_quote($key, '/') . self::$boundary . '/iu'; - if (preg_match($regex, $text, $match, PREG_OFFSET_CAPTURE, $included[$key])) { - $p = $match[0][1]; + + // See if we can find $key after where we found it the last time. Since + // we are requiring a match on a word boundary, make sure $text starts + // and ends with a space. + $matches = array(); + + if (!$this->options['highlight_partial']) { + $found_position = FALSE; + $regex = '/' . static::$boundary . preg_quote($key, '/') . static::$boundary . '/iu'; + if (preg_match($regex, ' ' . $text . ' ', $matches, PREG_OFFSET_CAPTURE, $look_start[$key])) { + $found_position = $matches[0][1]; } } else { - $p = stripos($text, $key, $included[$key]); + $found_position = stripos($text, $key, $look_start[$key]); } - // Now locate a space in front (position $q) and behind it (position $s), - // leaving about 60 characters extra before and after for context. - // Note that a space was added to the front and end of $text above. - if ($p) { - if (($q = strpos(' ' . $text, ' ', max(0, $p - 61))) !== FALSE) { - $end = substr($text . ' ', $p, 80); - if (($s = strrpos($end, ' ')) !== FALSE) { - // Account for the added spaces. - $q = max($q - 1, 0); - $s = min($s, strlen($end) - 1); - $ranges[$q] = $p + $s; - $length += $p + $s - $q; - $included[$key] = $p + 1; - continue; + if ($found_position !== FALSE) { + $look_start[$key] = $found_position + 1; + // Keep track of which keys we found this time, in case we need to + // pass through again to find more text. + $found_keys[] = $key; + + // Locate a space before and after this match, leaving some context on + // each end. + if ($found_position > $context_length) { + $before = strpos($text, ' ', $found_position - $context_length); + if ($before !== FALSE) { + ++$before; + } + } + else { + $before = 0; + } + if ($before !== FALSE && $before <= $found_position) { + if ($text_length > $found_position + $context_length) { + $after = strrpos(substr($text, 0, $found_position + $context_length), ' ', $found_position); + } + else { + $after = $text_length; + } + if ($after !== FALSE && $after > $found_position) { + if ($before < $after) { + // Save this range. + $ranges[$before] = $after; + $length += $after - $before; + } } } } - // Unless we got a match above, we don't need to look for this key any - // more. - unset($work_keys[$k]); } + // Next time through this loop, only look for keys we found this time, + // if any. + $remaining_keys = $found_keys; } - if (count($ranges) == 0) { - // We didn't find any keyword matches, so just return NULL. + if (!$ranges) { + // We didn't find any keyword matches, return NULL. return NULL; } // Sort the text ranges by starting position. ksort($ranges); - // Now we collapse overlapping text ranges into one. The sorting makes it O(n). + // Collapse overlapping text ranges into one. The sorting makes it O(n). $newranges = array(); + $from1 = $to1 = NULL; foreach ($ranges as $from2 => $to2) { - if (!isset($from1)) { + if ($from1 === NULL) { + // This is the first time through this loop: initialize. $from1 = $from2; $to1 = $to2; continue; } if ($from2 <= $to1) { + // The ranges overlap: combine them. $to1 = max($to1, $to2); } else { + // The ranges do not overlap: save the working range and start a new + // one. $newranges[$from1] = $to1; $from1 = $from2; $to1 = $to2; } } + // Save the remaining working range. $newranges[$from1] = $to1; - // Fetch text + // Fetch text within the combined ranges we found. $out = array(); foreach ($newranges as $from => $to) { $out[] = substr($text, $from, $to - $from); } + if (!$out) { + return NULL; + } // Let translators have the ... separator text as one chunk. $dots = explode('!excerpt', t('... !excerpt ... !excerpt ...')); diff --git a/sites/all/modules/contrib/search/search_api/includes/processor_stemmer.inc b/sites/all/modules/contrib/search/search_api/includes/processor_stemmer.inc index a6c05489..03d9fb03 100644 --- a/sites/all/modules/contrib/search/search_api/includes/processor_stemmer.inc +++ b/sites/all/modules/contrib/search/search_api/includes/processor_stemmer.inc @@ -24,12 +24,11 @@ class SearchApiPorterStemmer extends SearchApiAbstractProcessor { $form = parent::configurationForm(); $args = array( - '!algorithm' => url('https://github.com/markfullmer/porter2'), - '!exclusions' => url('https://github.com/markfullmer/porter2#user-content-custom-exclusions'), + '@algorithm' => url('http://snowball.tartarus.org/algorithms/english/stemmer.html'), ); $form += array( 'help' => array( - '#markup' => '

' . t('Optionally, provide an exclusion list to override the stemmer algorithm. Read about the algorithm and exclusions.', $args) . '

', + '#markup' => '

' . t('Optionally, provide an exclusion list to override the stemmer algorithm. (Read about the algorithm.)', $args) . '

', ), 'exceptions' => array( '#type' => 'textarea', @@ -66,7 +65,7 @@ class SearchApiPorterStemmer extends SearchApiAbstractProcessor { $stemmed[] = $word; } } - $value = implode('', $stemmed); + $value = implode(' ', $stemmed); } /** diff --git a/sites/all/modules/contrib/search/search_api/search_api.admin.inc b/sites/all/modules/contrib/search/search_api/search_api.admin.inc index 3afbaa78..89680dbf 100644 --- a/sites/all/modules/contrib/search/search_api/search_api.admin.inc +++ b/sites/all/modules/contrib/search/search_api/search_api.admin.inc @@ -528,7 +528,7 @@ function theme_search_api_server(array $variables) { } /** - * Form constructor for completely clearing a server. + * Form constructor for server operations. * * @param SearchApiServer $server * The server for which the form is displayed. @@ -543,15 +543,39 @@ function search_api_server_status_form(array $form, array &$form_state, SearchAp $form['clear'] = array( '#type' => 'submit', '#value' => t('Delete all indexed data on this server'), + '#submit' => array('search_api_server_status_form_clear_submit') ); + $count = $server->enabled ? search_api_server_tasks_count($server) : 0; + if ($count) { + $message = format_plural($count, '@count pending task must be executed before indexing.', '@count pending tasks must be executed before indexing.'); + drupal_set_message($message, 'warning', FALSE); + $form['execute_pending_tasks'] = array( + '#type' => 'submit', + '#value' => t('Execute all pending tasks on this server'), + '#submit' => array('search_api_server_status_form_execute_pending_tasks_submit') + ); + } + return $form; } /** -* Form submission handler for search_api_server_status_form(). -*/ -function search_api_server_status_form_submit(array $form, array &$form_state) { + * Form submission handler for search_api_server_status_form(). + * + * Used for the "Execute all pending tasks" button. + */ +function search_api_server_status_form_execute_pending_tasks_submit($form, &$form_state) { + $server_id = $form_state['server']->machine_name; + $form_state['redirect'] = "admin/config/search/search_api/server/$server_id/execute-tasks"; +} + +/** + * Form submission handler for search_api_server_status_form(). + * + * Used for the "Delete all indexed data" button. + */ +function search_api_server_status_form_clear_submit(array $form, array &$form_state) { $server_id = $form_state['server']->machine_name; $form_state['redirect'] = "admin/config/search/search_api/server/$server_id/clear"; } @@ -1566,10 +1590,13 @@ function search_api_admin_index_workflow(array $form, array &$form_state, Search $form['processors'] = array( '#type' => 'fieldset', '#title' => t('Processors'), - '#description' => t('Select processors which will pre- and post-process data at index and search time, and their order. ' . - 'Most processors will only influence fulltext fields, but refer to their individual descriptions for details regarding their effect.'), + '#description' => '

' . t("Select processors which will pre- and post-process data at index and search time, and their order. Most processors will only influence fulltext fields, but refer to their individual descriptions for details regarding their effect.
Also, some processors shouldn't be used with more advanced search engines (like Solr or Elasticsearch), since the search engine already provides this functionality.") . '

', '#collapsible' => TRUE, ); + if ($index->server) { + $form['processors']['#description'] .= '

' . t("Check the server's service class description for details.", + array('@server-url' => url('admin/config/search/search_api/server/' . $index->server . '/edit'))) . '

'; + } // Processor status. $form['processors']['status'] = array( @@ -1696,6 +1723,7 @@ function search_api_admin_index_workflow_submit(array $form, array &$form_state) unset($values['callbacks']['settings']); unset($values['processors']['settings']); $index = $form_state['index']; + $index_path = 'admin/config/search/search_api/index/' . $index->machine_name; $options = empty($index->options) ? array() : $index->options; @@ -1761,13 +1789,14 @@ function search_api_admin_index_workflow_submit(array $form, array &$form_state) $index->save(); $index->reindex(); - drupal_set_message(t("The indexing workflow was successfully edited. All content was scheduled for re-indexing so the new settings can take effect.")); + $vars = array('@url' => url($index_path)); + drupal_set_message(t('The indexing workflow was successfully edited. All content was scheduled for re-indexing so the new settings can take effect.', $vars)); } else { drupal_set_message(t('No values were changed.')); } - $form_state['redirect'] = 'admin/config/search/search_api/index/' . $index->machine_name . '/workflow'; + $form_state['redirect'] = $index_path . '/workflow'; } /** @@ -1822,8 +1851,8 @@ function search_api_admin_index_fields(array $form, array &$form_state, SearchAp 'In any case, fields of type "Fulltext" will always be fulltext-searchable.

'), ); if ($index->server) { - $form['description']['#description'] .= '

' . t('Check the ' . "server's service class description for details.", - array('@server-url' => url('admin/config/search/search_api/server/' . $index->server))) . '

'; + $form['description']['#description'] .= '

' . t("Check the server's service class description for details.", + array('@server-url' => url('admin/config/search/search_api/server/' . $index->server . '/edit'))) . '

'; } foreach ($fields as $key => $info) { $form['fields'][$key]['title']['#markup'] = check_plain($info['name']); diff --git a/sites/all/modules/contrib/search/search_api/search_api.drush.inc b/sites/all/modules/contrib/search/search_api/search_api.drush.inc index 61957b9a..8c995b17 100644 --- a/sites/all/modules/contrib/search/search_api/search_api.drush.inc +++ b/sites/all/modules/contrib/search/search_api/search_api.drush.inc @@ -95,6 +95,18 @@ function search_api_drush_command() { 'aliases' => array('sapi-r'), ); + $items['search-api-reindex-items'] = array( + 'description' => 'Force re-indexing of one or more specific items.', + 'examples' => array( + 'drush search-api-reindex-items node 12,34,56' => dt('Schedule the nodes with ID 12, 34 and 56 for re-indexing.'), + ), + 'arguments' => array( + 'entity_type' => dt('The entity type whose items should be re-indexed.'), + 'entities' => dt('The entities of the given entity type to be re-indexed.'), + ), + 'aliases' => array('sapi-ri'), + ); + $items['search-api-clear'] = array( 'description' => 'Clear one or all search indexes and mark them for re-indexing.', 'examples' => array( @@ -109,6 +121,19 @@ function search_api_drush_command() { 'aliases' => array('sapi-c'), ); + $items['search-api-execute-tasks'] = array( + 'description' => 'Execute all pending tasks or all for a given server.', + 'examples' => array( + 'drush search-api-execute-tasks my_solr_server' => dt('Execute all pending tasks on !server', array('!server' => 'my_solr_server')), + 'drush sapi-et my_solr_server' => dt('Execute all pending tasks on !server', array('!server' => 'my_solr_server')), + 'drush sapi-et' => dt('Execute all pending tasks on all servers.') + ), + 'arguments' => array( + 'server_id' => dt('The numeric ID or machine name of a server to execute tasks on.'), + ), + 'aliases' => array('sapi-et') + ); + $items['search-api-set-index-server'] = array( 'description' => 'Set the search server used by a given index.', 'examples' => array( @@ -448,6 +473,33 @@ function drush_search_api_reindex($index_id = NULL) { } } +/** + * Marks the given entities as needing to be re-indexed. + */ +function drush_search_api_reindex_items($entity_type, $entities) { + if (search_api_drush_static(__FUNCTION__)) { + return; + } + + // Validate list of entity ids. + if (!empty($entities) && !preg_match('#^[0-9]*(,[0-9]*)*$#', $entities)) { + drush_log(dt('Entities should be a single numeric entity ID or a list with the numeric entity IDs separated by comma.'), 'error'); + return; + } + + $ids = explode(',', $entities); + + if (!empty($ids)) { + search_api_track_item_change($entity_type, $ids); + + $combined_ids = array(); + foreach ($ids as $id) { + $combined_ids[] = $entity_type . '/' . $id; + } + search_api_track_item_change('multiple', $combined_ids); + } +} + /** * Clear an index. */ @@ -466,6 +518,34 @@ function drush_search_api_clear($index_id = NULL) { } } +/** + * Execute all pending tasks or all for a given server. + */ +function drush_search_api_execute_tasks($server_id = NULL) { + if (search_api_drush_static(__FUNCTION__)) { + return; + } + + // Attempt to load the associated server. + $server = NULL; + if ($server_id) { + $servers = search_api_drush_get_server($server_id); + if (!$servers) { + return; + } + $server = reset($servers); + } + + // Process batch op with drush. + try { + search_api_execute_pending_tasks($server); + drush_log(dt('!server tasks have been successfully executed.', array('!server' => $server->machine_name ? $server->machine_name : 'All')), 'ok'); + } + catch (SearchApiException $e) { + drush_log($e->getMessage(), 'error'); + } +} + /** * Set the server for a given index. */ diff --git a/sites/all/modules/contrib/search/search_api/search_api.info b/sites/all/modules/contrib/search/search_api/search_api.info index e7317531..b59bc929 100644 --- a/sites/all/modules/contrib/search/search_api/search_api.info +++ b/sites/all/modules/contrib/search/search_api/search_api.info @@ -1,6 +1,6 @@ name = Search API description = "Provides a generic API for modules offering search capabilities." -dependencies[] = entity +dependencies[] = entity:entity core = 7.x package = Search @@ -16,6 +16,7 @@ files[] = includes/callback_language_control.inc files[] = includes/callback_node_access.inc files[] = includes/callback_node_status.inc files[] = includes/callback_role_filter.inc +files[] = includes/callback_user_content.inc files[] = includes/callback_user_status.inc files[] = includes/datasource.inc files[] = includes/datasource_entity.inc @@ -37,9 +38,8 @@ files[] = includes/service.inc configure = admin/config/search/search_api -; Information added by Drupal.org packaging script on 2017-02-23 -version = "7.x-1.21" +; Information added by Drupal.org packaging script on 2019-03-11 +version = "7.x-1.26" core = "7.x" project = "search_api" -datestamp = "1487844493" - +datestamp = "1552334832" diff --git a/sites/all/modules/contrib/search/search_api/search_api.install b/sites/all/modules/contrib/search/search_api/search_api.install index 5dc26895..61f686d7 100644 --- a/sites/all/modules/contrib/search/search_api/search_api.install +++ b/sites/all/modules/contrib/search/search_api/search_api.install @@ -264,6 +264,51 @@ function search_api_schema() { return $schema; } +/** + * Implements hook_requirements(). + */ +function search_api_requirements($phase) { + $requirements = array(); + + if ($phase == 'runtime') { + // Check whether at least one server has pending tasks. + if (search_api_server_tasks_count()) { + $items = array(); + + $conditions = array('enabled' => TRUE); + foreach (search_api_server_load_multiple(FALSE, $conditions) as $server) { + $count = search_api_server_tasks_count($server); + if ($count) { + $args = array( + '@name' => $server->name, + ); + $text = format_plural($count, '@name has @count pending task.', '@name has @count pending tasks.', $args); + $items[] = l($text, "admin/config/search/search_api/server/{$server->machine_name}/execute-tasks"); + } + } + + if ($items) { + $text = t('There are pending tasks for the following servers:'); + $text .= theme('item_list', array( + 'type' => 'ul', + 'items' => $items, + )); + if (count($items) > 1) { + $label = t('Execute pending tasks on all servers'); + $text .= l($label, 'admin/config/search/search_api/execute-tasks'); + } + $requirements['search_api_pending_tasks'] = array( + 'title' => t('Search API'), + 'value' => $text, + 'severity' => REQUIREMENT_WARNING, + ); + } + } + } + + return $requirements; +} + /** * Implements hook_install(). * diff --git a/sites/all/modules/contrib/search/search_api/search_api.module b/sites/all/modules/contrib/search/search_api/search_api.module index 61385c5d..c1cf4e4b 100644 --- a/sites/all/modules/contrib/search/search_api/search_api.module +++ b/sites/all/modules/contrib/search/search_api/search_api.module @@ -72,6 +72,15 @@ function search_api_menu() { 'type' => MENU_LOCAL_TASK, 'context' => MENU_CONTEXT_INLINE | MENU_CONTEXT_PAGE, ); + $items[$pre . '/server/%search_api_server/execute-tasks'] = array( + 'title' => 'Execute pending tasks', + 'description' => 'Attempt to process pending tasks for a given server.', + 'page callback' => 'search_api_execute_pending_tasks', + 'page arguments' => array(5), + 'access callback' => 'search_api_access_execute_tasks_batch', + 'access arguments' => array(5), + 'type' => MENU_CALLBACK, + ); $items[$pre . '/server/%search_api_server/disable'] = array( 'title' => 'Disable', 'description' => 'Disable index.', @@ -98,6 +107,13 @@ function search_api_menu() { 'context' => MENU_CONTEXT_INLINE, 'weight' => 10, ); + $items[$pre . '/execute-tasks'] = array( + 'title' => 'Execute pending tasks', + 'description' => 'Attempt to process pending server tasks.', + 'page callback' => 'search_api_execute_pending_tasks', + 'access callback' => 'search_api_access_execute_tasks_batch', + 'type' => MENU_LOCAL_ACTION, + ); $items[$pre . '/index/%search_api_index'] = array( 'title' => 'View index', 'title callback' => 'search_api_admin_item_title', @@ -1025,6 +1041,28 @@ function search_api_search_api_item_type_info() { return $types; } +/** + * Implements hook_module_implements_alter(). + * + * Ensures the item type and service class static caches are invalidated at the + * right time. + */ +function search_api_module_implements_alter(array &$implementations, $hook) { + switch ($hook) { + case 'modules_enabled': + $group = $implementations['search_api']; + unset($implementations['search_api']); + $implementations = array('search_api' => $group) + $implementations; + break; + + case 'modules_disabled': + $group = $implementations['search_api']; + unset($implementations['search_api']); + $implementations['search_api'] = $group; + break; + } +} + /** * Implements hook_modules_enabled(). */ @@ -1103,6 +1141,11 @@ function search_api_search_api_alter_callback_info() { 'description' => t('Exclude unpublished nodes from the index. Caution: This only affects the indexed nodes themselves. If an enabled node has references to disabled nodes, those will still be indexed (or displayed) normally.'), 'class' => 'SearchApiAlterNodeStatus', ); + $callbacks['search_api_alter_user_content'] = array( + 'name' => t('Add user content'), + 'description' => t('Allows indexing of nodes (and their fields) created by the indexed user. (Caution: This might lead to performance problems, or even errors during indexing, on larger sites.)'), + 'class' => 'SearchApiAlterAddUserContent', + ); $callbacks['search_api_alter_user_status'] = array( 'name' => t('Exclude blocked users'), 'description' => t('Exclude blocked users from the index. Caution: This only affects the indexed users themselves. If an active user account includes a reference to a disabled user, that reference will still be indexed (or displayed) normally.'), @@ -1382,6 +1425,10 @@ function search_api_server_tasks_check(SearchApiServer $server = NULL) { // Sometimes the order of tasks might be important, so make sure to order by // the task ID (which should be in order of insertion). $select->orderBy('t.id'); + // Only retrieve and execute 100 tasks at once, to avoid running out of memory + // or time. We just can't do anything else until all tasks have been resolved, + // but at least we shouldn't crash sites, or keep piling up tasks, that way. + $select->range(0, 100); $tasks = $select->execute(); $executed_tasks = array(); @@ -1429,7 +1476,7 @@ function search_api_server_tasks_check(SearchApiServer $server = NULL) { default: // This should never happen. - continue; + continue 2; } $executed_tasks[] = $task->id; } @@ -1438,11 +1485,116 @@ function search_api_server_tasks_check(SearchApiServer $server = NULL) { if (!$executed_tasks) { return TRUE; } - // Otherwise, delete the executed tasks and check if new tasks were created. + // Otherwise, delete the executed tasks and check if new tasks were created + // (or if we didn't even fetch all due to the 100 tasks limit). search_api_server_tasks_delete($executed_tasks); return $count_query->execute()->fetchField() === 0; } +/** + * Provides a batch wrapper for search_api_server_tasks_check(). + * + * @param SearchApiServer|null $server + * (optional) The server whose tasks should be executed, or NULL to execute + * tasks for all servers. + */ +function search_api_execute_pending_tasks(SearchApiServer $server = NULL) { + batch_set(array( + 'title' => t('Processing pending tasks'), + 'operations' => array( + array( + 'search_api_execute_pending_tasks_batch', + array( + $server, + ), + ), + ), + 'finished' => 'search_api_execute_pending_tasks_finished' + )); + if ($server) { + $path = 'admin/config/search/search_api/server/' . $server->machine_name; + } + else { + $path = 'admin/config/search/search_api'; + } + + if (function_exists('drush_backend_batch_process')) { + drush_backend_batch_process(); + } + else { + batch_process($path); + } +} + +/** + * Executes pending server tasks as part of a batch operation. + */ +function search_api_execute_pending_tasks_batch(SearchApiServer $server = NULL, &$context) { + if (!isset($context['results']['total'])) { + $context['results']['total'] = search_api_server_tasks_count($server); + } + $total = $context['results']['total']; + + search_api_server_tasks_check($server); + + $remaining = search_api_server_tasks_count($server); + $executed = max($total - $remaining, 0); + + $args['@remaining'] = $remaining; + $context['message'] = format_plural($executed, 'Successfully executed @count task, @remaining remaining.', 'Successfully executed @count tasks, @remaining remaining.', $args); + $context['finished'] = $executed / $total; +} + +/** + * Batch finish callback for pending server tasks. + */ +function search_api_execute_pending_tasks_finished($success, $results, $operations) { + if ($success) { + // Clear the previous warning. + drupal_get_messages('warning'); + + // Alert user to the number of tasks executed. + drupal_set_message(format_plural($results['total'], 'Successfully executed @count task.', 'Successfully executed @count tasks.')); + } +} + +/** + * Return the number of pending tasks. + * + * @param SearchApiServer|null $server + * (optional) The server for which tasks should be counted, or NULL to count + * for all enabled servers. + * + * @return int + * The number of pending tasks for the server, or in total. + */ +function search_api_server_tasks_count(SearchApiServer $server = NULL) { + $query = db_select('search_api_task', 't') + ->fields('t'); + + if ($server) { + $query->condition('server_id', $server->machine_name); + } + else { + $query->join('search_api_server', 's', 's.machine_name = t.server_id'); + $query->condition('s.enabled', 1); + } + + return $query->countQuery()->execute()->fetchField(); +} + +/** + * Access callback: Checks whether a user can execute pending tasks. + * + * @param SearchApiServer|null $server + * (optional) The server for which tasks would be executed. + */ +function search_api_access_execute_tasks_batch(SearchApiServer $server = NULL) { + return user_access('administer search_api') + && search_api_server_tasks_count($server) + && (!$server || $server->enabled); +} + /** * Adds an entry into a server's list of pending tasks. * diff --git a/sites/all/modules/contrib/search/search_api/search_api.test b/sites/all/modules/contrib/search/search_api/search_api.test index 23f36bcf..76a89705 100644 --- a/sites/all/modules/contrib/search/search_api/search_api.test +++ b/sites/all/modules/contrib/search/search_api/search_api.test @@ -86,6 +86,7 @@ class SearchApiWebTest extends DrupalWebTestCase { * and then run tests on it. */ public function testFramework() { + module_enable(array('search_api_test_2')); $this->drupalLogin($this->drupalCreateUser(array('administer search_api'))); $this->insertItems(); $this->createIndex(); @@ -730,13 +731,17 @@ class SearchApiWebTest extends DrupalWebTestCase { * deleteServer()) and that all associated tables and variables are removed. */ protected function disableModules() { + module_disable(array('search_api_test_2'), FALSE); + $this->assertFalse(module_exists('search_api_test_2'), 'Second test module was successfully disabled.'); module_disable(array('search_api_test'), FALSE); - $this->assertFalse(module_exists('search_api_test'), 'Test module was successfully disabled.'); + $this->assertFalse(module_exists('search_api_test'), 'First test module was successfully disabled.'); module_disable(array('search_api'), FALSE); $this->assertFalse(module_exists('search_api'), 'Search API module was successfully disabled.'); + drupal_uninstall_modules(array('search_api_test_2'), FALSE); + $this->assertEqual(drupal_get_installed_schema_version('search_api_test_2', TRUE), SCHEMA_UNINSTALLED, 'Second test module was successfully uninstalled.'); drupal_uninstall_modules(array('search_api_test'), FALSE); - $this->assertEqual(drupal_get_installed_schema_version('search_api_test', TRUE), SCHEMA_UNINSTALLED, 'Test module was successfully uninstalled.'); + $this->assertEqual(drupal_get_installed_schema_version('search_api_test', TRUE), SCHEMA_UNINSTALLED, 'First test module was successfully uninstalled.'); $this->assertFalse(db_table_exists('search_api_test'), 'Test module table was successfully removed.'); drupal_uninstall_modules(array('search_api'), FALSE); $this->assertEqual(drupal_get_installed_schema_version('search_api', TRUE), SCHEMA_UNINSTALLED, 'Search API module was successfully uninstalled.'); diff --git a/sites/all/modules/contrib/search/search_api/tests/search_api_test.info b/sites/all/modules/contrib/search/search_api/tests/search_api_test.info index 95c999ab..2c5f46e0 100644 --- a/sites/all/modules/contrib/search/search_api/tests/search_api_test.info +++ b/sites/all/modules/contrib/search/search_api/tests/search_api_test.info @@ -1,18 +1,16 @@ - -name = Search API test +name = Search API Test description = "Some dummy implementations for testing the Search API." core = 7.x package = Search -dependencies[] = search_api +dependencies[] = search_api:search_api files[] = search_api_test.module hidden = TRUE -; Information added by Drupal.org packaging script on 2017-02-23 -version = "7.x-1.21" +; Information added by Drupal.org packaging script on 2019-03-11 +version = "7.x-1.26" core = "7.x" project = "search_api" -datestamp = "1487844493" - +datestamp = "1552334832" diff --git a/sites/all/modules/contrib/search/search_api/tests/search_api_test_2.info b/sites/all/modules/contrib/search/search_api/tests/search_api_test_2.info new file mode 100644 index 00000000..5d36c8fb --- /dev/null +++ b/sites/all/modules/contrib/search/search_api/tests/search_api_test_2.info @@ -0,0 +1,16 @@ +name = Search API Test Service 2 +description = "A module providing a second test search service." +core = 7.x +package = Search + +dependencies[] = search_api:search_api + +files[] = search_api_test_service_2.module + +hidden = TRUE + +; Information added by Drupal.org packaging script on 2019-03-11 +version = "7.x-1.26" +core = "7.x" +project = "search_api" +datestamp = "1552334832" diff --git a/sites/all/modules/contrib/search/search_api/tests/search_api_test_2.module b/sites/all/modules/contrib/search/search_api/tests/search_api_test_2.module new file mode 100644 index 00000000..d78a80eb --- /dev/null +++ b/sites/all/modules/contrib/search/search_api/tests/search_api_test_2.module @@ -0,0 +1,136 @@ + $name, + 'description' => 'search_api_test_service_2 description', + 'class' => 'SearchApiDummyService', + ); + return $services; +} + +/** + * Implements hook_default_search_api_server(). + */ +function search_api_test_2_default_search_api_server() { + $id = 'test_server_2'; + $items[$id] = entity_create('search_api_server', array( + 'name' => 'Search API test server 2', + 'machine_name' => $id, + 'enabled' => 1, + 'description' => 'A server used for testing.', + 'class' => 'search_api_test_service_2', + )); + return $items; +} + +/** + * Dummy service for testing. + */ +class SearchApiDummyService implements SearchApiServiceInterface { + + /** + * {@inheritdoc} + */ + public function __construct(\SearchApiServer $server) {} + + /** + * {@inheritdoc} + */ + public function configurationForm(array $form, array &$form_state) { + return array(); + } + + /** + * {@inheritdoc} + */ + public function configurationFormValidate(array $form, array &$values, array &$form_state) {} + + /** + * {@inheritdoc} + */ + public function configurationFormSubmit(array $form, array &$values, array &$form_state) {} + + /** + * {@inheritdoc} + */ + public function supportsFeature($feature) { + return FALSE; + } + + /** + * {@inheritdoc} + */ + public function viewSettings() { + return array(); + } + + /** + * {@inheritdoc} + */ + public function postCreate() {} + + /** + * {@inheritdoc} + */ + public function postUpdate() { + return FALSE; + } + + /** + * {@inheritdoc} + */ + public function preDelete() {} + + /** + * {@inheritdoc} + */ + public function addIndex(SearchApiIndex $index) {} + + /** + * {@inheritdoc} + */ + public function fieldsUpdated(SearchApiIndex $index) { + return FALSE; + } + + /** + * {@inheritdoc} + */ + public function removeIndex($index) {} + + /** + * {@inheritdoc} + */ + public function indexItems(SearchApiIndex $index, array $items) { + return array(); + } + + /** + * {@inheritdoc} + */ + public function deleteItems($ids = 'all', SearchApiIndex $index = NULL) {} + + /** + * {@inheritdoc} + */ + public function query(SearchApiIndex $index, $options = array()) { + throw new SearchApiException("The dummy service doesn't support queries"); + } + + /** + * {@inheritdoc} + */ + public function search(SearchApiQueryInterface $query) { + return array(); + } +} diff --git a/sites/all/modules/contrib/search/search_api_solr_overrides/README.txt b/sites/all/modules/contrib/search/search_api_solr_overrides/README.txt index 9ddedd2d..f13e5e80 100644 --- a/sites/all/modules/contrib/search/search_api_solr_overrides/README.txt +++ b/sites/all/modules/contrib/search/search_api_solr_overrides/README.txt @@ -1 +1,39 @@ -See major version branches. +INTRODUCTION +------------ +Allows you to override solr connection settings on an environment (site) basis, +via your settings.php without editing servers managed in features. + +REQUIREMENTS +------------ +* search_api_solr module + +CONFIGURATION +------------- +The module has no menu or modifiable settings. There is no configuration. When +enabled, you can set your override values in your settings.php file. +Search api will automatically pick up your values, but make sure to clear your +cache first. + +EXAMPLE +------- +You can add following example to your settings.php file. + +$conf['search_api_solr_overrides'] = array( + 'solr-server-id' => array( + 'name' => 'Solr Server (Overridden)', + 'options' => array( + 'host' => '127.0.0.1', + 'port' => 8983, + 'path' => '/solr', + ), + ), +); + +MAINTAINERS +----------- +Current maintainers: +* nick_schuch - https://www.drupal.org/u/nick_schuch +* cafuego - https://www.drupal.org/u/cafuego + +This project has been sponsored by: +* PreviousNext - http://www.previousnext.com.au diff --git a/sites/all/modules/contrib/search/search_api_solr_overrides/search_api_solr_overrides.info b/sites/all/modules/contrib/search/search_api_solr_overrides/search_api_solr_overrides.info index 58a03527..1da542fb 100644 --- a/sites/all/modules/contrib/search/search_api_solr_overrides/search_api_solr_overrides.info +++ b/sites/all/modules/contrib/search/search_api_solr_overrides/search_api_solr_overrides.info @@ -3,9 +3,9 @@ description = Provides site specific overrides for search_api_solr configuration core = 7.x dependencies[] = search_api_solr -; Information added by drupal.org packaging script on 2013-10-01 -version = "7.x-1.0-rc1+1-dev" +; Information added by Drupal.org packaging script on 2017-06-13 +version = "7.x-1.0" core = "7.x" project = "search_api_solr_overrides" -datestamp = "1380626863" +datestamp = "1497319149" diff --git a/sites/all/modules/contrib/search/search_api_solr_overrides/search_api_solr_overrides.module b/sites/all/modules/contrib/search/search_api_solr_overrides/search_api_solr_overrides.module old mode 100644 new mode 100755 index e83dfcc1..515faf87 --- a/sites/all/modules/contrib/search/search_api_solr_overrides/search_api_solr_overrides.module +++ b/sites/all/modules/contrib/search/search_api_solr_overrides/search_api_solr_overrides.module @@ -15,7 +15,7 @@ * Example: * $conf['search_api_solr_overrides'] = array( * 'solr-server-id' => array( - * 'name' => t('Solr Server (Overridden)'), + * 'name' => 'Solr Server (Overridden)', * 'options' => array( * 'host' => '127.0.0.1', * 'port' => 8983, @@ -32,7 +32,7 @@ function search_api_solr_overrides_search_api_server_load($servers) { $overrides = variable_get('search_api_solr_overrides', FALSE); // Ensure the is information provided. - if (empty($overrides)) { + if (empty($overrides) || !is_array($overrides)) { return; } @@ -41,12 +41,12 @@ function search_api_solr_overrides_search_api_server_load($servers) { // Check to see if the server config exists. if (!empty($servers[$id])) { foreach ($servers[$id] as $key => $field) { - // Ensure we need to override. - if (empty($override[$key])) { + // Ensure we need to override. User isset, so we can set FALSE values. + if (!isset($override[$key])) { continue; } - // Check for if the field is an array. + // Check if the field contains an array. if (is_array($field)) { $servers[$id]->$key = array_merge($servers[$id]->$key, $override[$key]); }