Commit db2d92e7 authored by alexpott's avatar alexpott

Issue #2003482 by pwolanin, jhodgdon, kim.pepper, Nick_vh, tim.plunkett,...

Issue #2003482 by pwolanin, jhodgdon, kim.pepper, Nick_vh, tim.plunkett, jibran, Berdir, effulgentsia, alexpott: Convert hook_search_info() to plugin system.
parent 73c2194b
......@@ -34,7 +34,7 @@ function hook_comment_presave(Drupal\comment\Comment $comment) {
*/
function hook_comment_insert(Drupal\comment\Comment $comment) {
// Reindex the node when comments are added.
search_touch_node($comment->nid->target_id);
node_reindex_node_search($comment->nid->target_id);
}
/**
......@@ -45,7 +45,7 @@ function hook_comment_insert(Drupal\comment\Comment $comment) {
*/
function hook_comment_update(Drupal\comment\Comment $comment) {
// Reindex the node when comments are updated.
search_touch_node($comment->nid->target_id);
node_reindex_node_search($comment->nid->target_id);
}
/**
......
This diff is collapsed.
......@@ -95,14 +95,14 @@
* - Validating a node during editing form submit (calling
* node_form_validate()):
* - hook_node_validate() (all)
* - Searching (calling node_search_execute()):
* - Searching (using the 'node_search' plugin):
* - hook_ranking() (all)
* - Query is executed to find matching nodes
* - Resulting node is loaded (see Loading section above)
* - Resulting node is prepared for viewing (see Viewing a single node above)
* - comment_node_update_index() is called.
* - comment_node_update_index() is called (this adds "N comments" text)
* - hook_node_search_result() (all)
* - Search indexing (calling node_update_index()):
* - Search indexing (calling updateIndex() on the 'node_search' plugin):
* - Node is loaded (see Loading section above)
* - Node is prepared for viewing (see Viewing a single node above)
* - hook_node_update_index() (all)
......@@ -612,8 +612,8 @@ function hook_node_prepare_form(\Drupal\node\NodeInterface $node, $form_display,
/**
* Act on a node being displayed as a search result.
*
* This hook is invoked from node_search_execute(), after node_load() and
* node_view() have been called.
* This hook is invoked from the node search plugin during search execution,
* after loading and rendering the node.
*
* @param \Drupal\Core\Entity\EntityInterface $node
* The node being displayed in a search result.
......@@ -686,8 +686,8 @@ function hook_node_update(\Drupal\Core\Entity\EntityInterface $node) {
/**
* Act on a node being indexed for searching.
*
* This hook is invoked during search indexing, after node_load(), and after the
* result of node_view() is added as $node->rendered to the node object.
* This hook is invoked during search indexing, after loading, and after the
* result of rendering is added as $node->rendered to the node object.
*
* @param \Drupal\Core\Entity\EntityInterface $node
* The node being indexed.
......
This diff is collapsed.
......@@ -4,18 +4,18 @@ search.settings:
type: mapping
label: 'Search settings'
mapping:
active_modules:
active_plugins:
type: sequence
label: 'Active search modules'
label: 'Active search plugins'
sequence:
- type: string
label: 'Module'
label: 'Plugin'
and_or_limit:
type: integer
label: 'AND/OR combination limit'
default_module:
default_plugin:
type: string
label: 'Default search module'
label: 'Default search plugin'
index:
type: mapping
label: 'Indexing settings'
......
active_modules:
- node
- user
active_plugins:
node_search: node_search
user_search: user_search
and_or_limit: '7'
default_module: node
default_plugin: node_search
index:
cron_limit: '100'
overlap_cjk: '1'
......
<?php
/**
* @file
* Contains \Drupal\search\Annotation\SearchPlugin.
*/
namespace Drupal\search\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines a SearchPlugin type annotation object.
*
* SearchPlugin classes define search types for the core Search module. Each
* active search type is displayed in a tab on the Search page, and each has a
* path suffix after "search/".
*
* @see SearchPluginBase
*
* @Annotation
*/
class SearchPlugin extends Plugin {
/**
* A unique identifier for the search plugin.
*
* @var string
*/
public $id;
/**
* The path fragment to be added to search/ for the search page.
*
* @var string
*/
public $path;
/**
* The title for the search page tab.
*
* @todo This will potentially be translated twice or cached with the wrong
* translation until the search tabs are converted to local task plugins.
*
* @ingroup plugin_translatable
*
* @var \Drupal\Core\Annotation\Translation
*/
public $title;
}
......@@ -79,7 +79,7 @@ public function submitForm(array &$form, array &$form_state) {
}
$form_id = $form['form_id']['#value'];
$info = search_get_default_module_info();
$info = search_get_default_plugin_info();
if ($info) {
$form_state['redirect'] = 'search/' . $info['path'] . '/' . trim($form_state['values'][$form_id]);
}
......
<?php
/**
* @file
* Contains \Drupal\search\Plugin\SearchIndexingInterface.
*/
namespace Drupal\search\Plugin;
/**
* Defines an optional interface for SearchPlugin objects using the index.
*
* Plugins implementing this interface will have these methods invoked during
* search_cron() and via the search module administration form. Plugins not
* implementing this interface are assumed to use alternate mechanisms for
* indexing the data used to provide search results.
*/
interface SearchIndexingInterface {
/**
* Updates the search index for this plugin.
*
* This method is called every cron run if the plugin has been set as
* an active search module on the Search settings page
* (admin/config/search/settings). It allows your module to add items to the
* built-in search index using search_index(), or to add them to your module's
* own indexing mechanism.
*
* When implementing this method, your module should index content items that
* were modified or added since the last run. PHP has a time limit
* for cron, though, so it is advisable to limit how many items you index
* per run using config('search.settings')->get('index.cron_limit'). Also,
* since the cron run could time out and abort in the middle of your run, you
* should update any needed internal bookkeeping on when items have last
* been indexed as you go rather than waiting to the end of indexing.
*/
public function updateIndex();
/**
* Takes action when the search index is going to be rebuilt.
*
* Modules that use updateIndex() should update their indexing bookkeeping so
* that it starts from scratch the next time updateIndex() is called.
*/
public function resetIndex();
/**
* Reports the status of indexing.
*
* The core search module only invokes this method on active module plugins.
* Implementing modules do not need to check whether they are active when
* calculating their return values.
*
* @return array
* An associative array with the key-value pairs:
* - remaining: The number of items left to index.
* - total: The total number of items to index.
*/
public function indexStatus();
}
<?php
/**
* @file
* Contains \Drupal\search\Plugin\SearchInterface.
*/
namespace Drupal\search\Plugin;
use Drupal\Component\Plugin\PluginInspectionInterface;
/**
* Defines a common interface for all SearchPlugin objects.
*/
interface SearchInterface extends PluginInspectionInterface {
/**
* Sets the keywords, parameters, and attributes to be used by execute().
*
* @param string $keywords
* The keywords to use in a search.
* @param array $parameters
* Array of parameters as am associative array. This is expected to
* be the query string from the current request.
* @param array $attributes
* Array of attributes, usually from the current request object. The search
* plugin may use the '_account' attribute if present to personalize the
* search, or use attributes from the current route variables.
*
* @return \Drupal\search\Plugin\SearchInterface
* A search plugin object for chaining.
*/
public function setSearch($keywords, array $parameters, array $attributes);
/**
* Returns the currently set keywords of the plugin instance.
*
* @return string
* The keywords.
*/
public function getKeywords();
/**
* Returns the current parameters set using setSearch().
*
* @return array
* The parameters.
*/
public function getParameters();
/**
* Returns the currently set attributes (from the request).
*
* @return array
* The attributes.
*/
public function getAttributes();
/**
* Verifies if the values set via setSearch() are valid and sufficient.
*
* @return bool
* TRUE if the search settings are valid and sufficient to execute a search,
* and FALSE if not.
*/
public function isSearchExecutable();
/**
* Executes the search.
*
* @return array
* A structured list of search results.
*/
public function execute();
/**
* Executes the search and builds a render array.
*
* @return array
* The search results in a renderable array.
*/
public function buildResults();
/**
* Alters the search form when being built for a given plugin.
*
* The core search module only invokes this method on active module plugins
* when building a form for them in search_form(). A plugin implementing
* this needs to add validate and submit callbacks to the form if it needs
* to act after form submission.
*
* @param array $form
* Nested array of form elements that comprise the form.
* @param array $form_state
* A keyed array containing the current state of the form. The arguments
* that drupal_get_form() was originally called with are available in the
* array $form_state['build_info']['args'].
*/
public function searchFormAlter(array &$form, array &$form_state);
/**
* Adds elements to the search settings form.
*
* The core search module only invokes this method on active module plugins.
*
* @param array $form
* Nested array of form elements that comprise the form.
* @param array $form_state
* A keyed array containing the current state of the form. The arguments
* that drupal_get_form() was originally called with are available in the
* array $form_state['build_info']['args'].
*/
public function addToAdminForm(array &$form, array &$form_state);
/**
* Handles any submission for elements on the search settings form.
*
* The core search module only invokes this method on active module plugins.
*
* @param array $form
* Nested array of form elements that comprise the form.
* @param array $form_state
* A keyed array containing the current state of the form. The arguments
* that drupal_get_form() was originally called with are available in the
* array $form_state['build_info']['args'].
*/
public function submitAdminForm(array &$form, array &$form_state);
}
<?php
/**
* @file
* Contains \Drupal\search\Plugin\SearchPluginBase
*/
namespace Drupal\search\Plugin;
use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
/**
* Defines a base class for plugins wishing to support search.
*/
abstract class SearchPluginBase extends PluginBase implements ContainerFactoryPluginInterface, SearchInterface {
/**
* The keywords to use in a search.
*
* @var string
*/
protected $keywords;
/**
* Array of parameters from the query string from the request.
*
* @var array
*/
protected $searchParameters;
/**
* Array of attributes - usually from the request object.
*
* @var array
*/
protected $searchAttributes;
/**
* {@inheritdoc}
*/
public function setSearch($keywords, array $parameters, array $attributes) {
$this->keywords = (string) $keywords;
$this->searchParameters = $parameters;
$this->searchAttributes = $attributes;
return $this;
}
/**
* {@inheritdoc}
*/
public function getKeywords() {
return $this->keywords;
}
/**
* {@inheritdoc}
*/
public function getParameters() {
return $this->searchParameters;
}
/**
* {@inheritdoc}
*/
public function getAttributes() {
return $this->searchAttributes;
}
/**
* {@inheritdoc}
*/
public function isSearchExecutable() {
// Default implementation suitable for plugins that only use keywords.
return !empty($this->keywords);
}
/**
* {@inheritdoc}
*/
public function buildResults() {
$results = $this->execute();
return array(
'#theme' => 'search_results',
'#results' => $results,
'#plugin_id' => $this->getPluginId(),
);
}
/**
* {@inheritdoc}
*/
public function searchFormAlter(array &$form, array &$form_state) {
// Empty default implementation.
}
/**
* {@inheritdoc}
*/
public function addToAdminForm(array &$form, array &$form_state) {
// Empty default implementation.
}
/**
* {@inheritdoc}
*/
public function submitAdminForm(array &$form, array &$form_state) {
// Empty default implementation.
}
}
<?php
/**
* @file
* Contains \Drupal\search\SearchExpression.
*/
namespace Drupal\search;
/**
* Defines a search expression.
*/
class SearchExpression {
/**
* The search expression string
*
* @var string
*/
protected $expression;
/**
* Constructs a SearchExpression.
*
* @param string $expression
* The search expression.
*/
public function __construct($expression) {
$this->expression = $expression;
}
/**
* Gets the expression.
*
* @return string
*/
public function getExpression() {
return $this->expression;
}
/**
* Extracts a module-specific search option from a search expression.
*
* Search options are added using SearchExpression::insert() and retrieved
* using SearchExpression::extract(). They take the form option:value, and
* are added to the ordinary keywords in the search expression.
*
* @param string $option
* The name of the option to retrieve from the search expression.
*
* @return string
* The value previously stored in the search expression for option $option,
* if any. Trailing spaces in values will not be included.
*/
public function extract($option) {
if (preg_match('/(^| )' . $option . ':([^ ]*)( |$)/i', $this->expression, $matches)) {
return $matches[2];
}
}
/**
* Adds a module-specific search option to a search expression.
*
* Search options are added using SearchExpression::insert() and retrieved
* using SearchExpression::extract(). They take the form option:value, and
* are added to the ordinary keywords in the search expression.
*
* @param string $option
* The name of the option to add to the search expression.
* @param string $value
* The value to add for the option. If present, it will replace any previous
* value added for the option. Cannot contain any spaces or | characters, as
* these are used as delimiters. If you want to add a blank value $option: to
* the search expression, pass in an empty string or a string that is
* composed of only spaces. To clear a previously-stored option without
* adding a replacement, pass in NULL for $value or omit.
*
* @return static|\Drupal\search\SearchExpression
* The search expression, with any previous value for this option removed, and
* a new $option:$value pair added if $value was provided.
*/
public function insert($option, $value = NULL) {
// Remove any previous values stored with $option.
$this->expression = trim(preg_replace('/(^| )' . $option . ':[^ ]*/i', '', $this->expression));
// Set new value, if provided.
if (isset($value)) {
$this->expression .= ' ' . $option . ':' . trim($value);
}
return $this;
}
}
<?php
/**
* @file
* Contains \Drupal\search\SearchPluginManager.
*/
namespace Drupal\search;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Session\AccountInterface;
/**
* SearchExecute plugin manager.
*/
class SearchPluginManager extends DefaultPluginManager {
/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactory
*/
protected $configFactory;
/**
* {@inheritdoc}
*/
public function __construct(\Traversable $namespaces, ConfigFactory $config_factory) {
$annotation_namespaces = array('Drupal\search\Annotation' => $namespaces['Drupal\search']);
parent::__construct('Plugin/Search', $namespaces, $annotation_namespaces, 'Drupal\search\Annotation\SearchPlugin');
$this->configFactory = $config_factory;
}
/**
* {@inheritdoc}
*/
public function processDefinition(&$definition, $plugin_id) {
parent::processDefinition($definition, $plugin_id);
// Fill in the provider as default values for missing keys.
$definition += array(
'title' => $definition['provider'],
'path' => $definition['provider'],
);
}
/**
* Returns an instance for each active search plugin.
*
* @return \Drupal\search\Plugin\SearchInterface[]
* An array of active search plugins, keyed by their ID.
*/
public function getActivePlugins() {
$plugins = array();
foreach ($this->getActiveDefinitions() as $plugin_id => $definition) {
$plugins[$plugin_id] = $this->createInstance($plugin_id);
}
return $plugins;
}
/**
* Returns an instance for each active plugin that implements \Drupal\search\Plugin\SearchIndexingInterface.
*
* @return \Drupal\search\Plugin\SearchInterface[]
* An array of active search plugins, keyed by their ID.
*/
public function getActiveIndexingPlugins() {
$plugins = array();
foreach ($this->getActiveDefinitions() as $plugin_id => $definition) {
if (is_subclass_of($definition['class'], '\Drupal\search\Plugin\SearchIndexingInterface')) {
$plugins[$plugin_id] = $this->createInstance($plugin_id);
}
}
return $plugins;
}
/**
* Returns definitions for active search plugins keyed by their ID.
*
* @return array
* An array of active search plugin definitions, keyed by their ID.
*/
public function getActiveDefinitions() {
$active_definitions = array();
$active_config = $this->configFactory->get('search.settings')->get('active_plugins');
$active_plugins = $active_config ? array_flip($active_config) : array();
foreach ($this->getDefinitions() as $plugin_id => $definition) {
if (isset($active_plugins[$plugin_id])) {
$active_definitions[$plugin_id] = $definition;
}
}
return $active_definitions;
}
/**
* Check whether access is allowed to search results from a given plugin.
*
* @param string $plugin_id
* The id of the plugin being checked.
* @param \Drupal\Core\Session\AccountInterface $account
* The account being checked for access
*
* @return bool
* TRUE if access is allowed, FALSE otherwise.
*/
public function pluginAccess($plugin_id, AccountInterface $account) {
$definition = $this->getDefinition($plugin_id);
if (empty($definition['class'])) {
return FALSE;
}
// Plugins that implement AccessibleInterface can deny access.
if (is_subclass_of($definition['class'], '\Drupal\Core\TypedData\AccessibleInterface')) {
return $this->createInstance($plugin_id)->access('view', $account);
}
return TRUE;
}
}
......@@ -15,8 +15,8 @@
/**
* Performs a query on the full-text search index for a word or words.
*
* This function is normally only called by each module that supports the
* indexed search (and thus, implements hook_update_index()).
* This function is normally only called by each plugin that supports the
* indexed search.
*
* Results are retrieved in two logical passes. However, the two passes are
* joined together into a single query, and in the case of most simple queries
......@@ -28,7 +28,7 @@
* The second portion of the query further refines this set by verifying
* advanced text conditions (such as negative or phrase matches).
*
* The used query object has the tag 'search_$module' and can be further
* The used query object has the tag 'search_$type' and can be further
* extended with hook_query_alter().
*/
class SearchQuery extends SelectExtender {
......@@ -40,11 +40,11 @@ class SearchQuery extends SelectExtender {
protected $searchExpression;
/**
* The type of search (search module).
* The type of search (search type).
*