diff --git a/core/modules/views/config/views.view.backlinks.yml b/core/modules/search/config/views.view.backlinks.yml similarity index 100% rename from core/modules/views/config/views.view.backlinks.yml rename to core/modules/search/config/views.view.backlinks.yml diff --git a/core/modules/search/lib/Drupal/search/Plugin/views/argument/Search.php b/core/modules/search/lib/Drupal/search/Plugin/views/argument/Search.php index 756b8de..5a5248b 100644 --- a/core/modules/search/lib/Drupal/search/Plugin/views/argument/Search.php +++ b/core/modules/search/lib/Drupal/search/Plugin/views/argument/Search.php @@ -25,18 +25,20 @@ class Search extends ArgumentPluginBase { /** * Take sure that parseSearchExpression is runned and everything is set up for it. * - * @param $input + * @param string $input * The search phrase which was input by the user. */ - function query_parse_search_expression($input) { + protected function query_parse_search_expression($input) { if (!isset($this->search_query)) { $this->search_query = db_select('search_index', 'i', array('target' => 'slave'))->extend('Drupal\search\ViewsSearchQuery'); - $this->search_query->searchExpression($input, $this->view->base_table); + $this->search_query->searchExpression($input, $this->view->storage->get('base_table')); $this->search_query->publicParseSearchExpression(); } } /** + * Overrides \Drupal\views\Plugin\views\argument\ArgumentPluginBase::query(). + * * Add this argument to the query. */ public function query($group_by = FALSE) { @@ -110,6 +112,10 @@ public function query($group_by = FALSE) { $matches = $this->search_query->matches(); $placeholder = $this->placeholder(); $this->query->add_having_expression(0, "COUNT(*) >= $placeholder", array($placeholder => $matches)); + + // Set to NULL to prevent PDO exception when views object is serialized in + // form cache. + $this->search_query = NULL; } } diff --git a/core/modules/search/lib/Drupal/search/Plugin/views/filter/Search.php b/core/modules/search/lib/Drupal/search/Plugin/views/filter/Search.php index 8953c91..9d050e6 100644 --- a/core/modules/search/lib/Drupal/search/Plugin/views/filter/Search.php +++ b/core/modules/search/lib/Drupal/search/Plugin/views/filter/Search.php @@ -7,7 +7,6 @@ namespace Drupal\search\Plugin\views\filter; -use Drupal\search\SearchQuery; use Drupal\views\Plugin\views\filter\FilterPluginBase; use Drupal\Core\Annotation\Plugin; @@ -23,7 +22,12 @@ */ class Search extends FilterPluginBase { - var $always_multiple = TRUE; + /** + * Disable the possibility to force a single value. + * + * @var bool + */ + public $always_multiple = TRUE; /** * Stores an extended query extender from the search module. @@ -31,15 +35,20 @@ class Search extends FilterPluginBase { * This value extends the query extender to be able to provide methods * which returns the protected values. * - * @var Drupal\search\ViewsSearchQuery + * @var \Drupal\search\ViewsSearchQuery */ - var $search_query = NULL; + protected $search_query = NULL; /** * Checks if the search query has been parsed. + * + * @var bool */ - var $parsed = FALSE; + protected $parsed = FALSE; + /** + * Overrides \Drupal\views\Plugin\views\filter\FilterPluginBase::defineOptions(). + */ protected function defineOptions() { $options = parent::defineOptions(); @@ -49,9 +58,11 @@ protected function defineOptions() { } /** + * Overrides \Drupal\views\Plugin\views\filter\FilterPluginBase::operator_form(). + * * Provide simple equality operator */ - function operator_form(&$form, &$form_state) { + public function operator_form(&$form, &$form_state) { $form['operator'] = array( '#type' => 'radios', '#title' => t('On empty input'), @@ -64,9 +75,11 @@ function operator_form(&$form, &$form_state) { } /** + * Overrides \Drupal\views\Plugin\views\filter\FilterPluginBase::value_form(). + * * Provide a simple textfield for equality */ - function value_form(&$form, &$form_state) { + public function value_form(&$form, &$form_state) { $form['value'] = array( '#type' => 'textfield', '#size' => 15, @@ -77,6 +90,8 @@ function value_form(&$form, &$form_state) { } /** + * Overrides \Drupal\views\Plugin\views\argument\ArgumentPluginBase::value_form(). + * * Validate the options form. */ public function validateExposed(&$form, &$form_state) { @@ -99,17 +114,17 @@ public function validateExposed(&$form, &$form_state) { * @param $input * The search phrase which was input by the user. */ - function query_parse_search_expression($input) { + protected function query_parse_search_expression($input) { if (!isset($this->search_query)) { $this->parsed = TRUE; $this->search_query = db_select('search_index', 'i', array('target' => 'slave'))->extend('Drupal\search\ViewsSearchQuery'); - $this->search_query->searchExpression($input, $this->view->base_table); + $this->search_query->searchExpression($input, $this->view->storage->get('base_table')); $this->search_query->publicParseSearchExpression(); } } /** - * Add this filter to the query. + * Overrides \Drupal\views\Plugin\views\filter\FilterPluginBase::query(). * * Due to the nature of fapi, the value and the operator have an unintended * level of indirection. You will find them in $this->operator @@ -191,7 +206,8 @@ public function query() { $placeholder = $this->placeholder(); $this->query->add_having_expression($this->options['group'], "COUNT(*) >= $placeholder", array($placeholder => $matches)); } - // Set to NULL to prevent PDO exception when views object is cached. + // Set to NULL to prevent PDO exception when views object is serialized in + // form cache. $this->search_query = NULL; } diff --git a/core/modules/search/lib/Drupal/search/Tests/Views/BackLinksViewTest.php b/core/modules/search/lib/Drupal/search/Tests/Views/BackLinksViewTest.php new file mode 100644 index 0000000..28f3d2f --- /dev/null +++ b/core/modules/search/lib/Drupal/search/Tests/Views/BackLinksViewTest.php @@ -0,0 +1,44 @@ + 'Search: backlinks view', + 'description' => 'Tests the backlinks default view.', + 'group' => 'Views Modules', + ); + } + + /** + * Tests the backlinks default view. + */ + protected function testBacklinksView() { + $view = views_get_view('backlinks'); + $this->executeView($view, array(1)); + + $this->assertEqual(count($view->result), 9, 'The expected amount of results got returned.'); + $node_one_found = FALSE; + foreach ($view->result as $row) { + if ($row->nid == 1) { + break; + $node_one_found = TRUE; + } + } + $this->assertFalse($node_one_found, 'The first node does not reference itself.'); + + } + +} diff --git a/core/modules/search/lib/Drupal/search/Tests/Views/SearchTestBase.php b/core/modules/search/lib/Drupal/search/Tests/Views/SearchTestBase.php new file mode 100644 index 0000000..08914a5 --- /dev/null +++ b/core/modules/search/lib/Drupal/search/Tests/Views/SearchTestBase.php @@ -0,0 +1,52 @@ +drupalCreateNode(); + $node->body[LANGUAGE_NOT_SPECIFIED][0]['value'] = 'Word ' . ($i != 0 ? l('Node ' . 1, 'node/' . 1) : ''); + $node->save(); + + $this->nodes[$node->id()] = $node; + + search_index($node->nid, 'node', $node->body[LANGUAGE_NOT_SPECIFIED][0]['value'], LANGUAGE_NOT_SPECIFIED); + } + } + +} diff --git a/core/modules/search/lib/Drupal/search/Tests/Views/SearchViewTest.php b/core/modules/search/lib/Drupal/search/Tests/Views/SearchViewTest.php new file mode 100644 index 0000000..6c6e3e8 --- /dev/null +++ b/core/modules/search/lib/Drupal/search/Tests/Views/SearchViewTest.php @@ -0,0 +1,98 @@ + 'Search: search term view', + 'description' => 'Tests the search term filter/argument.', + 'group' => 'Views Modules', + ); + } + + protected function setUp() { + parent::setUp(); + + // Create a special node without the search word. + $node = $this->drupalCreateNode(); + $node->body[LANGUAGE_NOT_SPECIFIED][0]['value'] = 'NotFound'; + $node->save(); + + $this->nodes[$node->id()] = $node; + + search_index($node->nid, 'node', $node->body[LANGUAGE_NOT_SPECIFIED][0]['value'], LANGUAGE_NOT_SPECIFIED); + } + + /** + * Tests the search filter and argument. + */ + public function testSearchTerm() { + menu_router_rebuild(); + + // Prepare some expected values. + $expected_word = array(); + $last_node = end($this->nodes); + foreach ($this->nodes as $node) { + if ($node->id() != $last_node->id()) { + $expected_word[] = $node->label(); + } + } + $expected_not_found = array(end($this->nodes)->label()); + + // Run the view with the default filter value. + $this->drupalGet('test-search-view'); + $this->assertEqual($this->getNodeTitles(), $expected_word); + + // Run the view with a custom filter value. + $this->drupalGet('test-search-view', array('query' => array('keys' => 'NotFound'))); + $this->assertEqual($this->getNodeTitles(), $expected_not_found); + + // Run the view with an argument value. + $this->drupalGet('test-search-view-2/Word'); + $this->assertEqual($this->getNodeTitles(), $expected_word); + } + + /** + * Get the node titles found on the page. + * + * @return array + * The list of node titles. + */ + protected function getNodeTitles() { + $titles = array(); + foreach ($this->xpath('//div[contains(@class, "views-field-title")]//a') as $link) { + $titles[] = $link; + } + + return $titles; + } + + +} diff --git a/core/modules/search/lib/Drupal/search/ViewsSearchQuery.php b/core/modules/search/lib/Drupal/search/ViewsSearchQuery.php index 8f6b295..bae7e1c 100644 --- a/core/modules/search/lib/Drupal/search/ViewsSearchQuery.php +++ b/core/modules/search/lib/Drupal/search/ViewsSearchQuery.php @@ -14,6 +14,17 @@ */ class ViewsSearchQuery extends SearchQuery { + + /** + * Stores an extended query extender from the search module. + * + * This value extends the query extender to be able to provide methods + * which returns the protected values. + * + * @var \Drupal\search\ViewsSearchQuery + */ + protected $search_query = NULL; + /** * Returns the conditions property. * diff --git a/core/modules/search/search.views.inc b/core/modules/search/search.views.inc new file mode 100644 index 0000000..a0207a8 --- /dev/null +++ b/core/modules/search/search.views.inc @@ -0,0 +1,129 @@ + array( + 'left_field' => 'nid', + 'field' => 'sid', + ), + ); + + $data['search_total']['table']['join'] = array( + 'node' => array( + 'left_table' => 'search_index', + 'left_field' => 'word', + 'field' => 'word', + ), + 'users' => array( + 'left_table' => 'search_index', + 'left_field' => 'word', + 'field' => 'word', + ) + ); + + $data['search_dataset']['table']['join'] = array( + 'node' => array( + 'left_table' => 'search_index', + 'left_field' => 'sid', + 'field' => 'sid', + 'extra' => 'search_index.type = search_dataset.type', + 'type' => 'INNER', + ), + 'users' => array( + 'left_table' => 'search_index', + 'left_field' => 'sid', + 'field' => 'sid', + 'extra' => 'search_index.type = search_dataset.type', + 'type' => 'INNER', + ), + ); + + $data['search_index']['score'] = array( + 'title' => t('Score'), + 'help' => t('The score of the search item. This will not be used if the search filter is not also present.'), + 'field' => array( + 'id' => 'search_score', + 'click sortable' => TRUE, + 'float' => TRUE, + 'no group by' => TRUE, + ), + 'sort' => array( + 'id' => 'search_score', + 'no group by' => TRUE, + ), + ); + + $data['search_node_links_from']['table']['group'] = t('Search'); + $data['search_node_links_from']['table']['join'] = array( + 'node' => array( + 'arguments' => array( + 'table' => 'search_node_links', + 'left_table' => 'node', + 'field' => 'nid', + 'left_field' => 'nid', + 'type' => 'INNER' + ), + ), + ); + $data['search_node_links_from']['sid'] = array( + 'title' => t('Links from'), + 'help' => t('Other nodes that are linked from the node.'), + 'argument' => array( + 'id' => 'node_nid', + ), + 'filter' => array( + 'id' => 'equality', + ), + ); + + $data['search_node_links_to']['table']['group'] = t('Search'); + $data['search_node_links_to']['table']['join'] = array( + 'node' => array( + 'arguments' => array( + 'table' => 'search_node_links', + 'left_table' => 'node', + 'field' => 'sid', + 'left_field' => 'nid', + 'type' => 'INNER' + ), + ), + ); + $data['search_node_links_to']['nid'] = array( + 'title' => t('Links to'), + 'help' => t('Other nodes that link to the node.'), + 'argument' => array( + 'id' => 'node_nid', + ), + 'filter' => array( + 'id' => 'equality', + ), + ); + + $data['search_index']['keys'] = array( + 'title' => t('Search Terms'), + 'help' => t('The terms to search for.'), + 'filter' => array( + 'id' => 'search', + 'no group by' => TRUE, + ), + 'argument' => array( + 'id' => 'search', + 'no group by' => TRUE, + ), + ); + + return $data; +} diff --git a/core/modules/search/tests/modules/search_test_views/search_test_views.info b/core/modules/search/tests/modules/search_test_views/search_test_views.info new file mode 100644 index 0000000..da616d6 --- /dev/null +++ b/core/modules/search/tests/modules/search_test_views/search_test_views.info @@ -0,0 +1,9 @@ +name = "Test search views" +description = "Support module for search module views integration." +package = Testing +version = VERSION +core = 8.x +hidden = TRUE + +dependencies[] = views +dependencies[] = search diff --git a/core/modules/search/tests/modules/search_test_views/search_test_views.module b/core/modules/search/tests/modules/search_test_views/search_test_views.module new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/core/modules/search/tests/modules/search_test_views/search_test_views.module @@ -0,0 +1 @@ +drupalCreateNode($values); - search_index($node->nid, 'node', $node->body[LANGUAGE_NOT_SPECIFIED][0]['value'], LANGUAGE_NOT_SPECIFIED); - $comment = array( 'uid' => $user->uid, 'nid' => $node->nid,