Update to drupal 8.0.0-rc1. For more information, see https://www.drupal.org/node/2582663
This commit is contained in:
parent
eb34d130a8
commit
f32e58e4b1
8476 changed files with 211648 additions and 170042 deletions
|
@ -1,5 +1,5 @@
|
|||
id: d6_search_settings
|
||||
label: Drupal 6 search configuration
|
||||
label: Search configuration
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
source:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
id: d7_search_settings
|
||||
label: Drupal 7 search configuration
|
||||
label: Search configuration
|
||||
migration_tags:
|
||||
- Drupal 7
|
||||
source:
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
id: d6_search_page
|
||||
label: Drupal 6 search page configuration
|
||||
id: search_page
|
||||
label: Search page configuration
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
- Drupal 7
|
||||
source:
|
||||
plugin: variable
|
||||
variables:
|
||||
|
@ -20,6 +21,6 @@ process:
|
|||
path: 'constants/path'
|
||||
plugin: 'constants/plugin'
|
||||
'configuration/rankings':
|
||||
plugin: d6_search_configuration_rankings
|
||||
plugin: search_configuration_rankings
|
||||
destination:
|
||||
plugin: entity:search_page
|
|
@ -74,25 +74,22 @@ function search_help($route_name, RouteMatchInterface $route_match) {
|
|||
case 'help.page.search':
|
||||
$output = '';
|
||||
$output .= '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Search module provides the ability to set up search pages based on plugins provided by other modules. In Drupal core, there are two page-type plugins: the Content page type provides keyword searching for content managed by the Node module, and the Users page type provides keyword searching for registered users. Contributed modules may provide other page-type plugins. For more information, see <a href="!search-module">the online documentation for the Search module</a>.', array('!search-module' => 'https://www.drupal.org/documentation/modules/search')) . '</p>';
|
||||
$output .= '<p>' . t('The Search module provides the ability to set up search pages based on plugins provided by other modules. In Drupal core, there are two page-type plugins: the Content page type provides keyword searching for content managed by the Node module, and the Users page type provides keyword searching for registered users. Contributed modules may provide other page-type plugins. For more information, see the <a href=":search-module">online documentation for the Search module</a>.', array(':search-module' => 'https://www.drupal.org/documentation/modules/search')) . '</p>';
|
||||
$output .= '<h3>' . t('Uses') . '</h3>';
|
||||
$output .= '<dl>';
|
||||
$output .= '<dt>' . t('Configuring search pages') . '</dt>';
|
||||
$output .= '<dd>' . t('To configure search pages, visit the <a href="!search-settings">Search pages page</a>. In the Search pages section, you can add a new search page, edit the configuration of existing search pages, enable and disable search pages, and choose the default search page. Each enabled search page has a URL path starting with <em>search</em>, and each will appear as a tab or local task link on the <a href="!search-url">search page</a>; you can configure the text that is shown in the tab. In addition, some search page plugins have additional settings that you can configure for each search page.', array('!search-settings' => \Drupal::url('entity.search_page.collection'), '!search-url' => \Drupal::url('search.view'))) . '</dd>';
|
||||
$output .= '<dd>' . t('To configure search pages, visit the <a href=":search-settings">Search pages page</a>. In the Search pages section, you can add a new search page, edit the configuration of existing search pages, enable and disable search pages, and choose the default search page. Each enabled search page has a URL path starting with <em>search</em>, and each will appear as a tab or local task link on the <a href=":search-url">search page</a>; you can configure the text that is shown in the tab. In addition, some search page plugins have additional settings that you can configure for each search page.', array(':search-settings' => \Drupal::url('entity.search_page.collection'), ':search-url' => \Drupal::url('search.view'))) . '</dd>';
|
||||
$output .= '<dt>' . t('Managing the search index') . '</dt>';
|
||||
$output .= '<dd>' . t('Some search page plugins, such as the core Content search page, index searchable text using the Drupal core search index, and will not work unless content is indexed. Indexing is done during <em>cron</em> runs, so it requires a <a href="!cron">cron maintenance task</a> to be set up. There are also several settings affecting indexing that can be configured on the <a href="!search-settings">Search pages page</a>: the number of items to index per cron run, the minimum word length to index, and how to handle Chinese, Japanese, and Korean characters.', array('!cron' => \Drupal::url('system.cron_settings'), '!search-settings' => \Drupal::url('entity.search_page.collection'))) . '</dd>';
|
||||
$output .= '<dd>' . t('Modules providing search page plugins generally ensure that 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. However, there are some actions related to the structure of your site that 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 <a href="!search-settings">Search pages page</a>. If you have a lot of content on your site, it may take several cron runs for the content to be reindexed.', array('!search-settings' => \Drupal::url('entity.search_page.collection'))) . '</dd>';
|
||||
$output .= '<dd>' . t('Some search page plugins, such as the core Content search page, index searchable text using the Drupal core search index, and will not work unless content is indexed. Indexing is done during <em>cron</em> runs, so it requires a <a href=":cron">cron maintenance task</a> to be set up. There are also several settings affecting indexing that can be configured on the <a href=":search-settings">Search pages page</a>: the number of items to index per cron run, the minimum word length to index, and how to handle Chinese, Japanese, and Korean characters.', array(':cron' => \Drupal::url('system.cron_settings'), ':search-settings' => \Drupal::url('entity.search_page.collection'))) . '</dd>';
|
||||
$output .= '<dd>' . t('Modules providing search page plugins generally ensure that 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. However, there are some actions related to the structure of your site that 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 <a href=":search-settings">Search pages page</a>. If you have a lot of content on your site, it may take several cron runs for the content to be reindexed.', array(':search-settings' => \Drupal::url('entity.search_page.collection'))) . '</dd>';
|
||||
$output .= '<dt>' . t('Displaying the Search block') . '</dt>';
|
||||
$output .= '<dd>' . t('The Search module includes a block, which can be enabled and configured on the <a href="!blocks">Block layout page</a>, if you have the Block module enabled; the default block title is Search, and it is the Search form block in the Forms category, if you wish to add another instance. The block is available to users with the <a href="!search_permission">Use search</a> permission, and it performs a search using the configured default search page.', array('!blocks' => (\Drupal::moduleHandler()->moduleExists('block')) ? \Drupal::url('block.admin_display') : '#', '!search_permission' => \Drupal::url('user.admin_permissions', array(), array('fragment' => 'module-search')))) . '</dd>';
|
||||
$output .= '<dd>' . t('The Search module includes a block, which can be enabled and configured on the <a href=":blocks">Block layout page</a>, if you have the Block module enabled; the default block title is Search, and it is the Search form block in the Forms category, if you wish to add another instance. The block is available to users with the <a href=":search_permission">Use search</a> permission, and it performs a search using the configured default search page.', array(':blocks' => (\Drupal::moduleHandler()->moduleExists('block')) ? \Drupal::url('block.admin_display') : '#', ':search_permission' => \Drupal::url('user.admin_permissions', array(), array('fragment' => 'module-search')))) . '</dd>';
|
||||
$output .= '<dt>' . t('Searching your site') . '</dt>';
|
||||
$output .= '<dd>' . t('Users with <a href="!search_permission">Use search</a> permission can use the Search block and <a href="!search">Search page</a>. Users with the <a href="!node_permission">View published content</a> permission can use configured search pages of type <em>Content</em> to search for content containing exact keywords; in addition, users with <a href="!search_permission">Use advanced search</a> permission can use more complex search filtering. Users with the <a href="!user_permission">View user information</a> permission can use configured search pages of type <em>Users</em> to search for active users containing the keyword anywhere in the username, and users with the <a href="!user_permission">Administer users</a> permission can search for active and blocked users, by email address or username keyword.', array('!search' => \Drupal::url('search.view'), '!search_permission' => \Drupal::url('user.admin_permissions', array(), array('fragment' => 'module-search')), '!node_permission' => \Drupal::url('user.admin_permissions', array(), array('fragment' => 'module-node')), '!user_permission' => \Drupal::url('user.admin_permissions', array(), array('fragment' => 'module-user')))) . '</dd>';
|
||||
$output .= '<dd>' . t('Users with <a href=":search_permission">Use search</a> permission can use the Search block and <a href=":search">Search page</a>. Users with the <a href=":node_permission">View published content</a> permission can use configured search pages of type <em>Content</em> to search for content containing exact keywords; in addition, users with <a href=":search_permission">Use advanced search</a> permission can use more complex search filtering. Users with the <a href=":user_permission">View user information</a> permission can use configured search pages of type <em>Users</em> to search for active users containing the keyword anywhere in the username, and users with the <a href=":user_permission">Administer users</a> permission can search for active and blocked users, by email address or username keyword.', array(':search' => \Drupal::url('search.view'), ':search_permission' => \Drupal::url('user.admin_permissions', array(), array('fragment' => 'module-search')), ':node_permission' => \Drupal::url('user.admin_permissions', array(), array('fragment' => 'module-node')), ':user_permission' => \Drupal::url('user.admin_permissions', array(), array('fragment' => 'module-user')))) . '</dd>';
|
||||
$output .= '<dt>' . t('Extending the Search module') . '</dt>';
|
||||
$output .= '<dd>' . t('By default, the Search module only supports exact keyword matching in content searches. You can modify this behavior by installing a language-specific stemming module for your language (such as <a href="!porterstemmer_url">Porter Stemmer</a> for American English), which allows words such as walk, walking, and walked to be matched in the Search module. Another approach is to use a third-party search technology with stemming or partial word matching features built in, such as <a href="!solr_url">Apache Solr</a> or <a href="!sphinx_url">Sphinx</a>. There are also contributed modules that provide additional search pages. These and other <a href="!contrib-search">search-related contributed modules</a> can be downloaded by visiting Drupal.org.', array('!contrib-search' => 'https://www.drupal.org/project/project_module?f[2]=im_vid_3%3A105', '!porterstemmer_url' => 'https://www.drupal.org/project/porterstemmer', '!solr_url' => 'https://www.drupal.org/project/apachesolr', '!sphinx_url' => 'https://www.drupal.org/project/sphinx')) . '</dd>';
|
||||
$output .= '<dd>' . t('By default, the Search module only supports exact keyword matching in content searches. You can modify this behavior by installing a language-specific stemming module for your language (such as <a href=":porterstemmer_url">Porter Stemmer</a> for American English), which allows words such as walk, walking, and walked to be matched in the Search module. Another approach is to use a third-party search technology with stemming or partial word matching features built in, such as <a href=":solr_url">Apache Solr</a> or <a href=":sphinx_url">Sphinx</a>. There are also contributed modules that provide additional search pages. These and other <a href=":contrib-search">search-related contributed modules</a> can be downloaded by visiting Drupal.org.', array(':contrib-search' => 'https://www.drupal.org/project/project_module?f[2]=im_vid_3%3A105', ':porterstemmer_url' => 'https://www.drupal.org/project/porterstemmer', ':solr_url' => 'https://www.drupal.org/project/apachesolr', ':sphinx_url' => 'https://www.drupal.org/project/sphinx')) . '</dd>';
|
||||
$output .= '</dl>';
|
||||
return $output;
|
||||
|
||||
case 'entity.search_page.collection':
|
||||
return '<p>' . t('The search engine maintains an index of words found in your site\'s content. To build and maintain this index, a correctly configured <a href="!cron">cron maintenance task</a> is required. Indexing behavior can be adjusted using the settings below.', array('!cron' => \Drupal::url('system.status'))) . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,7 +259,7 @@ function search_simplify($text, $langcode = NULL) {
|
|||
// Lowercase
|
||||
$text = Unicode::strtolower($text);
|
||||
|
||||
// Remove diacitics.
|
||||
// Remove diacritics.
|
||||
$text = \Drupal::service('transliteration')->removeDiacritics($text);
|
||||
|
||||
// Call an external processor for word handling.
|
||||
|
@ -383,10 +380,22 @@ function search_index_split($text, $langcode = NULL) {
|
|||
* Helper function for array_walk in search_index_split.
|
||||
*/
|
||||
function _search_index_truncate(&$text) {
|
||||
// Use a static array to avoid re-truncating text we've done before.
|
||||
// The same words may often be passed in during excerpt generation.
|
||||
static $truncated = array();
|
||||
if (isset($truncated[$text])) {
|
||||
$text = $truncated[$text];
|
||||
return;
|
||||
}
|
||||
|
||||
// If we didn't find it in the static array, perform the operation.
|
||||
$original = $text;
|
||||
if (is_numeric($text)) {
|
||||
$text = ltrim($text, '0');
|
||||
}
|
||||
$text = Unicode::truncate($text, 50);
|
||||
// Save it for the next time.
|
||||
$truncated[$original] = $text;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -632,7 +641,9 @@ function search_mark_for_reindex($type = NULL, $sid = NULL, $langcode = NULL) {
|
|||
*/
|
||||
function search_excerpt($keys, $text, $langcode = NULL) {
|
||||
// We highlight around non-indexable or CJK characters.
|
||||
$boundary = '(?:(?<=[' . Unicode::PREG_CLASS_WORD_BOUNDARY . PREG_CLASS_CJK . '])|(?=[' . Unicode::PREG_CLASS_WORD_BOUNDARY . PREG_CLASS_CJK . ']))';
|
||||
$boundary_character = '[' . Unicode::PREG_CLASS_WORD_BOUNDARY . PREG_CLASS_CJK . ']';
|
||||
$preceded_by_boundary = '(?<=' . $boundary_character . ')';
|
||||
$followed_by_boundary = '(?=' . $boundary_character . ')';
|
||||
|
||||
// Extract positive keywords and phrases.
|
||||
preg_match_all('/ ("([^"]+)"|(?!OR)([^" ]+))/', ' ' . $keys, $matches);
|
||||
|
@ -648,7 +659,7 @@ function search_excerpt($keys, $text, $langcode = NULL) {
|
|||
// search_simplify().
|
||||
$temp_keys = array();
|
||||
foreach ($keys as $key) {
|
||||
$key = _search_find_match_with_simplify($key, $text, $boundary, $langcode);
|
||||
$key = _search_find_match_with_simplify($key, $text, $boundary_character, $langcode);
|
||||
if (isset($key)) {
|
||||
// Quote slashes so they can be used in regular expressions.
|
||||
$temp_keys[] = preg_quote($key, '/');
|
||||
|
@ -683,7 +694,7 @@ function search_excerpt($keys, $text, $langcode = NULL) {
|
|||
// we are requiring a match on a word boundary, make sure $text starts
|
||||
// and ends with a space.
|
||||
$matches = array();
|
||||
if (preg_match('/' . $boundary . $key . $boundary . '/iu', ' ' . $text . ' ', $matches, PREG_OFFSET_CAPTURE, $look_start[$key])) {
|
||||
if (preg_match('/' . $preceded_by_boundary . $key . $followed_by_boundary . '/iu', ' ' . $text . ' ', $matches, PREG_OFFSET_CAPTURE, $look_start[$key])) {
|
||||
$found_position = $matches[0][1];
|
||||
$look_start[$key] = $found_position + 1;
|
||||
// Keep track of which keys we found this time, in case we need to
|
||||
|
@ -762,13 +773,13 @@ function search_excerpt($keys, $text, $langcode = NULL) {
|
|||
|
||||
// Combine the text chunks with "…" separators. The "…" needs to be
|
||||
// translated. Let translators have the … separator text as one chunk.
|
||||
$ellipses = explode('!excerpt', t('… !excerpt … !excerpt …'));
|
||||
$ellipses = explode('@excerpt', t('… @excerpt … @excerpt …'));
|
||||
$text = (isset($new_ranges[0]) ? '' : $ellipses[0]) . implode($ellipses[1], $out) . (($max_end < strlen($text) - 1) ? $ellipses[2] : '');
|
||||
$text = Html::escape($text);
|
||||
|
||||
// Highlight keywords. Must be done at once to prevent conflicts ('strong'
|
||||
// and '<strong>').
|
||||
$text = trim(preg_replace('/' . $boundary . '(?:' . implode('|', $keys) . ')' . $boundary . '/iu', '<strong>\0</strong>', ' ' . $text . ' '));
|
||||
$text = trim(preg_replace('/' . $preceded_by_boundary . '(?:' . implode('|', $keys) . ')' . $followed_by_boundary . '/iu', '<strong>\0</strong>', ' ' . $text . ' '));
|
||||
return [
|
||||
'#markup' => $text,
|
||||
'#allowed_tags' => ['strong']
|
||||
|
@ -787,7 +798,8 @@ function search_excerpt($keys, $text, $langcode = NULL) {
|
|||
* @param string $text
|
||||
* The text to search for the keyword.
|
||||
* @param string $boundary
|
||||
* Regular expression for boundary characters between words.
|
||||
* Regular expression for the boundary character class (characters that
|
||||
* indicate spaces between words).
|
||||
* @param string|null $langcode
|
||||
* Language code for the language of $text, if known.
|
||||
*
|
||||
|
@ -798,6 +810,9 @@ function search_excerpt($keys, $text, $langcode = NULL) {
|
|||
* not located, NULL is returned.
|
||||
*/
|
||||
function _search_find_match_with_simplify($key, $text, $boundary, $langcode = NULL) {
|
||||
$preceded_by_boundary = '(?<=' . $boundary . ')';
|
||||
$followed_by_boundary = '(?=' . $boundary . ')';
|
||||
|
||||
// See if $key appears as-is. When testing, make sure $text starts/ends with
|
||||
// a space, because we require $key to be surrounded by word boundary
|
||||
// characters.
|
||||
|
@ -805,8 +820,19 @@ function _search_find_match_with_simplify($key, $text, $boundary, $langcode = NU
|
|||
if ($temp == '') {
|
||||
return NULL;
|
||||
}
|
||||
if (preg_match('/' . $boundary . preg_quote($temp, '/') . $boundary . '/iu', ' ' . $text . ' ')) {
|
||||
return $key;
|
||||
if (preg_match('/' . $preceded_by_boundary . preg_quote($temp, '/') . $followed_by_boundary . '/iu', ' ' . $text . ' ')) {
|
||||
return $temp;
|
||||
}
|
||||
|
||||
// See if there is a match after lower-casing and removing diacritics in
|
||||
// both, which should preserve the string length.
|
||||
$new_text = Unicode::strtolower($text);
|
||||
$new_text = \Drupal::service('transliteration')->removeDiacritics($new_text);
|
||||
$new_key = Unicode::strtolower($temp);
|
||||
$new_key = \Drupal::service('transliteration')->removeDiacritics($new_key);
|
||||
if (preg_match('/' . $preceded_by_boundary . preg_quote($new_key, '/') . $followed_by_boundary . '/u', ' ' . $new_text . ' ')) {
|
||||
$position = Unicode::strpos($new_text, $new_key);
|
||||
return Unicode::substr($text, $position, Unicode::strlen($new_key));
|
||||
}
|
||||
|
||||
// Run both text and key through search_simplify.
|
||||
|
@ -818,45 +844,69 @@ function _search_find_match_with_simplify($key, $text, $boundary, $langcode = NU
|
|||
}
|
||||
|
||||
// Split $text into words, keeping track of where the word boundaries are.
|
||||
$words = preg_split('/' . $boundary . '/iu', $text, NULL, PREG_SPLIT_OFFSET_CAPTURE);
|
||||
$words = preg_split('/' . $boundary . '+/u', $text, NULL, PREG_SPLIT_OFFSET_CAPTURE);
|
||||
// Add an entry pointing to the end of the string, for the loop below.
|
||||
$words[] = array('', strlen($text));
|
||||
$num_words = count($words);
|
||||
|
||||
// Find the smallest segment of complete words in $text that we can simplify
|
||||
// to match $simplified_key.
|
||||
$start_position = 0;
|
||||
$word_end = 0;
|
||||
for ($word_index = 0; $word_index < $num_words; $word_index++) {
|
||||
// See if we can move the starting position out from our previously-saved
|
||||
// best position to here and still have a match.
|
||||
$trial_position = $words[$word_index][1];
|
||||
if ($trial_position < strlen($text)) {
|
||||
$candidate = substr($text, $trial_position);
|
||||
$test_text = trim(search_simplify($candidate, $langcode));
|
||||
if (strpos($test_text, $simplified_key) !== FALSE) {
|
||||
$start_position = $trial_position;
|
||||
$word_end = $trial_position + strlen($words[$word_index][0]);
|
||||
continue;
|
||||
}
|
||||
// Using a binary search, find the earliest possible ending position in
|
||||
// $text where it will still match the keyword after applying
|
||||
// search_simplify().
|
||||
$start_index = 0;
|
||||
$start_pos = $words[$start_index][1];
|
||||
$min_end_index = 1;
|
||||
$max_end_index = count($words) - 1;
|
||||
while ($max_end_index > $min_end_index) {
|
||||
// Check the index half way between min and max. See if we ended there,
|
||||
// if we would still have a match.
|
||||
$proposed_end_index = floor(($max_end_index + $min_end_index) / 2);
|
||||
$proposed_end_pos = $words[$proposed_end_index][1];
|
||||
// Since the split was done with preg_split(), the positions are byte counts
|
||||
// not character counts, so use substr() not Unicode::substr() here.
|
||||
$trial_text = trim(search_simplify(substr($text, $start_pos, $proposed_end_pos - $start_pos), $langcode));
|
||||
if (strpos($trial_text, $simplified_key) !== FALSE) {
|
||||
// The proposed endpoint is fine, text still matches.
|
||||
$max_end_index = $proposed_end_index;
|
||||
}
|
||||
|
||||
// See if we can end at our currently-saved word-ending position and still
|
||||
// match, in which case this is the minimal matching string.
|
||||
if ($word_end > $start_position) {
|
||||
$candidate = substr($text, $start_position, $word_end - $start_position);
|
||||
$test_text = trim(search_simplify($candidate, $langcode));
|
||||
if (strpos($test_text, $simplified_key) !== FALSE) {
|
||||
return $candidate;
|
||||
}
|
||||
else {
|
||||
// The proposed endpoint index is too early, so the earliest possible
|
||||
// OK ending point would be the next index.
|
||||
$min_end_index = $proposed_end_index + 1;
|
||||
}
|
||||
|
||||
// Save the end position of this word for the next time through this loop.
|
||||
$word_end = $trial_position + strlen($words[$word_index][0]);
|
||||
}
|
||||
|
||||
// If we get here, we couldn't find a match.
|
||||
return NULL;
|
||||
// Now do the same for the starting position: using a binary search, find the
|
||||
// latest possible starting position in $text where it will still match the
|
||||
// keyword after applying search_simplify().
|
||||
$end_index = $min_end_index;
|
||||
$end_pos = $words[$end_index][1];
|
||||
$min_start_index = 0;
|
||||
$max_start_index = $end_index - 1;
|
||||
while ($max_start_index > $min_start_index) {
|
||||
// Check the index half way between min and max. See if we started there,
|
||||
// if we would still have a match.
|
||||
$proposed_start_index = ceil(($max_start_index + $min_start_index) / 2);
|
||||
$proposed_start_pos = $words[$proposed_start_index][1];
|
||||
// Since the split was done with preg_split(), the positions are byte counts
|
||||
// not character counts, so use substr() not Unicode::substr() here.
|
||||
$trial_text = trim(search_simplify(substr($text, $proposed_start_pos, $end_pos - $proposed_start_pos), $langcode));
|
||||
if (strpos($trial_text, $simplified_key) !== FALSE) {
|
||||
// The proposed start point is fine, text still matches.
|
||||
$min_start_index = $proposed_start_index;
|
||||
}
|
||||
else {
|
||||
// The proposed start point index is too late, so the latest possible
|
||||
// OK starting point would be the previous index.
|
||||
$max_start_index = $proposed_start_index - 1;
|
||||
}
|
||||
}
|
||||
$start_index = $max_start_index;
|
||||
|
||||
// Return the matching text. We need to use substr() here and not the
|
||||
// Unicode::substr() function, because the indices in $words came from
|
||||
// preg_split(), so they are Unicode-safe byte positions, not character
|
||||
// positions.
|
||||
return trim(substr($text, $words[$start_index][1], $words[$end_index][1] - $words[$start_index][1]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
namespace Drupal\search\Controller;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
use Drupal\Core\Config\ConfigFactory;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\search\SearchPageInterface;
|
||||
use Drupal\search\SearchPageRepositoryInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
@ -35,6 +37,13 @@ class SearchController extends ControllerBase {
|
|||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* The renderer.
|
||||
*
|
||||
* @var \Drupal\Core\Render\RendererInterface
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* Constructs a new search controller.
|
||||
*
|
||||
|
@ -42,10 +51,13 @@ class SearchController extends ControllerBase {
|
|||
* The search page repository.
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* A logger instance.
|
||||
* @param \Drupal\Core\Render\RendererInterface $renderer
|
||||
* The renderer.
|
||||
*/
|
||||
public function __construct(SearchPageRepositoryInterface $search_page_repository, LoggerInterface $logger) {
|
||||
public function __construct(SearchPageRepositoryInterface $search_page_repository, LoggerInterface $logger, RendererInterface $renderer) {
|
||||
$this->searchPageRepository = $search_page_repository;
|
||||
$this->logger = $logger;
|
||||
$this->renderer = $renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,7 +66,8 @@ class SearchController extends ControllerBase {
|
|||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('search.search_page_repository'),
|
||||
$container->get('logger.factory')->get('search')
|
||||
$container->get('logger.factory')->get('search'),
|
||||
$container->get('renderer')
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -75,6 +88,7 @@ class SearchController extends ControllerBase {
|
|||
|
||||
// Build the form first, because it may redirect during the submit,
|
||||
// and we don't want to build the results based on last time's request.
|
||||
$build['#cache']['contexts'][] = 'url.query_args:keys';
|
||||
if ($request->query->has('keys')) {
|
||||
$keys = trim($request->get('keys'));
|
||||
$plugin->setSearch($keys, $request->query->all(), $request->attributes->all());
|
||||
|
@ -117,14 +131,16 @@ class SearchController extends ControllerBase {
|
|||
'#markup' => '<h3>' . $this->t('Your search yielded no results.') . '</h3>',
|
||||
),
|
||||
'#list_type' => 'ol',
|
||||
'#cache' => array(
|
||||
'tags' => $entity->getCacheTags(),
|
||||
),
|
||||
'#context' => array(
|
||||
'plugin' => $plugin->getPluginId(),
|
||||
),
|
||||
);
|
||||
|
||||
$this->renderer->addCacheableDependency($build, $entity);
|
||||
if ($plugin instanceof CacheableDependencyInterface) {
|
||||
$this->renderer->addCacheableDependency($build, $plugin);
|
||||
}
|
||||
|
||||
// If this plugin uses a search index, then also add the cache tag tracking
|
||||
// that search index, so that cached search result pages are invalidated
|
||||
// when necessary.
|
||||
|
|
|
@ -89,7 +89,6 @@ class SearchBlockForm extends FormBase {
|
|||
|
||||
$route = 'search.view_' . $entity_id;
|
||||
$form['#action'] = $this->url($route);
|
||||
$form['#token'] = FALSE;
|
||||
$form['#method'] = 'get';
|
||||
|
||||
$form['keys'] = array(
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
namespace Drupal\search\Plugin;
|
||||
|
||||
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
|
||||
use Drupal\Core\Cache\RefinableCacheableDependencyTrait;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
|
@ -16,7 +18,9 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
/**
|
||||
* Defines a base class for plugins wishing to support search.
|
||||
*/
|
||||
abstract class SearchPluginBase extends PluginBase implements ContainerFactoryPluginInterface, SearchInterface {
|
||||
abstract class SearchPluginBase extends PluginBase implements ContainerFactoryPluginInterface, SearchInterface, RefinableCacheableDependencyInterface {
|
||||
|
||||
use RefinableCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* The keywords to use in a search.
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\search\Plugin\migrate\process\SearchConfigurationRankings.
|
||||
*/
|
||||
|
||||
namespace Drupal\search\Plugin\migrate\process;
|
||||
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Generate configuration rankings.
|
||||
*
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "search_configuration_rankings"
|
||||
* )
|
||||
*/
|
||||
class SearchConfigurationRankings extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Generate the configuration rankings.
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
$return = array();
|
||||
foreach ($row->getSource() as $name => $rank) {
|
||||
if (substr($name, 0, 10) == 'node_rank_' && is_numeric($rank)) {
|
||||
$return[substr($name, 10)] = $rank;
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
}
|
|
@ -23,14 +23,14 @@ class SearchPageAccessControlHandler extends EntityAccessControlHandler {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
|
||||
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
|
||||
/** @var $entity \Drupal\search\SearchPageInterface */
|
||||
if (in_array($operation, array('delete', 'disable'))) {
|
||||
if ($entity->isDefaultSearch()) {
|
||||
return AccessResult::forbidden()->cacheUntilEntityChanges($entity);
|
||||
}
|
||||
else {
|
||||
return parent::checkAccess($entity, $operation, $langcode, $account)->cacheUntilEntityChanges($entity);
|
||||
return parent::checkAccess($entity, $operation, $account)->cacheUntilEntityChanges($entity);
|
||||
}
|
||||
}
|
||||
if ($operation == 'view') {
|
||||
|
@ -43,7 +43,7 @@ class SearchPageAccessControlHandler extends EntityAccessControlHandler {
|
|||
}
|
||||
return AccessResult::allowed()->cacheUntilEntityChanges($entity);
|
||||
}
|
||||
return parent::checkAccess($entity, $operation, $langcode, $account);
|
||||
return parent::checkAccess($entity, $operation, $account);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -188,6 +188,7 @@ class SearchPageListBuilder extends DraggableListBuilder implements FormInterfac
|
|||
'#type' => 'details',
|
||||
'#title' => $this->t('Indexing progress'),
|
||||
'#open' => TRUE,
|
||||
'#description' => $this->t('Only items in the index will appear in search results. To build and maintain the index, a correctly configured <a href=":cron">cron maintenance task</a> is required.', array(':cron' => \Drupal::url('system.cron_settings'))),
|
||||
);
|
||||
$form['status']['status'] = array('#markup' => $status);
|
||||
$form['status']['wipe'] = array(
|
||||
|
@ -210,7 +211,7 @@ class SearchPageListBuilder extends DraggableListBuilder implements FormInterfac
|
|||
'#title' => $this->t('Number of items to index per cron run'),
|
||||
'#default_value' => $search_settings->get('index.cron_limit'),
|
||||
'#options' => $items,
|
||||
'#description' => $this->t('The maximum number of items indexed in each pass of a <a href="@cron">cron maintenance task</a>. If necessary, reduce the number of items to prevent timeouts and memory errors while indexing. Some search page types may have their own setting for this.', array('@cron' => \Drupal::url('system.status'))),
|
||||
'#description' => $this->t('The maximum number of items indexed in each run of the <a href=":cron">cron maintenance task</a>. If necessary, reduce the number of items to prevent timeouts and memory errors while indexing. Some search page types may have their own setting for this.', array(':cron' => \Drupal::url('system.cron_settings'))),
|
||||
);
|
||||
// Indexing settings:
|
||||
$form['indexing_settings'] = array(
|
||||
|
@ -227,7 +228,7 @@ class SearchPageListBuilder extends DraggableListBuilder implements FormInterfac
|
|||
'#default_value' => $search_settings->get('index.minimum_word_size'),
|
||||
'#min' => 1,
|
||||
'#max' => 1000,
|
||||
'#description' => $this->t('The number of characters a word has to be to be indexed. A lower setting means better search result ranking, but also a larger database. Each search query must contain at least one keyword that is this size (or longer).')
|
||||
'#description' => $this->t('The minimum character length for a word to be added to the index. Searches must include a keyword of at least this length.'),
|
||||
);
|
||||
$form['indexing_settings']['overlap_cjk'] = array(
|
||||
'#type' => 'checkbox',
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
|
||||
namespace Drupal\search\Tests\Migrate\d6;
|
||||
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
|
||||
use Drupal\search\Entity\SearchPage;
|
||||
|
||||
/**
|
||||
* Upgrade search rank settings to search.page.*.yml.
|
||||
|
@ -19,18 +19,16 @@ use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
|
|||
class MigrateSearchPageTest extends MigrateDrupal6TestBase {
|
||||
|
||||
/**
|
||||
* The modules to be enabled during the test.
|
||||
*
|
||||
* @var array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
static $modules = array('node', 'search');
|
||||
public static $modules = ['search'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->executeMigration('d6_search_page');
|
||||
$this->executeMigration('search_page');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -39,11 +37,13 @@ class MigrateSearchPageTest extends MigrateDrupal6TestBase {
|
|||
public function testSearchPage() {
|
||||
$id = 'node_search';
|
||||
/** @var \Drupal\search\Entity\SearchPage $search_page */
|
||||
$search_page = entity_load('search_page', $id);
|
||||
$search_page = SearchPage::load($id);
|
||||
$this->assertIdentical($id, $search_page->id());
|
||||
$configuration = $search_page->getPlugin()->getConfiguration();
|
||||
$this->assertIdentical($configuration['rankings'], array(
|
||||
'comments' => 5,
|
||||
'promote' => 0,
|
||||
'recent' => 0,
|
||||
'relevance' => 2,
|
||||
'sticky' => 8,
|
||||
'views' => 1,
|
||||
|
@ -57,12 +57,15 @@ class MigrateSearchPageTest extends MigrateDrupal6TestBase {
|
|||
->condition('name', 'node_rank_comments')
|
||||
->execute();
|
||||
|
||||
$migration = entity_load_unchanged('migration', 'd6_search_page');
|
||||
$executable = new MigrateExecutable($migration, $this);
|
||||
$executable->import();
|
||||
/** @var \Drupal\migrate\Entity\MigrationInterface $migration */
|
||||
$migration = \Drupal::entityManager()
|
||||
->getStorage('migration')
|
||||
->loadUnchanged('search_page');
|
||||
// Indicate we're rerunning a migration that's already run.
|
||||
$migration->getIdMap()->prepareUpdate();
|
||||
$this->executeMigration($migration);
|
||||
|
||||
$search_page = entity_load('search_page', $id);
|
||||
$configuration = $search_page->getPlugin()->getConfiguration();
|
||||
$configuration = SearchPage::load($id)->getPlugin()->getConfiguration();
|
||||
$this->assertIdentical(4, $configuration['rankings']['comments']);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,11 +20,9 @@ class MigrateSearchSettingsTest extends MigrateDrupal6TestBase {
|
|||
use SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('search');
|
||||
public static $modules = ['search'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\search\Tests\Migrate\d7\MigrateSearchPageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\search\Tests\Migrate\d7;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\migrate_drupal\Tests\d7\MigrateDrupal7TestBase;
|
||||
use Drupal\search\Entity\SearchPage;
|
||||
|
||||
/**
|
||||
* Upgrade search rank settings to search.page.*.yml.
|
||||
*
|
||||
* @group migrate_drupal_7
|
||||
*/
|
||||
class MigrateSearchPageTest extends MigrateDrupal7TestBase {
|
||||
|
||||
/**
|
||||
* The modules to be enabled during the test.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('node', 'search');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->executeMigration('search_page');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Drupal 7 search ranking to Drupal 8 search page entity migration.
|
||||
*/
|
||||
public function testSearchPage() {
|
||||
$id = 'node_search';
|
||||
/** @var \Drupal\search\Entity\SearchPage $search_page */
|
||||
$search_page = SearchPage::load($id);
|
||||
$this->assertIdentical($id, $search_page->id());
|
||||
$configuration = $search_page->getPlugin()->getConfiguration();
|
||||
$expected_rankings = array(
|
||||
'comments' => 0,
|
||||
'promote' => 0,
|
||||
'relevance' => 2,
|
||||
'sticky' => 0,
|
||||
'views' => 0,
|
||||
);
|
||||
$this->assertIdentical($expected_rankings, $configuration['rankings']);
|
||||
$this->assertIdentical('node', $search_page->getPath());
|
||||
|
||||
// Test that we can re-import using the EntitySearchPage destination.
|
||||
Database::getConnection('default', 'migrate')
|
||||
->update('variable')
|
||||
->fields(array('value' => serialize(4)))
|
||||
->condition('name', 'node_rank_comments')
|
||||
->execute();
|
||||
|
||||
/** @var \Drupal\migrate\Entity\MigrationInterface $migration */
|
||||
$migration = \Drupal::entityManager()
|
||||
->getStorage('migration')
|
||||
->loadUnchanged('search_page');
|
||||
// Indicate we're rerunning a migration that's already run.
|
||||
$migration->getIdMap()->prepareUpdate();
|
||||
$this->executeMigration($migration);
|
||||
|
||||
$configuration = SearchPage::load($id)->getPlugin()->getConfiguration();
|
||||
$this->assertIdentical(4, $configuration['rankings']['comments']);
|
||||
}
|
||||
|
||||
}
|
|
@ -59,6 +59,7 @@ class SearchConfigSettingsFormTest extends SearchTestBase {
|
|||
// Enable the search block.
|
||||
$this->drupalPlaceBlock('search_form_block');
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -35,35 +35,33 @@ class SearchExcerptTest extends KernelTestBase {
|
|||
function testSearchExcerpt() {
|
||||
// Make some text with entities and tags.
|
||||
$text = 'The <strong>quick</strong> <a href="#">brown</a> fox & jumps <h2>over</h2> the lazy dog';
|
||||
// Note: The search_excerpt() function adds some extra spaces -- not
|
||||
// important for HTML formatting. Remove these for comparison.
|
||||
$expected = 'The quick brown fox & jumps over the lazy dog';
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('nothing', $text));
|
||||
$result = $this->doSearchExcerpt('nothing', $text);
|
||||
$this->assertEqual(preg_replace('| +|', ' ', $result), $expected, 'Entire string, stripped of HTML tags, is returned when keyword is not found in short string');
|
||||
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('fox', $text));
|
||||
$result = $this->doSearchExcerpt('fox', $text);
|
||||
$this->assertEqual($result, 'The quick brown <strong>fox</strong> & jumps over the lazy dog', 'Found keyword is highlighted');
|
||||
|
||||
$expected = '<strong>The</strong> quick brown fox & jumps over <strong>the</strong> lazy dog';
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('The', $text));
|
||||
$result = $this->doSearchExcerpt('The', $text);
|
||||
$this->assertEqual(preg_replace('| +|', ' ', $result), $expected, 'Keyword is highlighted at beginning of short string');
|
||||
|
||||
$expected = 'The quick brown fox & jumps over the lazy <strong>dog</strong>';
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('dog', $text));
|
||||
$result = $this->doSearchExcerpt('dog', $text);
|
||||
$this->assertEqual(preg_replace('| +|', ' ', $result), $expected, 'Keyword is highlighted at end of short string');
|
||||
|
||||
$longtext = str_repeat(str_replace('brown', 'silver', $text) . ' ', 10) . $text . str_repeat(' ' . str_replace('brown', 'pink', $text), 10);
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('brown', $longtext));
|
||||
$result = $this->doSearchExcerpt('brown', $longtext);
|
||||
$expected = '… silver fox & jumps over the lazy dog The quick <strong>brown</strong> fox & jumps over the lazy dog The quick …';
|
||||
$this->assertEqual($result, $expected, 'Snippet around keyword in long text is correctly capped');
|
||||
|
||||
$longtext = str_repeat($text . ' ', 10);
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('nothing', $longtext));
|
||||
$result = $this->doSearchExcerpt('nothing', $longtext);
|
||||
$expected = 'The quick brown fox & jumps over the lazy dog';
|
||||
$this->assertTrue(strpos($result, $expected) === 0, 'When keyword is not found in long string, return value starts as expected');
|
||||
|
||||
$entities = str_repeat('készítése ', 20);
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('nothing', $entities));
|
||||
$result = $this->doSearchExcerpt('nothing', $entities);
|
||||
$this->assertFalse(strpos($result, '&'), 'Entities are not present in excerpt');
|
||||
$this->assertTrue(strpos($result, 'í') > 0, 'Entities are converted in excerpt');
|
||||
|
||||
|
@ -82,6 +80,8 @@ class SearchExcerptTest extends KernelTestBase {
|
|||
* and compares them with strings that contain the original unsimplified word.
|
||||
*/
|
||||
function testSearchExcerptSimplified() {
|
||||
$start_time = microtime(TRUE);
|
||||
|
||||
$lorem1 = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vitae arcu at leo cursus laoreet. Curabitur dui tortor, adipiscing malesuada tempor in, bibendum ac diam. Cras non tellus a libero pellentesque condimentum. What is a Drupalism? Suspendisse ac lacus libero. Ut non est vel nisl faucibus interdum nec sed leo. Pellentesque sem risus, vulputate eu semper eget, auctor in libero.';
|
||||
$lorem2 = 'Ut fermentum est vitae metus convallis scelerisque. Phasellus pellentesque rhoncus tellus, eu dignissim purus posuere id. Quisque eu fringilla ligula. Morbi ullamcorper, lorem et mattis egestas, tortor neque pretium velit, eget eleifend odio turpis eu purus. Donec vitae metus quis leo pretium tincidunt a pulvinar sem. Morbi adipiscing laoreet mauris vel placerat. Nullam elementum, nisl sit amet scelerisque malesuada, dolor nunc hendrerit quam, eu ultrices erat est in orci.';
|
||||
|
||||
|
@ -89,37 +89,37 @@ class SearchExcerptTest extends KernelTestBase {
|
|||
$text = $lorem1 . ' Number: 123456.7890 Hyphenated: one-two abc,def ' . $lorem2;
|
||||
// Note: The search_excerpt() function adds some extra spaces -- not
|
||||
// important for HTML formatting. Remove these for comparison.
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('123456.7890', $text));
|
||||
$result = $this->doSearchExcerpt('123456.7890', $text);
|
||||
$this->assertTrue(strpos($result, 'Number: <strong>123456.7890</strong>') !== FALSE, 'Numeric keyword is highlighted with exact match');
|
||||
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('1234567890', $text));
|
||||
$result = $this->doSearchExcerpt('1234567890', $text);
|
||||
$this->assertTrue(strpos($result, 'Number: <strong>123456.7890</strong>') !== FALSE, 'Numeric keyword is highlighted with simplified match');
|
||||
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('Number 1234567890', $text));
|
||||
$result = $this->doSearchExcerpt('Number 1234567890', $text);
|
||||
$this->assertTrue(strpos($result, '<strong>Number</strong>: <strong>123456.7890</strong>') !== FALSE, 'Punctuated and numeric keyword is highlighted with simplified match');
|
||||
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('"Number 1234567890"', $text));
|
||||
$result = $this->doSearchExcerpt('"Number 1234567890"', $text);
|
||||
$this->assertTrue(strpos($result, '<strong>Number: 123456.7890</strong>') !== FALSE, 'Phrase with punctuated and numeric keyword is highlighted with simplified match');
|
||||
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('"Hyphenated onetwo"', $text));
|
||||
$result = $this->doSearchExcerpt('"Hyphenated onetwo"', $text);
|
||||
$this->assertTrue(strpos($result, '<strong>Hyphenated: one-two</strong>') !== FALSE, 'Phrase with punctuated and hyphenated keyword is highlighted with simplified match');
|
||||
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('"abc def"', $text));
|
||||
$result = $this->doSearchExcerpt('"abc def"', $text);
|
||||
$this->assertTrue(strpos($result, '<strong>abc,def</strong>') !== FALSE, 'Phrase with keyword simplified into two separate words is highlighted with simplified match');
|
||||
|
||||
// Test phrases with characters which are being truncated.
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('"ipsum _"', $text));
|
||||
$result = $this->doSearchExcerpt('"ipsum _"', $text);
|
||||
$this->assertTrue(strpos($result, '<strong>ipsum</strong>') !== FALSE, 'Only valid part of the phrase is highlighted and invalid part containing "_" is ignored.');
|
||||
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('"ipsum 0000"', $text));
|
||||
$result = $this->doSearchExcerpt('"ipsum 0000"', $text);
|
||||
$this->assertTrue(strpos($result, '<strong>ipsum</strong>') !== FALSE, 'Only valid part of the phrase is highlighted and invalid part "0000" is ignored.');
|
||||
|
||||
// Test combination of the valid keyword and keyword containing only
|
||||
// characters which are being truncated during simplification.
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('ipsum _', $text));
|
||||
$result = $this->doSearchExcerpt('ipsum _', $text);
|
||||
$this->assertTrue(strpos($result, '<strong>ipsum</strong>') !== FALSE, 'Only valid keyword is highlighted and invalid keyword "_" is ignored.');
|
||||
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('ipsum 0000', $text));
|
||||
$result = $this->doSearchExcerpt('ipsum 0000', $text);
|
||||
$this->assertTrue(strpos($result, '<strong>ipsum</strong>') !== FALSE, 'Only valid keyword is highlighted and invalid keyword "0000" is ignored.');
|
||||
|
||||
// Test using the hook_search_preprocess() from the test module.
|
||||
|
@ -127,37 +127,57 @@ class SearchExcerptTest extends KernelTestBase {
|
|||
// So, if we search for "find" or "finds" or "finding", we should
|
||||
// highlight "finding".
|
||||
$text = "this tests finding a string";
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('finds', $text, 'ex'));
|
||||
$result = $this->doSearchExcerpt('finds', $text, 'ex');
|
||||
$this->assertTrue(strpos($result, '<strong>finding</strong>') !== FALSE, 'Search excerpt works with preprocess hook, search for finds');
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('find', $text, 'ex'));
|
||||
$result = $this->doSearchExcerpt('find', $text, 'ex');
|
||||
$this->assertTrue(strpos($result, '<strong>finding</strong>') !== FALSE, 'Search excerpt works with preprocess hook, search for find');
|
||||
|
||||
// Just to be sure, test with the replacement at the beginning and end.
|
||||
$text = "finding at the beginning";
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('finds', $text, 'ex'));
|
||||
$result = $this->doSearchExcerpt('finds', $text, 'ex');
|
||||
$this->assertTrue(strpos($result, '<strong>finding</strong>') !== FALSE, 'Search excerpt works with preprocess hook, text at start');
|
||||
|
||||
$text = "at the end finding";
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('finds', $text, 'ex'));
|
||||
$result = $this->doSearchExcerpt('finds', $text, 'ex');
|
||||
$this->assertTrue(strpos($result, '<strong>finding</strong>') !== FALSE, 'Search excerpt works with preprocess hook, text at end');
|
||||
|
||||
// Testing with a one-to-many replacement: the test module replaces DIC
|
||||
// with Dependency Injection Container.
|
||||
$text = "something about the DIC is happening";
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('Dependency', $text, 'ex'));
|
||||
$result = $this->doSearchExcerpt('Dependency', $text, 'ex');
|
||||
$this->assertTrue(strpos($result, '<strong>DIC</strong>') !== FALSE, 'Search excerpt works with preprocess hook, acronym first word');
|
||||
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('Injection', $text, 'ex'));
|
||||
$result = $this->doSearchExcerpt('Injection', $text, 'ex');
|
||||
$this->assertTrue(strpos($result, '<strong>DIC</strong>') !== FALSE, 'Search excerpt works with preprocess hook, acronym second word');
|
||||
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('Container', $text, 'ex'));
|
||||
$result = $this->doSearchExcerpt('Container', $text, 'ex');
|
||||
$this->assertTrue(strpos($result, '<strong>DIC</strong>') !== FALSE, 'Search excerpt works with preprocess hook, acronym third word');
|
||||
|
||||
// Testing with a many-to-one replacement: the test module replaces
|
||||
// hypertext markup language with HTML.
|
||||
$text = "we always use hypertext markup language to describe things";
|
||||
$result = preg_replace('| +|', ' ', $this->doSearchExcerpt('html', $text, 'ex'));
|
||||
$result = $this->doSearchExcerpt('html', $text, 'ex');
|
||||
$this->assertTrue(strpos($result, '<strong>hypertext markup language</strong>') !== FALSE, 'Search excerpt works with preprocess hook, acronym many to one');
|
||||
|
||||
// Test with accents and caps in a longer piece of text with the target
|
||||
// near the end.
|
||||
$text = str_repeat($lorem2, 20) . ' ' . $lorem1;
|
||||
$result = $this->doSearchExcerpt('Lìbêró', $text);
|
||||
$this->assertTrue(strpos($result, '<strong>libero</strong>') !== FALSE, 'Search excerpt works with caps and accents in longer text');
|
||||
|
||||
// Test with an acronym provided by the hook, with the target text in the
|
||||
// middle of a long string.
|
||||
$text = str_repeat($lorem2, 10) . ' DIC ' . str_repeat($lorem2, 10);
|
||||
$result = $this->doSearchExcerpt('Dependency', $text, 'ex');
|
||||
$this->assertTrue(strpos($result, '<strong>DIC</strong>') !== FALSE, 'Search excerpt works with acronym in longer text');
|
||||
|
||||
// Test a long string with a lot of whitespace in it.
|
||||
$lorem3 = str_replace(' ', str_repeat(" \n", 20), $lorem2);
|
||||
$text = str_repeat($lorem3, 20) . ' ' . $lorem1;
|
||||
$result = $this->doSearchExcerpt('Lìbêró', $text);
|
||||
$this->assertTrue(strpos($result, '<strong>libero</strong>') !== FALSE, 'Search excerpt works with caps and accents in longer text with whitespace');
|
||||
|
||||
$this->verbose('Elapsed time: ' . (microtime(TRUE) - $start_time));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,7 +195,10 @@ class SearchExcerptTest extends KernelTestBase {
|
|||
*/
|
||||
protected function doSearchExcerpt($keys, $render_array, $langcode = NULL) {
|
||||
$render_array = search_excerpt($keys, $render_array, $langcode);
|
||||
return \Drupal::service('renderer')->renderPlain($render_array);
|
||||
$text = \Drupal::service('renderer')->renderPlain($render_array);
|
||||
// The search_excerpt() function adds some extra spaces -- not
|
||||
// important for HTML formatting or this test. Remove these for comparison.
|
||||
return preg_replace('| +|', ' ', $text);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,9 +41,6 @@ class SearchMultilingualEntityTest extends SearchTestBase {
|
|||
$user = $this->drupalCreateUser(array('administer search', 'search content', 'use advanced search', 'access content', 'access site reports', 'administer site configuration'));
|
||||
$this->drupalLogin($user);
|
||||
|
||||
// Make sure that auto-cron is disabled.
|
||||
$this->config('system.cron')->set('threshold.autorun', 0)->save();
|
||||
|
||||
// Set up the search plugin.
|
||||
$this->plugin = $this->container->get('plugin.manager.search')->createInstance('node_search');
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
namespace Drupal\search\Tests;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
|
||||
/**
|
||||
* Tests the search_page entity cache tags on the search results pages.
|
||||
*
|
||||
|
@ -44,6 +46,8 @@ class SearchPageCacheTagsTest extends SearchTestBase {
|
|||
|
||||
// Create a node and update the search index.
|
||||
$this->node = $this->drupalCreateNode(['title' => 'bike shed shop']);
|
||||
$this->node->setOwner($this->searchingUser);
|
||||
$this->node->save();
|
||||
$this->container->get('plugin.manager.search')->createInstance('node_search')->updateIndex();
|
||||
search_update_totals();
|
||||
}
|
||||
|
@ -58,6 +62,7 @@ class SearchPageCacheTagsTest extends SearchTestBase {
|
|||
$this->drupalGet('search/node');
|
||||
$this->assertCacheTag('config:search.page.node_search');
|
||||
$this->assertCacheTag('search_index:node_search');
|
||||
$this->assertCacheTag('node_list');
|
||||
|
||||
// Node search results.
|
||||
$edit = array();
|
||||
|
@ -67,6 +72,10 @@ class SearchPageCacheTagsTest extends SearchTestBase {
|
|||
$this->assertCacheTag('config:search.page.node_search');
|
||||
$this->assertCacheTag('search_index');
|
||||
$this->assertCacheTag('search_index:node_search');
|
||||
$this->assertCacheTag('node:1');
|
||||
$this->assertCacheTag('user:2');
|
||||
$this->assertCacheTag('rendered');
|
||||
$this->assertCacheTag('node_list');
|
||||
|
||||
// Updating a node should invalidate the search plugin's index cache tag.
|
||||
$this->node->title = 'bike shop';
|
||||
|
@ -76,6 +85,10 @@ class SearchPageCacheTagsTest extends SearchTestBase {
|
|||
$this->assertCacheTag('config:search.page.node_search');
|
||||
$this->assertCacheTag('search_index');
|
||||
$this->assertCacheTag('search_index:node_search');
|
||||
$this->assertCacheTag('node:1');
|
||||
$this->assertCacheTag('user:2');
|
||||
$this->assertCacheTag('rendered');
|
||||
$this->assertCacheTag('node_list');
|
||||
|
||||
// Deleting a node should invalidate the search plugin's index cache tag.
|
||||
$this->node->delete();
|
||||
|
@ -84,10 +97,12 @@ class SearchPageCacheTagsTest extends SearchTestBase {
|
|||
$this->assertCacheTag('config:search.page.node_search');
|
||||
$this->assertCacheTag('search_index');
|
||||
$this->assertCacheTag('search_index:node_search');
|
||||
$this->assertCacheTag('node_list');
|
||||
|
||||
// Initial page for searching users.
|
||||
$this->drupalGet('search/user');
|
||||
$this->assertCacheTag('config:search.page.user_search');
|
||||
$this->assertCacheTag('user_list');
|
||||
$this->assertNoCacheTag('search_index');
|
||||
$this->assertNoCacheTag('search_index:user_search');
|
||||
|
||||
|
@ -95,8 +110,111 @@ class SearchPageCacheTagsTest extends SearchTestBase {
|
|||
$edit['keys'] = $this->searchingUser->getUsername();
|
||||
$this->drupalPostForm('search/user', $edit, t('Search'));
|
||||
$this->assertCacheTag('config:search.page.user_search');
|
||||
$this->assertCacheTag('user_list');
|
||||
$this->assertCacheTag('user:2');
|
||||
$this->assertNoCacheTag('search_index');
|
||||
$this->assertNoCacheTag('search_index:user_search');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the presence of expected cache tags with referenced entities.
|
||||
*/
|
||||
public function testSearchTagsBubbling() {
|
||||
|
||||
// Install field UI and entity reference modules.
|
||||
$this->container->get('module_installer')->install(['field_ui', 'entity_reference']);
|
||||
$this->resetAll();
|
||||
|
||||
|
||||
// Creates a new content type that will have an entity reference.
|
||||
$type_name = 'entity_reference_test';
|
||||
$type = $this->drupalCreateContentType(['name' => $type_name, 'type' => $type_name]);
|
||||
|
||||
$bundle_path = 'admin/structure/types/manage/' . $type->id();
|
||||
|
||||
// Create test user.
|
||||
$admin_user = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'create ' . $type_name . ' content',
|
||||
'administer node fields',
|
||||
'administer node display',
|
||||
|
||||
]);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// First step: 'Add new field' on the 'Manage fields' page.
|
||||
$this->drupalGet($bundle_path . '/fields/add-field');
|
||||
$this->drupalPostForm(NULL, [
|
||||
'label' => 'Test label',
|
||||
'field_name' => 'test__ref',
|
||||
'new_storage_type' => 'entity_reference',
|
||||
], t('Save and continue'));
|
||||
|
||||
// Second step: 'Field settings' form.
|
||||
$this->drupalPostForm(NULL, [], t('Save field settings'));
|
||||
|
||||
// Create a new node of our newly created node type and fill in the entity
|
||||
// reference field.
|
||||
$edit = [
|
||||
'title[0][value]' => 'Llama shop',
|
||||
'field_test__ref[0][target_id]' => $this->node->getTitle()
|
||||
];
|
||||
$this->drupalPostForm('node/add/' . $type->id(), $edit, t('Save'));
|
||||
|
||||
// Test that the value of the entity reference field is shown.
|
||||
$this->drupalGet('node/2');
|
||||
$this->assertText('bike shed shop');
|
||||
|
||||
// Refresh the search index.
|
||||
$this->container->get('plugin.manager.search')->createInstance('node_search')->updateIndex();
|
||||
search_update_totals();
|
||||
|
||||
// Login with searching user again.
|
||||
$this->drupalLogin($this->searchingUser);
|
||||
|
||||
// Default search cache tags.
|
||||
$default_search_tags = [
|
||||
'config:search.page.node_search',
|
||||
'search_index',
|
||||
'search_index:node_search',
|
||||
'rendered',
|
||||
'node_list',
|
||||
];
|
||||
|
||||
// Node search results for shop, should return node:1 (bike shed shop) and
|
||||
// node:2 (Llama shop). The related authors cache tags should be visible as
|
||||
// well.
|
||||
$edit = array();
|
||||
$edit['keys'] = 'shop';
|
||||
$this->drupalPostForm('search/node', $edit, t('Search'));
|
||||
$this->assertText('bike shed shop');
|
||||
$this->assertText('Llama shop');
|
||||
$expected_cache_tags = Cache::mergeTags($default_search_tags, [
|
||||
'node:1',
|
||||
'user:2',
|
||||
'node:2',
|
||||
'user:3',
|
||||
'node_view',
|
||||
'config:filter.format.plain_text',
|
||||
]);
|
||||
$cache_tags = $this->drupalGetHeader('X-Drupal-Cache-Tags');
|
||||
$this->assertEqual(explode(' ', $cache_tags), $expected_cache_tags);
|
||||
|
||||
// Only get the new node in the search results, should result in node:1,
|
||||
// node:2 and user:3 as cache tags even though only node:1 is shown. This is
|
||||
// because node:2 is reference in node:1 as an entity reference.
|
||||
$edit = array();
|
||||
$edit['keys'] = 'Llama';
|
||||
$this->drupalPostForm('search/node', $edit, t('Search'));
|
||||
$this->assertText('Llama shop');
|
||||
$expected_cache_tags = Cache::mergeTags($default_search_tags, [
|
||||
'node:1',
|
||||
'node:2',
|
||||
'user:3',
|
||||
'node_view',
|
||||
]);
|
||||
$cache_tags = $this->drupalGetHeader('X-Drupal-Cache-Tags');
|
||||
$this->assertEqual(explode(' ', $cache_tags), $expected_cache_tags);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ class SearchPageTextTest extends SearchTestBase {
|
|||
// Create user.
|
||||
$this->searchingUser = $this->drupalCreateUser(array('search content', 'access user profiles', 'use advanced search'));
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Reference in a new issue