Index: modules/node/node.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.admin.inc,v
retrieving revision 1.74
diff -u -r1.74 node.admin.inc
--- modules/node/node.admin.inc 27 Oct 2009 04:06:44 -0000 1.74
+++ modules/node/node.admin.inc 2 Nov 2009 14:39:42 -0000
@@ -70,8 +70,22 @@
*/
function node_filters() {
// Regular filters
+ $filters['titlesearch'] = array(
+ 'title' => t('title'),
+ 'type' => 'textfield',
+ );
+
+ // If the search module is enabled, use it to search the contents of nodes.
+ if (module_exists('search')) {
+ $filters['textsearch'] = array(
+ 'title' => t('data'),
+ 'type' => 'textfield',
+ );
+ }
+
$filters['status'] = array(
'title' => t('status'),
+ 'type' => 'select',
'options' => array(
'status-1' => t('published'),
'status-0' => t('not published'),
@@ -89,16 +103,16 @@
);
}
- $filters['type'] = array('title' => t('type'), 'options' => node_type_get_names());
+ $filters['type'] = array('title' => t('type'), 'type' => 'select', 'options' => node_type_get_names());
// The taxonomy filter
if ($taxonomy = module_invoke('taxonomy', 'form_all', 1)) {
- $filters['term'] = array('title' => t('term'), 'options' => $taxonomy);
+ $filters['term'] = array('title' => t('term'), 'type' => 'select', 'options' => $taxonomy);
}
// Language filter if there is a list of languages
if ($languages = module_invoke('locale', 'language_list')) {
$languages = array('' => t('Language neutral')) + $languages;
- $filters['language'] = array('title' => t('language'), 'options' => $languages);
+ $filters['language'] = array('title' => t('language'), 'type' => 'select', 'options' => $languages);
}
return $filters;
}
@@ -116,6 +130,33 @@
foreach ($filter_data as $index => $filter) {
list($key, $value) = $filter;
switch ($key) {
+ case 'titlesearch':
+ $query->condition('n.title', '%' . $value .'%', 'LIKE');
+ break;
+ case 'textsearch':
+ // Avoid errors when there is session data but the module is disabled.
+ if (!module_exists('search')) {
+ break;
+ }
+
+ // First get a list of nids that match the description.
+ $result = node_search_execute($value);
+ $nids = array();
+ if (is_array($result)) {
+ foreach ($result as $node_result) {
+ $nids[] = $node_result['node']->nid;
+ }
+ }
+
+ if (!empty($nids)) {
+ // Now restrict to just that list of nids.
+ $query->condition('nid', $nids);
+ }
+ else {
+ // No nodes match, force a return of nothing without an error.
+ $query->condition('1', '0');
+ }
+ break;
case 'term':
$index = 'tn' . $counter++;
$query->join('taxonomy_term_node', $index, "n.nid = $index.nid");
@@ -155,7 +196,7 @@
elseif ($type == 'language') {
$value = empty($value) ? t('Language neutral') : module_invoke('locale', 'language_name', $value);
}
- else {
+ elseif ($filters[$type]['type'] == 'select') {
$value = $filters[$type]['options'][$value];
}
if ($i++) {
@@ -164,18 +205,21 @@
else {
$form['filters']['current'][] = array('#markup' => t('%type is %value', array('%type' => $filters[$type]['title'], '%value' => $value)));
}
- if (in_array($type, array('type', 'language'))) {
- // Remove the option if it is already being filtered on.
+ if (in_array($type, array('type', 'language')) || $filters[$type]['type'] == 'textfield') {
+ // Remove the option if it is already being filtered on and can't have multiple options.
unset($filters[$type]);
}
}
foreach ($filters as $key => $filter) {
$names[$key] = $filter['title'];
- $form['filters']['status'][$key] = array('#type' => 'select', '#options' => $filter['options']);
+ $form['filters']['status'][$key] = array('#type' => $filter['type']);
+ if (isset($filter['options'])) {
+ $form['filters']['status'][$key]['#options'] = $filter['options'];
+ }
}
- $form['filters']['filter'] = array('#type' => 'radios', '#options' => $names, '#default_value' => 'status');
+ $form['filters']['filter'] = array('#type' => 'checkboxes', '#options' => $names);
$form['filters']['buttons']['submit'] = array('#type' => 'submit', '#value' => (count($session) ? t('Refine') : t('Filter')));
if (count($session)) {
$form['filters']['buttons']['undo'] = array('#type' => 'submit', '#value' => t('Undo'));
@@ -247,14 +291,26 @@
switch ($form_state['values']['op']) {
case t('Filter'):
case t('Refine'):
- if (isset($form_state['values']['filter'])) {
- $filter = $form_state['values']['filter'];
-
- // Flatten the options array to accommodate hierarchical/nested options.
- $flat_options = form_options_flatten($filters[$filter]['options']);
+ foreach ($form_state['values']['filter'] as $filter) {
+ // Filter may not have been checked, but will still be in the array.
+ if (empty($filter)) {
+ continue;
+ }
- if (isset($flat_options[$form_state['values'][$filter]])) {
- $_SESSION['node_overview_filter'][] = array($filter, $form_state['values'][$filter]);
+ if ($filters[$filter]['type'] == 'select' && is_array($filters[$filter]['options'])) {
+ // Flatten the options array to accommodate hierarchical/nested options.
+ $flat_options = form_options_flatten($filters[$filter]['options']);
+
+ if (isset($flat_options[$form_state['values'][$filter]])) {
+ $_SESSION['node_overview_filter'][] = array($filter, $form_state['values'][$filter]);
+ }
+ }
+ else {
+ // Text fields.
+ $value = $form_state['values'][$filter];
+ if (!empty($value)) {
+ $_SESSION['node_overview_filter'][] = array($filter, $value);
+ }
}
}
break;
Index: modules/node/node.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.test,v
retrieving revision 1.51
diff -u -r1.51 node.test
--- modules/node/node.test 30 Oct 2009 22:33:35 -0000 1.51
+++ modules/node/node.test 2 Nov 2009 14:39:42 -0000
@@ -704,7 +704,8 @@
// Enable dummy module that implements hook_node_grants(),
// hook_node_access_records(), hook_node_grants_alter() and
// hook_node_access_records_alter().
- parent::setUp('node_test');
+ // Search module is an optional dependency, used in the node search form.
+ parent::setUp('node_test', 'search');
}
/**
@@ -973,6 +974,8 @@
$node1 = $this->drupalCreateNode(array('type' => 'article', 'status' => 1));
$node2 = $this->drupalCreateNode(array('type' => 'article', 'status' => 0));
$node3 = $this->drupalCreateNode(array('type' => 'page'));
+ // Additional node for title search tests.
+ $node4 = $this->drupalCreateNode(array('type' => 'article', 'status' => 1));
$this->drupalGet('admin/content');
$this->assertText($node1->title[FIELD_LANGUAGE_NONE][0]['value'], t('Node appears on the node administration listing.'));
@@ -982,24 +985,78 @@
// Filter the node listing by status.
$edit = array(
- 'filter' => 'status',
+ 'filter[status]' => 'status',
'status' => 'status-1',
);
$this->drupalPost('admin/content', $edit, t('Filter'));
$this->assertRaw(t('%type is %value', array('%type' => t('status'), '%value' => t('published'))), t('The node administration listing is filtered by status.'));
- $this->assertText($node1->title[FIELD_LANGUAGE_NONE][0]['value'], t('Published node appears on the node administration listing.'));
+ // For result assertRaw, must appear in the node list as a link rather than in the filter.
+ $this->assertRaw($node1->title[FIELD_LANGUAGE_NONE][0]['value'] . '', t('Published node appears on the node administration listing.'));
+ // NoText = shouldn't appear at all.
$this->assertNoText($node2->title[FIELD_LANGUAGE_NONE][0]['value'], t('Unpublished node does not appear on the node administration listing.'));
+ $this->verbose($node1->title[FIELD_LANGUAGE_NONE][0]['value'] . '');
+
// Filter the node listing by content type.
$edit = array(
- 'filter' => 'type',
+ 'filter[type]' => 'type',
'type' => 'article',
);
$this->drupalPost('admin/content', $edit, t('Refine'));
$this->assertRaw(t('%type is %value', array('%type' => t('status'), '%value' => t('published'))), t('The node administration listing is filtered by status.'));
$this->assertRaw(t('%type is %value', array('%type' => t('type'), '%value' => 'Article')), t('The node administration listing is filtered by content type.'));
- $this->assertText($node1->title[FIELD_LANGUAGE_NONE][0]['value'], t('Article node appears on the node administration listing.'));
+ $this->assertRaw($node1->title[FIELD_LANGUAGE_NONE][0]['value'] . '', t('Article node appears on the node administration listing.'));
+ $this->assertNoText($node3->title[FIELD_LANGUAGE_NONE][0]['value'], t('Page node does not appear on the node administration listing.'));
+
+ // Filter the node listing by title.
+ $edit = array(
+ 'filter[titlesearch]' => 'titlesearch',
+ 'titlesearch' => $node1->title[FIELD_LANGUAGE_NONE][0]['value'],
+ );
+ $this->drupalPost('admin/content', $edit, t('Refine'));
+ $this->assertRaw(t('%type is %value', array('%type' => t('status'), '%value' => t('published'))), t('The node administration listing is filtered by status.'));
+ $this->assertRaw(t('%type is %value', array('%type' => t('type'), '%value' => 'Article')), t('The node administration listing is filtered by content type.'));
+ $this->assertRaw(t('%type is %value', array('%type' => t('title'), '%value' => $node1->title[FIELD_LANGUAGE_NONE][0]['value'])), t('The node administration listing is filtered by node title.'));
+ $this->assertRaw($node1->title[FIELD_LANGUAGE_NONE][0]['value'] . '', t('Article node appears on the node administration listing.'));
+ $this->assertNoText($node3->title[FIELD_LANGUAGE_NONE][0]['value'], t('Page node does not appear on the node administration listing.'));
+ $this->assertNoText($node4->title[FIELD_LANGUAGE_NONE][0]['value'], t('Non-matching article title does not appear on the node administration listing.'));
+
+ // Test the Undo button, which removes the most recently added filter.
+ $edit = array();
+ $this->drupalPost('admin/content', $edit, t('Undo'));
+ $this->assertRaw(t('%type is %value', array('%type' => t('status'), '%value' => t('published'))), t('The node administration listing is filtered by status.'));
+ $this->assertRaw(t('%type is %value', array('%type' => t('type'), '%value' => 'Article')), t('The node administration listing is filtered by content type.'));
+ $this->assertNoRaw(t('%type is %value', array('%type' => t('title'), '%value' => $node1->title[FIELD_LANGUAGE_NONE][0]['value'])), t('The node administration listing is no longer filtered by title.'));
+ $this->assertRaw($node1->title[FIELD_LANGUAGE_NONE][0]['value'] . '', t('Article node appears on the node administration listing.'));
$this->assertNoText($node3->title[FIELD_LANGUAGE_NONE][0]['value'], t('Page node does not appear on the node administration listing.'));
+ $this->assertRaw($node4->title[FIELD_LANGUAGE_NONE][0]['value'] . '', t('Non-matching article title now appears again on the node administration listing.'));
+
+ // Test the Reset button, which removes all remaining filters.
+ $edit = array();
+ $this->drupalPost('admin/content', $edit, t('Reset'));
+ $this->assertNoRaw(t('%type is %value', array('%type' => t('status'), '%value' => t('published'))), t('The node administration listing is no longer filtered by status.'));
+ $this->assertNoRaw(t('%type is %value', array('%type' => t('type'), '%value' => 'Article')), t('The node administration listing is no longer filtered by content type.'));
+ $this->assertNoRaw(t('%type is %value', array('%type' => t('title'), '%value' => $node1->title[FIELD_LANGUAGE_NONE][0]['value'])), t('The node administration listing is no longer filtered by title.'));
+ $this->assertRaw($node1->title[FIELD_LANGUAGE_NONE][0]['value'] . '', t('Article node appears on the node administration listing.'));
+ $this->assertRaw($node3->title[FIELD_LANGUAGE_NONE][0]['value'] . '', t('Page node appears on the node administration listing.'));
+ $this->assertRaw($node4->title[FIELD_LANGUAGE_NONE][0]['value'] . '', t('Non-matching article title now appears again on the node administration listing.'));
+
+ // Filter the node listing by search data. Must trigger search indexing first.
+ /* TODO: The textsearch field needs search index data in place for it to work.
+ $edit = array(
+ 'filter[textsearch]' => 'textsearch',
+ 'textsearch' => $node2->title[FIELD_LANGUAGE_NONE][0]['value'],
+ );
+ $this->drupalPost('admin/content', $edit, t('Filter'));
+ $this->assertNoRaw(t('%type is %value', array('%type' => t('status'), '%value' => t('published'))), t('The node administration listing is no longer filtered by status.'));
+ $this->assertNoRaw(t('%type is %value', array('%type' => t('type'), '%value' => 'Article')), t('The node administration listing is no longer filtered by content type.'));
+ $this->assertNoRaw(t('%type is %value', array('%type' => t('title'), '%value' => $node1->title[FIELD_LANGUAGE_NONE][0]['value'])), t('The node administration listing is no longer filtered by title.'));
+ $this->assertRaw(t('%type is %value', array('%type' => t('data'), '%value' => $node2->title[FIELD_LANGUAGE_NONE][0]['value'])), t('The node administration listing filtered by search data.'));
+ $this->assertRaw($node2->title[FIELD_LANGUAGE_NONE][0]['value'] . '', t('Node matching search appears on the node administration listing.'));
+ $this->assertNoText($node1->title[FIELD_LANGUAGE_NONE][0]['value'], t('Non-matching article title does not appear on the node administration listing.'));
+ */
+
+ // TODO: Also test language and taxonomy filters.
}
}
Index: modules/system/system.css
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.css,v
retrieving revision 1.63
diff -u -r1.63 system.css
--- modules/system/system.css 21 Sep 2009 08:52:41 -0000 1.63
+++ modules/system/system.css 2 Nov 2009 14:39:42 -0000
@@ -193,7 +193,7 @@
padding-bottom: 0;
font-size: 0.9em;
}
-dl.multiselect dd.b, dl.multiselect dd.b .form-item, dl.multiselect dd.b select {
+dl.multiselect dd.b, dl.multiselect dd.b .form-item, dl.multiselect dd.b select, dl.multiselect dd.b input.form-text {
font-family: inherit;
font-size: inherit;
width: 14em;
Index: misc/form.js
===================================================================
RCS file: /cvs/drupal/drupal/misc/form.js,v
retrieving revision 1.12
diff -u -r1.12 form.js
--- misc/form.js 16 Oct 2009 16:37:00 -0000 1.12
+++ misc/form.js 2 Nov 2009 14:39:41 -0000
@@ -61,9 +61,9 @@
Drupal.behaviors.multiselectSelector = {
attach: function (context, settings) {
// Automatically selects the right radio button in a multiselect control.
- $('.multiselect select', context).once('multiselect').change(function () {
- $('.multiselect input:radio[value="' + this.id.substr(5) + '"]')
- .attr('checked', true);
+ $('.multiselect select, .multiselect input:text', context).once('multiselect').change(function () {
+ $('.multiselect input:radio[value="' + this.name + '"], .multiselect input:checkbox[value="' + this.name + '"]')
+ .attr('checked', true);
});
}
};