diff --git a/core/modules/node/lib/Drupal/node/Plugin/Search/NodeSearch.php b/core/modules/node/lib/Drupal/node/Plugin/Search/NodeSearch.php index 78c7996..6df1200 100644 --- a/core/modules/node/lib/Drupal/node/Plugin/Search/NodeSearch.php +++ b/core/modules/node/lib/Drupal/node/Plugin/Search/NodeSearch.php @@ -14,10 +14,13 @@ use Drupal\Core\Entity\EntityManager; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\KeyValueStore\KeyValueStoreInterface; +use Drupal\Core\Language\Language; use Drupal\Core\Session\AccountInterface; use Drupal\Core\TypedData\AccessibleInterface; -use Drupal\search\Plugin\SearchPluginBase; +use Drupal\Core\Database\Query\Condition; use Drupal\search\Annotation\SearchPlugin; +use Drupal\search\Plugin\SearchPluginBase; +use Drupal\search\Plugin\SearchIndexingInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -30,7 +33,7 @@ * path = "node" * ) */ -class NodeSearch extends SearchPluginBase implements AccessibleInterface { +class NodeSearch extends SearchPluginBase implements AccessibleInterface, SearchIndexingInterface { /** * A database connection object. @@ -68,6 +71,25 @@ class NodeSearch extends SearchPluginBase implements AccessibleInterface { protected $state; /** + * The Drupal account to use for checking for access to advanced search. + * + * @var \Drupal\Core\Session\AccountInterface + */ + protected $account; + + /** + * The list of options and column names for advanced search filters. + * + * @var array + */ + protected $advanced = array( + 'type' => array('column' => 'n.type'), + 'langcode' => array('column' => 'n.langcode'), + 'user' => array('column' => 'n.uid'), + 'term', array('column' => 'ti.tid', 'join' => array('table' => 'taxonomy_index', 'alias' => 'ti', 'condition' => 'n.nid = ti.nid')), + ); + + /** * {@inheritdoc} */ static public function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) { @@ -79,7 +101,8 @@ static public function create(ContainerInterface $container, array $configuratio $container->get('plugin.manager.entity'), $container->get('module_handler'), $container->get('config.factory')->get('search.settings'), - $container->get('keyvalue')->get('state') + $container->get('keyvalue')->get('state'), + $container->get('request')->attributes->get('_account') ); } @@ -102,13 +125,16 @@ static public function create(ContainerInterface $container, array $configuratio * A config object for 'search.settings'. * @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $state * The Drupal state object used to set 'node.cron_last'. + * @param \Drupal\Core\Session\AccountInterface $account + * The $account object to use for checking for access to advanced search. */ - public function __construct(array $configuration, $plugin_id, array $plugin_definition, Connection $database, EntityManager $entity_manager, ModuleHandlerInterface $module_handler, Config $search_settings, KeyValueStoreInterface $state) { + public function __construct(array $configuration, $plugin_id, array $plugin_definition, Connection $database, EntityManager $entity_manager, ModuleHandlerInterface $module_handler, Config $search_settings, KeyValueStoreInterface $state, AccountInterface $account = NULL) { $this->database = $database; $this->entityManager = $entity_manager; $this->moduleHandler = $module_handler; $this->searchSettings = $search_settings; $this->state = $state; + $this->account = $account; parent::__construct($configuration, $plugin_id, $plugin_definition); } @@ -138,11 +164,28 @@ public function execute() { ->addTag('node_access') ->searchExpression($keys, 'node'); - // Insert special keywords. - $query->setOption('type', 'n.type'); - $query->setOption('langcode', 'n.langcode'); - if ($query->setOption('term', 'ti.tid')) { - $query->join('taxonomy_index', 'ti', 'n.nid = ti.nid'); + $parameters = $this->getParameters(); + // Combine the f query strings into one string to simplify searching. + $f = !empty($parameters['f']) ? implode(' ', $parameters['f']) : ''; + if ($f) { + $found = array(); + foreach ($this->advanced as $option => $info) { + $matches = array(); + $pattern = '/(^| )' . $option . ':([^ ]*)( |$)/i'; + if (preg_match_all($pattern, $f, $matches)) { + $found[$option] = $matches[2]; + // Insert special conditions. By default, all are use the OR operator. + $operator = empty($info['operator']) ? 'OR' : $info['operator']; + $where = new Condition($operator); + foreach ($matches[2] as $value) { + $where->condition($info['column'], $value); + } + $query->condition($where); + if (!empty($info['join'])) { + $query->join($info['join']['table'], $info['join']['alias'], $info['join']['condition']); + } + } + } } // Only continue if the first pass query matches. if (!$query->executeFirstPass()) { @@ -304,6 +347,156 @@ public function indexStatus() { return array('remaining' => $remaining, 'total' => $total); } + /** + * {@inheritdoc} + */ + public function searchFormAlter(array &$form, array &$form_state) { + // Keyword boxes: + $form['advanced'] = array( + '#type' => 'details', + '#title' => t('Advanced search'), + '#collapsed' => TRUE, + '#attributes' => array('class' => array('search-advanced')), + '#access' => $this->account && $this->account->hasPermission('use advanced search'), + ); + $form['advanced']['keywords-fieldset'] = array( + '#type' => 'fieldset', + '#title' => t('Keywords'), + '#collapsible' => FALSE, + ); + $form['advanced']['keywords'] = array( + '#prefix' => '
', + '#suffix' => '
', + ); + $form['advanced']['keywords-fieldset']['keywords']['or'] = array( + '#type' => 'textfield', + '#title' => t('Containing any of the words'), + '#size' => 30, + '#maxlength' => 255, + ); + $form['advanced']['keywords-fieldset']['keywords']['phrase'] = array( + '#type' => 'textfield', + '#title' => t('Containing the phrase'), + '#size' => 30, + '#maxlength' => 255, + ); + $form['advanced']['keywords-fieldset']['keywords']['negative'] = array( + '#type' => 'textfield', + '#title' => t('Containing none of the words'), + '#size' => 30, + '#maxlength' => 255, + ); + + // Node types: + $node_types = $this->entityManager->getStorageController('node_type')->loadMultiple(); + $types = array_map('check_plain', node_type_get_names()); + $form['advanced']['types-fieldset'] = array( + '#type' => 'fieldset', + '#title' => t('Types'), + '#collapsible' => FALSE, + ); + $form['advanced']['types-fieldset']['type'] = array( + '#type' => 'checkboxes', + '#title' => t('Only of the type(s)'), + '#prefix' => '
', + '#suffix' => '
', + '#options' => $types, + ); + $form['advanced']['submit'] = array( + '#type' => 'submit', + '#value' => t('Advanced search'), + '#prefix' => '
', + '#suffix' => '
', + '#weight' => 100, + ); + + // Languages: + $language_options = array(); + foreach (language_list(Language::STATE_ALL) as $langcode => $language) { + // Make locked languages appear special in the list. + $language_options[$langcode] = $language->locked ? t('- @name -', array('@name' => $language->name)) : $language->name; + } + if (count($language_options) > 1) { + $form['advanced']['lang-fieldset'] = array( + '#type' => 'fieldset', + '#title' => t('Languages'), + '#collapsible' => FALSE, + '#collapsed' => FALSE, + ); + $form['advanced']['lang-fieldset']['language'] = array( + '#type' => 'checkboxes', + '#title' => t('Languages'), + '#prefix' => '
', + '#suffix' => '
', + '#options' => $language_options, + ); + } + + // Add a submit handler. + $form['#submit'][] = array($this, 'searchFormSubmit'); + } + + /** + * Handle submission of elements added in searchFormAlter(). + * + * @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. + */ + public function searchFormSubmit(array &$form, array &$form_state) { + // Initialize using any existing basic search keywords. + $keys = $form_state['values']['processed_keys']; + $filters = array(); + + // Collect extra restrictions. + if (isset($form_state['values']['type']) && is_array($form_state['values']['type'])) { + // Retrieve selected types - Form API sets the value of unselected + // checkboxes to 0. + foreach ($form_state['values']['type'] as $type) { + if ($type) { + $filters[] = 'type:' . $type; + } + } + } + + if (isset($form_state['values']['term']) && is_array($form_state['values']['term'])) { + foreach ($form_state['values']['term'] as $term) { + $filters[] = 'term:' . $term; + } + } + if (isset($form_state['values']['language']) && is_array($form_state['values']['language'])) { + foreach ($form_state['values']['language'] as $language) { + if ($language) { + $filters[] = 'languate:' . $language; + } + } + } + if ($form_state['values']['or'] != '') { + if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' ' . $form_state['values']['or'], $matches)) { + $keys .= ' ' . implode(' OR ', $matches[1]); + } + } + if ($form_state['values']['negative'] != '') { + if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' ' . $form_state['values']['negative'], $matches)) { + $keys .= ' -' . implode(' -', $matches[1]); + } + } + if ($form_state['values']['phrase'] != '') { + $keys .= ' "' . str_replace('"', ' ', $form_state['values']['phrase']) . '"'; + } + if (!empty($keys)) { + form_set_value($form['basic']['processed_keys'], trim($keys), $form_state); + } + $path = $form_state['action'] . '/' . $keys; + $options = array(); + if ($filters) { + $options['query'] = array('f' => $filters); + } + + $form_state['redirect'] = array($path, $options); + } + /** * {@inheritdoc} */ diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 68deb0d..6dd3787 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -812,164 +812,6 @@ function node_permission() { } /** -<<<<<<< HEAD - * Gathers the rankings from the the hook_ranking() implementations. - * - * @param $query - * A query object that has been extended with the Search DB Extender. - */ -function _node_rankings(SelectExtender $query) { - if ($ranking = Drupal::moduleHandler()->invokeAll('ranking')) { - $tables = &$query->getTables(); - foreach ($ranking as $rank => $values) { - if ($node_rank = variable_get('node_rank_' . $rank, 0)) { - // If the table defined in the ranking isn't already joined, then add it. - if (isset($values['join']) && !isset($tables[$values['join']['alias']])) { - $query->addJoin($values['join']['type'], $values['join']['table'], $values['join']['alias'], $values['join']['on']); - } - $arguments = isset($values['arguments']) ? $values['arguments'] : array(); - $query->addScore($values['score'], $arguments, $node_rank); - } - } - } -} - -/** - * Implements hook_search_info(). - */ -function node_search_info() { - return array( - 'title' => 'Content', - 'path' => 'node', - ); -} - -/** - * Implements hook_search_access(). - */ -function node_search_access() { - return user_access('access content'); -} - -/** - * Implements hook_search_reset(). - */ -function node_search_reset() { - db_update('search_dataset') - ->fields(array('reindex' => REQUEST_TIME)) - ->condition('type', 'node') - ->execute(); -} - -/** - * Implements hook_search_status(). - */ -function node_search_status() { - $total = db_query('SELECT COUNT(*) FROM {node}')->fetchField(); - $remaining = db_query("SELECT COUNT(*) FROM {node} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE d.sid IS NULL OR d.reindex <> 0")->fetchField(); - return array('remaining' => $remaining, 'total' => $total); -} - -/** - * Implements hook_search_admin(). - */ -function node_search_admin() { - // Output form for defining rank factor weights. - $form['content_ranking'] = array( - '#type' => 'details', - '#title' => t('Content ranking'), - ); - $form['content_ranking']['#theme'] = 'node_search_admin'; - $form['content_ranking']['info'] = array( - '#value' => '' . t('The following numbers control which properties the content search should favor when ordering the results. Higher numbers mean more influence, zero means the property is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') . '' - ); - - // Note: reversed to reflect that higher number = higher ranking. - $options = drupal_map_assoc(range(0, 10)); - foreach (Drupal::moduleHandler()->invokeAll('ranking') as $var => $values) { - $form['content_ranking']['factors']['node_rank_' . $var] = array( - '#title' => $values['title'], - '#type' => 'select', - '#options' => $options, - '#default_value' => variable_get('node_rank_' . $var, 0), - ); - } - return $form; -} - -/** - * Implements hook_search_execute(). - */ -function node_search_execute($keys = NULL, $conditions = NULL) { - // Build matching conditions - $query = db_select('search_index', 'i', array('target' => 'slave')) - ->extend('Drupal\search\SearchQuery') - ->extend('Drupal\Core\Database\Query\PagerSelectExtender'); - $query->join('node_field_data', 'n', 'n.nid = i.sid'); - $query - ->condition('n.status', 1) - ->addTag('node_access') - ->searchExpression($keys, 'node'); - - // Insert special keywords. - $query->setOption('type', 'n.type'); - $query->setOption('langcode', 'n.langcode'); - if ($query->setOption('term', 'ti.tid')) { - $query->join('taxonomy_index', 'ti', 'n.nid = ti.nid'); - } - // Only continue if the first pass query matches. - if (!$query->executeFirstPass()) { - return array(); - } - - // Add the ranking expressions. - _node_rankings($query); - - // Load results. - $find = $query - // Add the language code of the indexed item to the result of the query, - // since the node will be rendered using the respective language. - ->fields('i', array('langcode')) - ->limit(10) - ->execute(); - $results = array(); - foreach ($find as $item) { - // Render the node. - $node = node_load($item->sid); - $build = node_view($node, 'search_result', $item->langcode); - unset($build['#theme']); - $node->rendered = drupal_render($build); - - // Fetch comments for snippet. - $node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node, $item->langcode); - - $extra = Drupal::moduleHandler()->invokeAll('node_search_result', array($node, $item->langcode)); - - $language = language_load($item->langcode); - $uri = $node->uri(); - $username = array( - '#theme' => 'username', - '#account' => user_load($node->uid), - ); - $results[] = array( - 'link' => url($uri['path'], array_merge($uri['options'], array('absolute' => TRUE, 'language' => $language))), - 'type' => check_plain(node_get_type_label($node)), - 'title' => $node->label($item->langcode), - 'user' => drupal_render($username), - 'date' => $node->getChangedTime(), - 'node' => $node, - 'extra' => $extra, - 'score' => $item->calculated_score, - 'snippet' => search_excerpt($keys, $node->rendered, $item->langcode), - 'langcode' => $node->language()->id, - ); - } - return $results; -} - -/** -======= ->>>>>>> applly patch https://drupal.org/files/2003482-search-plugin-113.patch * Implements hook_ranking(). */ function node_ranking() { @@ -1710,141 +1552,6 @@ function node_page_view(EntityInterface $node) { /** * Implements hook_form_FORM_ID_alter(). * - * @see node_search_validate() - */ -function node_form_search_form_alter(&$form, $form_state) { - - if (isset($form['plugin_id']) && $form['plugin_id']['#value'] == 'node_search' && user_access('use advanced search')) { - // Keyword boxes: - $form['advanced'] = array( - '#type' => 'details', - '#title' => t('Advanced search'), - '#collapsed' => TRUE, - '#attributes' => array('class' => array('search-advanced')), - ); - $form['advanced']['keywords-fieldset'] = array( - '#type' => 'fieldset', - '#title' => t('Keywords'), - '#collapsible' => FALSE, - ); - $form['advanced']['keywords'] = array( - '#prefix' => '
', - '#suffix' => '
', - ); - $form['advanced']['keywords-fieldset']['keywords']['or'] = array( - '#type' => 'textfield', - '#title' => t('Containing any of the words'), - '#size' => 30, - '#maxlength' => 255, - ); - $form['advanced']['keywords-fieldset']['keywords']['phrase'] = array( - '#type' => 'textfield', - '#title' => t('Containing the phrase'), - '#size' => 30, - '#maxlength' => 255, - ); - $form['advanced']['keywords-fieldset']['keywords']['negative'] = array( - '#type' => 'textfield', - '#title' => t('Containing none of the words'), - '#size' => 30, - '#maxlength' => 255, - ); - - // Node types: - $types = array_map('check_plain', node_type_get_names()); - $form['advanced']['types-fieldset'] = array( - '#type' => 'fieldset', - '#title' => t('Types'), - '#collapsible' => FALSE, - ); - $form['advanced']['types-fieldset']['type'] = array( - '#type' => 'checkboxes', - '#title' => t('Only of the type(s)'), - '#prefix' => '
', - '#suffix' => '
', - '#options' => $types, - ); - $form['advanced']['submit'] = array( - '#type' => 'submit', - '#value' => t('Advanced search'), - '#prefix' => '
', - '#suffix' => '
', - '#weight' => 100, - ); - - // Languages: - $language_options = array(); - foreach (language_list(Language::STATE_ALL) as $langcode => $language) { - // Make locked languages appear special in the list. - $language_options[$langcode] = $language->locked ? t('- @name -', array('@name' => $language->name)) : $language->name; - } - if (count($language_options) > 1) { - $form['advanced']['lang-fieldset'] = array( - '#type' => 'fieldset', - '#title' => t('Languages'), - '#collapsible' => FALSE, - '#collapsed' => FALSE, - ); - $form['advanced']['lang-fieldset']['language'] = array( - '#type' => 'checkboxes', - '#title' => t('Languages'), - '#prefix' => '
', - '#suffix' => '
', - '#options' => $language_options, - ); - } - - $form['#validate'][] = 'node_search_validate'; - } -} - -/** - * Form validation handler for node_form_search_form_alter(). - */ -function node_search_validate($form, &$form_state) { - // Initialize using any existing basic search keywords. - $keys = $form_state['values']['processed_keys']; - - // Insert extra restrictions into the search keywords string. - if (isset($form_state['values']['type']) && is_array($form_state['values']['type'])) { - // Retrieve selected types - Form API sets the value of unselected - // checkboxes to 0. - $form_state['values']['type'] = array_filter($form_state['values']['type']); - if (count($form_state['values']['type'])) { - $keys = search_expression_insert($keys, 'type', implode(',', array_keys($form_state['values']['type']))); - } - } - - if (isset($form_state['values']['term']) && is_array($form_state['values']['term']) && count($form_state['values']['term'])) { - $keys = search_expression_insert($keys, 'term', implode(',', $form_state['values']['term'])); - } - if (isset($form_state['values']['language']) && is_array($form_state['values']['language'])) { - $languages = array_filter($form_state['values']['language']); - if (count($languages)) { - $keys = search_expression_insert($keys, 'language', implode(',', $languages)); - } - } - if ($form_state['values']['or'] != '') { - if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' ' . $form_state['values']['or'], $matches)) { - $keys .= ' ' . implode(' OR ', $matches[1]); - } - } - if ($form_state['values']['negative'] != '') { - if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' ' . $form_state['values']['negative'], $matches)) { - $keys .= ' -' . implode(' -', $matches[1]); - } - } - if ($form_state['values']['phrase'] != '') { - $keys .= ' "' . str_replace('"', ' ', $form_state['values']['phrase']) . '"'; - } - if (!empty($keys)) { - form_set_value($form['basic']['processed_keys'], trim($keys), $form_state); - } -} - -/** - * Implements hook_form_FORM_ID_alter(). - * * Alters the System module's site information settings form to add a global * default setting for number of posts to show on node listing pages. * diff --git a/core/modules/search/lib/Drupal/search/Annotation/SearchPlugin.php b/core/modules/search/lib/Drupal/search/Annotation/SearchPlugin.php index 5dfda5b..b8ea0ad 100644 --- a/core/modules/search/lib/Drupal/search/Annotation/SearchPlugin.php +++ b/core/modules/search/lib/Drupal/search/Annotation/SearchPlugin.php @@ -12,25 +12,24 @@ /** * 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 { /** - * The ID. + * A unique identifier for the search plugin. * * @var string */ public $id; /** - * The name of the module providing the plugin. - * - * @var string - */ - public $module; - - /** * The path fragment to be added to search/ for the search page. * * @var string @@ -45,5 +44,4 @@ class SearchPlugin extends Plugin { * @var string */ public $title; - } diff --git a/core/modules/search/lib/Drupal/search/Form/SearchSettingsForm.php b/core/modules/search/lib/Drupal/search/Form/SearchSettingsForm.php index 21498c1..5d68083 100644 --- a/core/modules/search/lib/Drupal/search/Form/SearchSettingsForm.php +++ b/core/modules/search/lib/Drupal/search/Form/SearchSettingsForm.php @@ -109,14 +109,14 @@ public function buildForm(array $form, array &$form_state) { // Collect some stats. $remaining = 0; $total = 0; - $active_plugins = $this->searchPluginManager->getActivePlugins(); - foreach ($active_plugins as $plugin) { + + foreach ($this->searchPluginManager->getActiveIndexingPlugins() as $plugin) { if ($status = $plugin->indexStatus()) { $remaining += $status['remaining']; $total += $status['total']; } } - + $active_plugins = $this->searchPluginManager->getActivePlugins(); $this->moduleHandler->loadAllIncludes('admin.inc'); $count = format_plural($remaining, 'There is 1 item left to index.', 'There are @count items left to index.'); $percentage = ((int) min(100, 100 * ($total - $remaining) / max(1, $total))) . '%'; diff --git a/core/modules/search/lib/Drupal/search/Plugin/SearchIndexingInterface.php b/core/modules/search/lib/Drupal/search/Plugin/SearchIndexingInterface.php new file mode 100644 index 0000000..d5c9e4e --- /dev/null +++ b/core/modules/search/lib/Drupal/search/Plugin/SearchIndexingInterface.php @@ -0,0 +1,63 @@ +get('index.cron_limit') (see + * example below). Also, since the cron run could time out and abort in the + * middle of your run, you should update your module's 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(); + +} diff --git a/core/modules/search/lib/Drupal/search/Plugin/SearchInterface.php b/core/modules/search/lib/Drupal/search/Plugin/SearchInterface.php index f2d20d4..d886916 100644 --- a/core/modules/search/lib/Drupal/search/Plugin/SearchInterface.php +++ b/core/modules/search/lib/Drupal/search/Plugin/SearchInterface.php @@ -38,7 +38,7 @@ public function setSearch($keywords, array $parameters, array $attributes); * @return string * The keywords. */ - public function getSearchKeywords(); + public function getKeywords(); /** * Returns the current parameters set using setSearch(). @@ -46,7 +46,7 @@ public function getSearchKeywords(); * @return array * The parameters. */ - public function getSearchParameters(); + public function getParameters(); /** * Returns the currently set attributes (from the request). @@ -54,7 +54,7 @@ public function getSearchParameters(); * @return array * The attributes. */ - public function getSearchAttributes(); + public function getAttributes(); /** * Verifies if the values set via setSearch() are valid and sufficient. @@ -82,47 +82,21 @@ public function execute(); public function buildResults(); /** - * Updates the search index for this plugin. + * Alter the search form when being built for a given 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. + * 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. * - * 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') (see - * example below). Also, since the cron run could time out and abort in the - * middle of your run, you should update your module's 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. + * @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 indexStatus(); + public function searchFormAlter(array &$form, array &$form_state); /** * Adds elements to the search settings form. diff --git a/core/modules/search/lib/Drupal/search/Plugin/SearchPluginBase.php b/core/modules/search/lib/Drupal/search/Plugin/SearchPluginBase.php index 51ffea5..a7cd0f6 100644 --- a/core/modules/search/lib/Drupal/search/Plugin/SearchPluginBase.php +++ b/core/modules/search/lib/Drupal/search/Plugin/SearchPluginBase.php @@ -49,21 +49,21 @@ public function setSearch($keywords, array $parameters, array $attributes) { /** * {@inheritdoc} */ - public function getSearchKeywords() { + public function getKeywords() { return $this->keywords; } /** * {@inheritdoc} */ - public function getSearchParameters() { + public function getParameters() { return $this->searchParameters; } /** * {@inheritdoc} */ - public function getSearchAttributes() { + public function getAttributes() { return $this->searchAttributes; } @@ -87,29 +87,14 @@ public function buildResults() { ); } - /** - * {@inheritdoc} - */ - public function updateIndex() { - // Empty default implementation. - } - - /** + /** * {@inheritdoc} */ - public function resetIndex() { + public function searchFormAlter(array &$form, array &$form_state) { // Empty default implementation. } - /** - * {@inheritdoc} - */ - public function indexStatus() { - // No-op default implementation - return array('remaining' => 0, 'total' => 0); - } - - /** + /** * {@inheritdoc} */ public function addToAdminForm(array &$form, array &$form_state) { diff --git a/core/modules/search/lib/Drupal/search/SearchPluginManager.php b/core/modules/search/lib/Drupal/search/SearchPluginManager.php index 1eaf70a..207023b 100644 --- a/core/modules/search/lib/Drupal/search/SearchPluginManager.php +++ b/core/modules/search/lib/Drupal/search/SearchPluginManager.php @@ -47,7 +47,7 @@ public function processDefinition(&$definition, $plugin_id) { } /** - * Returns definitions for active search plugins. + * Returns an instance for each active search plugin. * * @return \Drupal\search\Plugin\SearchInterface[] * An array of active search plugins, keyed by their ID. @@ -61,6 +61,22 @@ public function getActivePlugins() { } /** + * 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 diff --git a/core/modules/search/lib/Drupal/search/SearchQuery.php b/core/modules/search/lib/Drupal/search/SearchQuery.php index 86887b7..13c02f3 100644 --- a/core/modules/search/lib/Drupal/search/SearchQuery.php +++ b/core/modules/search/lib/Drupal/search/SearchQuery.php @@ -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). * * This maps to the value of the type column in search_index, and is equal - * to the machine-readable name of the module that implements - * hook_search_info(). + * to the machine-readable name of the entity type being indexed, or other + * identifier provided by a search plugin. * * @var string */ @@ -144,21 +144,21 @@ class SearchQuery extends SelectExtender { * * @param $query * A search query string, which can contain options. - * @param $module - * The search module. This maps to {search_index}.type in the database. + * @param $type + * The search type. This maps to {search_index}.type in the database. * * @return * The SearchQuery object. */ - public function searchExpression($expression, $module) { + public function searchExpression($expression, $type) { $this->searchExpression = $expression; - $this->type = $module; + $this->type = $type; return $this; } /** - * Applies a search option and removes it from the search query string. + * Applies a search option and removes it from the search expression string. * * These options are in the form option:value,value2,value3. * diff --git a/core/modules/search/search.module b/core/modules/search/search.module index d0b4cf6..f6231ec 100644 --- a/core/modules/search/search.module +++ b/core/modules/search/search.module @@ -8,6 +8,7 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Component\Utility\Unicode; use Drupal\search\SearchExpression; +use Drupal\search\Plugin\SearchInterface; /** * Matches all 'N' Unicode character classes (numbers) @@ -82,7 +83,7 @@ function search_help($path, $arg) { $output .= '
' . t('Content reindexing') . '
'; $output .= '
' . t('Content-related actions on your site (creating, editing, or deleting content and comments) automatically cause affected content items to be marked for indexing or reindexing at the next cron run. When content is marked for reindexing, the previous content remains in the index until cron runs, at which time it is replaced by the new content. Unlike content-related actions, actions related to the structure of your site do not cause affected content to be marked for reindexing. Examples of structure-related actions that affect content include deleting or editing taxonomy terms, enabling or disabling modules that add text to content (such as Taxonomy, Comment, and field-providing modules), and modifying the fields or display parameters of your content types. If you take one of these actions and you want to ensure that the search index is updated to reflect your changed site structure, you can mark all content for reindexing by clicking the "Re-index site" button on the Search settings page. If you have a lot of content on your site, it may take several cron runs for the content to be reindexed.', array('@searchsettings' => url('admin/config/search/settings'))) . '
'; $output .= '
' . t('Configuring search settings') . '
'; - $output .= '
' . t('Indexing behavior can be adjusted using the Search settings page. Users with Administer search permission can control settings such as the Number of items to index per cron run, Indexing settings (word length), Active search modules, and Content ranking, which lets you adjust the priority in which indexed content is returned in results.', array('@searchsettings' => url('admin/config/search/settings'))) . '
'; + $output .= '
' . t('Indexing behavior can be adjusted using the Search settings page. Users with Administer search permission can control settings such as the Number of items to index per cron run, Indexing settings (word length), Active search plugins, and Content ranking, which lets you adjust the priority in which indexed content is returned in results.', array('@searchsettings' => url('admin/config/search/settings'))) . '
'; $output .= '
' . t('Search block') . '
'; $output .= '
' . t('The Search module includes a default Search form block, which can be enabled and configured on the Blocks administration page. The block is available to users with the Search content permission.', array('@blocks' => url('admin/structure/block'))) . '
'; $output .= '
' . t('Extending Search module') . '
'; @@ -284,7 +285,7 @@ function _search_menu_access($plugin_id) { */ function search_reindex($sid = NULL, $type = NULL, $reindex = FALSE, $langcode = NULL) { if ($type == NULL && $sid == NULL) { - foreach (Drupal::service('plugin.manager.search')->getActivePlugins() as $plugin) { + foreach (Drupal::service('plugin.manager.search')->getActiveIndexingPlugins() as $plugin) { $plugin->resetIndex(); } } @@ -343,7 +344,7 @@ function search_cron() { // to date. drupal_register_shutdown_function('search_update_totals'); - foreach (Drupal::service('plugin.manager.search')->getActivePlugins() as $plugin) { + foreach (Drupal::service('plugin.manager.search')->getActiveIndexingPlugins() as $plugin) { $plugin->updateIndex(); } } @@ -828,7 +829,7 @@ function search_comment_unpublish($comment) { } /** - * Extracts a module-specific search option from a search expression. + * Extracts a type-specific search option from a search expression. * * Search options are added using search_expression_insert(), and retrieved * using search_expression_extract(). They take the form option:value, and @@ -909,15 +910,12 @@ function search_expression_insert($expression, $option, $value = NULL) { /** * Form constructor for the search form. * + * @param Drupal\search\Plugin\SearchInterface $plugin + * A search plugin instance to render the form for. * @param $action * Form action. Defaults to "search/$path", where $path is the search path * associated with the plugin in its definition. This will be run through * url(). - * @param $keys - * The search string entered by the user, containing keywords for the search. - * @param $plugin_id - * The search plugin to render the form for. If not supplied, the default - * search plugin is used. * @param $prompt * Label for the keywords field. Defaults to t('Enter your keywords') if * NULL. Supply '' to omit. @@ -928,20 +926,9 @@ function search_expression_insert($expression, $option, $value = NULL) { * @see search_form_validate() * @see search_form_submit() */ -function search_form($form, &$form_state, $action = '', $keys = '', $plugin_id = NULL, $prompt = NULL) { - if (!$plugin_id) { - $plugin_info = search_get_default_plugin_info(); - } - else { - $info = Drupal::service('plugin.manager.search')->getActiveDefinitions(); - $plugin_info = isset($info[$plugin_id]) ? $info[$plugin_id] : FALSE; - } +function search_form($form, &$form_state, SearchInterface $plugin, $action = '', $prompt = NULL) { - // Sanity check. - if (!$plugin_info) { - form_set_error(NULL, t('Search is currently disabled.'), 'error'); - return $form; - } + $plugin_info = $plugin->getPluginDefinition(); if (!$action) { $action = 'search/' . $plugin_info['path']; @@ -953,12 +940,12 @@ function search_form($form, &$form_state, $action = '', $keys = '', $plugin_id = $form['#action'] = url($action); // Record the $action for later use in redirecting. $form_state['action'] = $action; - $form['plugin_id'] = array('#type' => 'value', '#value' => $plugin_id); + $form['plugin_id'] = array('#type' => 'value', '#value' => $plugin->getPluginId()); $form['basic'] = array('#type' => 'container', '#attributes' => array('class' => array('container-inline'))); $form['basic']['keys'] = array( '#type' => 'search', '#title' => $prompt, - '#default_value' => $keys, + '#default_value' => $plugin->getKeywords(), '#size' => $prompt ? 40 : 20, '#maxlength' => 255, ); @@ -966,6 +953,11 @@ function search_form($form, &$form_state, $action = '', $keys = '', $plugin_id = // that hook into the basic search form. $form['basic']['processed_keys'] = array('#type' => 'value', '#value' => ''); $form['basic']['submit'] = array('#type' => 'submit', '#value' => t('Search')); + // Make sure the default validate and submit handlers are added. + $form['#validate'][] = 'search_form_validate'; + $form['#submit'][] = 'search_form_submit'; + // Allow the plugin to add to or alter the search form. + $plugin->searchFormAlter($form, $form_state); return $form; } diff --git a/core/modules/search/search.pages.inc b/core/modules/search/search.pages.inc index 3e7c575..804e4be 100644 --- a/core/modules/search/search.pages.inc +++ b/core/modules/search/search.pages.inc @@ -66,7 +66,7 @@ function search_view($plugin_id = NULL, $keys = '') { } } // The form may be altered based on whether the search was run. - $build['search_form'] = drupal_get_form('search_form', NULL, $keys, $plugin_id); + $build['search_form'] = drupal_get_form('search_form', $plugin); $build['search_results'] = $results; return $build;