Issue #278958 by jhodgdon, pwolanin, mgifford: Advanced search form has usability issues
parent
fb35534db5
commit
0623c09519
|
@ -114,6 +114,11 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
||||||
'term' => array('column' => 'ti.tid', 'join' => array('table' => 'taxonomy_index', 'alias' => 'ti', 'condition' => 'n.nid = ti.nid')),
|
'term' => array('column' => 'ti.tid', 'join' => array('table' => 'taxonomy_index', 'alias' => 'ti', 'condition' => 'n.nid = ti.nid')),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A constant for setting and checking the query string.
|
||||||
|
*/
|
||||||
|
const ADVANCED_FORM = 'advanced-form';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -490,38 +495,59 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function searchFormAlter(array &$form, FormStateInterface $form_state) {
|
public function searchFormAlter(array &$form, FormStateInterface $form_state) {
|
||||||
|
$parameters = $this->getParameters();
|
||||||
|
$keys = $this->getKeywords();
|
||||||
|
$used_advanced = !empty($parameters[self::ADVANCED_FORM]);
|
||||||
|
if ($used_advanced) {
|
||||||
|
$f = isset($parameters['f']) ? (array) $parameters['f'] : array();
|
||||||
|
$defaults = $this->parseAdvancedDefaults($f, $keys);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$defaults = array('keys' => $keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
$form['basic']['keys']['#default_value'] = $defaults['keys'];
|
||||||
|
|
||||||
// Add advanced search keyword-related boxes.
|
// Add advanced search keyword-related boxes.
|
||||||
$form['advanced'] = array(
|
$form['advanced'] = array(
|
||||||
'#type' => 'details',
|
'#type' => 'details',
|
||||||
'#title' => t('Advanced search'),
|
'#title' => t('Advanced search'),
|
||||||
'#attributes' => array('class' => array('search-advanced')),
|
'#attributes' => array('class' => array('search-advanced')),
|
||||||
'#access' => $this->account && $this->account->hasPermission('use advanced search'),
|
'#access' => $this->account && $this->account->hasPermission('use advanced search'),
|
||||||
|
'#open' => $used_advanced,
|
||||||
);
|
);
|
||||||
$form['advanced']['keywords-fieldset'] = array(
|
$form['advanced']['keywords-fieldset'] = array(
|
||||||
'#type' => 'fieldset',
|
'#type' => 'fieldset',
|
||||||
'#title' => t('Keywords'),
|
'#title' => t('Keywords'),
|
||||||
);
|
);
|
||||||
|
|
||||||
$form['advanced']['keywords'] = array(
|
$form['advanced']['keywords'] = array(
|
||||||
'#prefix' => '<div class="criterion">',
|
'#prefix' => '<div class="criterion">',
|
||||||
'#suffix' => '</div>',
|
'#suffix' => '</div>',
|
||||||
);
|
);
|
||||||
|
|
||||||
$form['advanced']['keywords-fieldset']['keywords']['or'] = array(
|
$form['advanced']['keywords-fieldset']['keywords']['or'] = array(
|
||||||
'#type' => 'textfield',
|
'#type' => 'textfield',
|
||||||
'#title' => t('Containing any of the words'),
|
'#title' => t('Containing any of the words'),
|
||||||
'#size' => 30,
|
'#size' => 30,
|
||||||
'#maxlength' => 255,
|
'#maxlength' => 255,
|
||||||
|
'#default_value' => isset($defaults['or']) ? $defaults['or'] : '',
|
||||||
);
|
);
|
||||||
|
|
||||||
$form['advanced']['keywords-fieldset']['keywords']['phrase'] = array(
|
$form['advanced']['keywords-fieldset']['keywords']['phrase'] = array(
|
||||||
'#type' => 'textfield',
|
'#type' => 'textfield',
|
||||||
'#title' => t('Containing the phrase'),
|
'#title' => t('Containing the phrase'),
|
||||||
'#size' => 30,
|
'#size' => 30,
|
||||||
'#maxlength' => 255,
|
'#maxlength' => 255,
|
||||||
|
'#default_value' => isset($defaults['phrase']) ? $defaults['phrase'] : '',
|
||||||
);
|
);
|
||||||
|
|
||||||
$form['advanced']['keywords-fieldset']['keywords']['negative'] = array(
|
$form['advanced']['keywords-fieldset']['keywords']['negative'] = array(
|
||||||
'#type' => 'textfield',
|
'#type' => 'textfield',
|
||||||
'#title' => t('Containing none of the words'),
|
'#title' => t('Containing none of the words'),
|
||||||
'#size' => 30,
|
'#size' => 30,
|
||||||
'#maxlength' => 255,
|
'#maxlength' => 255,
|
||||||
|
'#default_value' => isset($defaults['negative']) ? $defaults['negative'] : '',
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add node types.
|
// Add node types.
|
||||||
|
@ -536,7 +562,9 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
||||||
'#prefix' => '<div class="criterion">',
|
'#prefix' => '<div class="criterion">',
|
||||||
'#suffix' => '</div>',
|
'#suffix' => '</div>',
|
||||||
'#options' => $types,
|
'#options' => $types,
|
||||||
|
'#default_value' => isset($defaults['type']) ? $defaults['type'] : array(),
|
||||||
);
|
);
|
||||||
|
|
||||||
$form['advanced']['submit'] = array(
|
$form['advanced']['submit'] = array(
|
||||||
'#type' => 'submit',
|
'#type' => 'submit',
|
||||||
'#value' => t('Advanced search'),
|
'#value' => t('Advanced search'),
|
||||||
|
@ -563,6 +591,7 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
||||||
'#prefix' => '<div class="criterion">',
|
'#prefix' => '<div class="criterion">',
|
||||||
'#suffix' => '</div>',
|
'#suffix' => '</div>',
|
||||||
'#options' => $language_options,
|
'#options' => $language_options,
|
||||||
|
'#default_value' => isset($defaults['language']) ? $defaults['language'] : array(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -574,6 +603,7 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
||||||
// Read keyword and advanced search information from the form values,
|
// Read keyword and advanced search information from the form values,
|
||||||
// and put these into the GET parameters.
|
// and put these into the GET parameters.
|
||||||
$keys = trim($form_state->getValue('keys'));
|
$keys = trim($form_state->getValue('keys'));
|
||||||
|
$advanced = FALSE;
|
||||||
|
|
||||||
// Collect extra filters.
|
// Collect extra filters.
|
||||||
$filters = array();
|
$filters = array();
|
||||||
|
@ -582,6 +612,7 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
||||||
// checkboxes to 0.
|
// checkboxes to 0.
|
||||||
foreach ($form_state->getValue('type') as $type) {
|
foreach ($form_state->getValue('type') as $type) {
|
||||||
if ($type) {
|
if ($type) {
|
||||||
|
$advanced = TRUE;
|
||||||
$filters[] = 'type:' . $type;
|
$filters[] = 'type:' . $type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -590,11 +621,13 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
||||||
if ($form_state->hasValue('term') && is_array($form_state->getValue('term'))) {
|
if ($form_state->hasValue('term') && is_array($form_state->getValue('term'))) {
|
||||||
foreach ($form_state->getValue('term') as $term) {
|
foreach ($form_state->getValue('term') as $term) {
|
||||||
$filters[] = 'term:' . $term;
|
$filters[] = 'term:' . $term;
|
||||||
|
$advanced = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($form_state->hasValue('language') && is_array($form_state->getValue('language'))) {
|
if ($form_state->hasValue('language') && is_array($form_state->getValue('language'))) {
|
||||||
foreach ($form_state->getValue('language') as $language) {
|
foreach ($form_state->getValue('language') as $language) {
|
||||||
if ($language) {
|
if ($language) {
|
||||||
|
$advanced = TRUE;
|
||||||
$filters[] = 'language:' . $language;
|
$filters[] = 'language:' . $language;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -602,15 +635,18 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
||||||
if ($form_state->getValue('or') != '') {
|
if ($form_state->getValue('or') != '') {
|
||||||
if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' ' . $form_state->getValue('or'), $matches)) {
|
if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' ' . $form_state->getValue('or'), $matches)) {
|
||||||
$keys .= ' ' . implode(' OR ', $matches[1]);
|
$keys .= ' ' . implode(' OR ', $matches[1]);
|
||||||
|
$advanced = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($form_state->getValue('negative') != '') {
|
if ($form_state->getValue('negative') != '') {
|
||||||
if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' ' . $form_state->getValue('negative'), $matches)) {
|
if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' ' . $form_state->getValue('negative'), $matches)) {
|
||||||
$keys .= ' -' . implode(' -', $matches[1]);
|
$keys .= ' -' . implode(' -', $matches[1]);
|
||||||
|
$advanced = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($form_state->getValue('phrase') != '') {
|
if ($form_state->getValue('phrase') != '') {
|
||||||
$keys .= ' "' . str_replace('"', ' ', $form_state->getValue('phrase')) . '"';
|
$keys .= ' "' . str_replace('"', ' ', $form_state->getValue('phrase')) . '"';
|
||||||
|
$advanced = TRUE;
|
||||||
}
|
}
|
||||||
$keys = trim($keys);
|
$keys = trim($keys);
|
||||||
|
|
||||||
|
@ -621,10 +657,69 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
||||||
if ($filters) {
|
if ($filters) {
|
||||||
$query['f'] = $filters;
|
$query['f'] = $filters;
|
||||||
}
|
}
|
||||||
|
// Record that the person used the advanced search form, if they did.
|
||||||
|
if ($advanced) {
|
||||||
|
$query[self::ADVANCED_FORM] = '1';
|
||||||
|
}
|
||||||
|
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the advanced search form default values.
|
||||||
|
*
|
||||||
|
* @param array $f
|
||||||
|
* The 'f' query parameter set up in self::buildUrlSearchQuery(), which
|
||||||
|
* contains the advanced query values.
|
||||||
|
* @param string $keys
|
||||||
|
* The search keywords string, which contains some information from the
|
||||||
|
* advanced search form.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Array of default form values for the advanced search form, including
|
||||||
|
* a modified 'keys' element for the bare search keywords.
|
||||||
|
*/
|
||||||
|
protected function parseAdvancedDefaults($f, $keys) {
|
||||||
|
$defaults = array();
|
||||||
|
|
||||||
|
// Split out the advanced search parameters.
|
||||||
|
foreach ($f as $advanced) {
|
||||||
|
list($key, $value) = explode(':', $advanced, 2);
|
||||||
|
if (!isset($defaults[$key])) {
|
||||||
|
$defaults[$key] = array();
|
||||||
|
}
|
||||||
|
$defaults[$key][] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split out the negative, phrase, and OR parts of keywords.
|
||||||
|
|
||||||
|
// For phrases, the form only supports one phrase.
|
||||||
|
$matches = array();
|
||||||
|
$keys = ' ' . $keys . ' ';
|
||||||
|
if (preg_match('/ "([^"]+)" /', $keys, $matches)) {
|
||||||
|
$keys = str_replace($matches[0], ' ', $keys);
|
||||||
|
$defaults['phrase'] = $matches[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negative keywords: pull all of them out.
|
||||||
|
if (preg_match_all('/ -([^ ]+)/', $keys, $matches)) {
|
||||||
|
$keys = str_replace($matches[0], ' ', $keys);
|
||||||
|
$defaults['negative'] = implode(' ', $matches[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// OR keywords: pull up to one set of them out of the query.
|
||||||
|
if (preg_match('/ [^ ]+( OR [^ ]+)+ /', $keys, $matches)) {
|
||||||
|
$keys = str_replace($matches[0], ' ', $keys);
|
||||||
|
$words = explode(' OR ', trim($matches[0]));
|
||||||
|
$defaults['or'] = implode(' ', $words);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put remaining keywords string back into keywords.
|
||||||
|
$defaults['keys'] = trim($keys);
|
||||||
|
|
||||||
|
return $defaults;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gathers ranking definitions from hook_ranking().
|
* Gathers ranking definitions from hook_ranking().
|
||||||
*
|
*
|
||||||
|
|
|
@ -40,13 +40,11 @@ class SearchAdvancedSearchFormTest extends SearchTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test using the search form with GET and POST queries.
|
* Tests advanced search by node type.
|
||||||
* Test using the advanced search form to limit search to nodes of type "Basic page".
|
|
||||||
*/
|
*/
|
||||||
function testNodeType() {
|
function testNodeType() {
|
||||||
|
// Verify some properties of the node that was created.
|
||||||
$this->assertTrue($this->node->getType() == 'page', 'Node type is Basic page.');
|
$this->assertTrue($this->node->getType() == 'page', 'Node type is Basic page.');
|
||||||
|
|
||||||
// Assert that the dummy title doesn't equal the real title.
|
|
||||||
$dummy_title = 'Lorem ipsum';
|
$dummy_title = 'Lorem ipsum';
|
||||||
$this->assertNotEqual($dummy_title, $this->node->label(), "Dummy title doesn't equal node title.");
|
$this->assertNotEqual($dummy_title, $this->node->label(), "Dummy title doesn't equal node title.");
|
||||||
|
|
||||||
|
@ -63,11 +61,56 @@ class SearchAdvancedSearchFormTest extends SearchTestBase {
|
||||||
$this->drupalPostForm('search/node', $edit, t('Advanced search'));
|
$this->drupalPostForm('search/node', $edit, t('Advanced search'));
|
||||||
$this->assertText($this->node->label(), 'Basic page node is found with POST query.');
|
$this->assertText($this->node->label(), 'Basic page node is found with POST query.');
|
||||||
|
|
||||||
// Advanced search type option.
|
// Search by node type.
|
||||||
$this->drupalPostForm('search/node', array_merge($edit, array('type[page]' => 'page')), t('Advanced search'));
|
$this->drupalPostForm('search/node', array_merge($edit, array('type[page]' => 'page')), t('Advanced search'));
|
||||||
$this->assertText($this->node->label(), 'Basic page node is found with POST query and type:page.');
|
$this->assertText($this->node->label(), 'Basic page node is found with POST query and type:page.');
|
||||||
|
|
||||||
$this->drupalPostForm('search/node', array_merge($edit, array('type[article]' => 'article')), t('Advanced search'));
|
$this->drupalPostForm('search/node', array_merge($edit, array('type[article]' => 'article')), t('Advanced search'));
|
||||||
$this->assertText('search yielded no results', 'Article node is not found with POST query and type:article.');
|
$this->assertText('search yielded no results', 'Article node is not found with POST query and type:article.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that after submitting the advanced search form, the form is refilled.
|
||||||
|
*/
|
||||||
|
function testFormRefill() {
|
||||||
|
$edit = array(
|
||||||
|
'keys' => 'cat',
|
||||||
|
'or' => 'dog gerbil',
|
||||||
|
'phrase' => 'pets are nice',
|
||||||
|
'negative' => 'fish snake',
|
||||||
|
'type[page]' => 'page',
|
||||||
|
);
|
||||||
|
$this->drupalPostForm('search/node', $edit, t('Advanced search'));
|
||||||
|
|
||||||
|
// Test that the encoded query appears in the page title. Only test the
|
||||||
|
// part not including the quote, because assertText() cannot seem to find
|
||||||
|
// the quote marks successfully.
|
||||||
|
$this->assertText('Search for cat dog OR gerbil -fish -snake');
|
||||||
|
|
||||||
|
// Verify that all of the form fields are filled out.
|
||||||
|
foreach ($edit as $key => $value) {
|
||||||
|
if ($key != 'type[page]') {
|
||||||
|
$elements = $this->xpath('//input[@name=:name]', array(':name' => $key));
|
||||||
|
$this->assertTrue(isset($elements[0]) && $elements[0]['value'] == $value, "Field $key is set to $value");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$elements = $this->xpath('//input[@name=:name]', array(':name' => $key));
|
||||||
|
$this->assertTrue(isset($elements[0]) && !empty($elements[0]['checked']), "Field $key is checked");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now test by submitting the or/not part of the query in the main
|
||||||
|
// search box, and verify that the advanced form is not filled out.
|
||||||
|
// (It shouldn't be filled out unless you submit values in those fields.)
|
||||||
|
$edit2 = array('keys' => 'cat dog OR gerbil -fish -snake');
|
||||||
|
$this->drupalPostForm('search/node', $edit2, t('Advanced search'));
|
||||||
|
$this->assertText('Search for cat dog OR gerbil -fish -snake');
|
||||||
|
foreach ($edit as $key => $value) {
|
||||||
|
if ($key != 'type[page]') {
|
||||||
|
$elements = $this->xpath('//input[@name=:name]', array(':name' => $key));
|
||||||
|
$this->assertFalse(isset($elements[0]) && $elements[0]['value'] == $value, "Field $key is not set to $value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue