Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.578
diff -u -r1.578 theme.inc
--- includes/theme.inc 23 Feb 2010 18:32:00 -0000 1.578
+++ includes/theme.inc 24 Feb 2010 20:18:39 -0000
@@ -1627,6 +1627,8 @@
* )
* );
* @endcode
+ * Note that when rowgroups are used rows need to grouped using an extra
+ * associative array. See colgroups variable for more info.
* - attributes: An array of HTML attributes to apply to the table tag.
* - caption: A localized string to use for the
tag.
* - colgroups: An array of column groups. Each element of the array can be
@@ -1637,9 +1639,9 @@
* include a "data" attribute. To add attributes to COL elements, set the
* "data" attribute with an array of columns, each of which is an
* associative array of HTML attributes.
- * Here's an example for $colgroup:
+ * Here's an example for $colgroups:
* @code
- * $colgroup = array(
+ * $colgroups = array(
* // COLGROUP with one COL element.
* array(
* array(
@@ -1660,6 +1662,43 @@
* These optional tags are used to group and set properties on columns
* within a table. For example, one may easily group three columns and
* apply same background style to all.
+ * - rowgroup: An array of attributes applied to the TBODY element(s).
+ * Rowgroup specific attributes set in rowgroups override these attributes.
+ * - rowgroups: An associative array of row groups. Each element represents a
+ * rowgroup identifiable by it's key. When this variable is set, rows are
+ * expected to be grouped by rowgroup using an associative array while their
+ * keys point to the keys of this variable. Elements of this variable are
+ * expected to be an array of attributes which will be applied to the TBODY
+ * element representing that rowgroup.
+ * Here's an example for $rows combined with $rowgroups:
+ * @code
+ * $rowgroups = array(
+ * 'funky' => array( // Attributes for the TBODY element.
+ * 'title' => 'Funky rows',
+ * 'class' => array('funky'),
+ * ),
+ * 'jazzy' => array( // Attributes for the TBODY element.
+ * 'title' => 'Jazzy rows',
+ * 'class' => array('jazzy'),
+ * ),
+ * );
+ * $rows = array(
+ * // Rows of the funky rowgroup.
+ * 'funky' => array(
+ * // Simple row
+ * array(
+ * 'Cell 1', 'Cell 2', 'Cell 3'
+ * ),
+ * ),
+ * // Rows of the jazzy rowgroup.
+ * 'jazzy' => array(
+ * // Row with attributes on the row and some of its cells.
+ * array(
+ * 'data' => array('Cell 1', array('data' => 'Cell 2', 'colspan' => 2)), 'class' => array('funky')
+ * ),
+ * ),
+ * );
+ * @endcode
* - sticky: Use a "sticky" table header.
* - empty: The message to display in an extra row if table does not have any
* rows.
@@ -1673,6 +1712,8 @@
$attributes = $variables['attributes'];
$caption = $variables['caption'];
$colgroups = $variables['colgroups'];
+ $rowgroup = $variables['rowgroup'];
+ $rowgroups = $variables['rowgroups'];
$sticky = $variables['sticky'];
$empty = $variables['empty'];
@@ -1747,44 +1788,56 @@
$rows[] = array(array('data' => $empty, 'colspan' => count($header), 'class' => array('empty', 'message')));
}
- // Format the table rows:
- if (count($rows)) {
- $output .= "\n";
- $flip = array('even' => 'odd', 'odd' => 'even');
- $class = 'even';
- foreach ($rows as $number => $row) {
- $attributes = array();
+ if (!count($rowgroups)) {
+ $rowgroups = array();
+ $rows_by_groups = array($rows);
+ }
+ else {
+ $rows_by_groups = $rows;
+ }
- // Check if we're dealing with a simple or complex row
- if (isset($row['data'])) {
- foreach ($row as $key => $value) {
- if ($key == 'data') {
- $cells = $value;
- }
- else {
- $attributes[$key] = $value;
+ // Format the table rows:
+ foreach ($rows_by_groups as $rowgroup_id => $rows) {
+ if (count($rows)) {
+ $attributes = isset($rowgroups[$rowgroup_id]) ? $rowgroup + $rowgroups[$rowgroup_id] : $rowgroup;
+ $output .= '\n";
+
+ $flip = array('even' => 'odd', 'odd' => 'even');
+ $class = 'even';
+ foreach ($rows as $number => $row) {
+ $attributes = array();
+
+ // Check if we're dealing with a simple or complex row
+ if (isset($row['data'])) {
+ foreach ($row as $key => $value) {
+ if ($key == 'data') {
+ $cells = $value;
+ }
+ else {
+ $attributes[$key] = $value;
+ }
}
}
- }
- else {
- $cells = $row;
- }
- if (count($cells)) {
- // Add odd/even class
- $class = $flip[$class];
- $attributes['class'][] = $class;
-
- // Build row
- $output .= ' ';
- $i = 0;
- foreach ($cells as $cell) {
- $cell = tablesort_cell($cell, $header, $ts, $i++);
- $output .= _theme_table_cell($cell);
+ else {
+ $cells = $row;
+ }
+ if (count($cells)) {
+ // Add odd/even class
+ $class = $flip[$class];
+ $attributes['class'][] = $class;
+
+ // Build row
+ $output .= '
';
+ $i = 0;
+ foreach ($cells as $cell) {
+ $cell = tablesort_cell($cell, $header, $ts, $i++);
+ $output .= _theme_table_cell($cell);
+ }
+ $output .= "
\n";
}
- $output .= " \n";
}
+ $output .= "\n";
}
- $output .= "\n";
}
$output .= "\n";
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1115
diff -u -r1.1115 common.inc
--- includes/common.inc 22 Feb 2010 19:21:34 -0000 1.1115
+++ includes/common.inc 24 Feb 2010 20:18:38 -0000
@@ -5019,7 +5019,7 @@
'variables' => array(),
),
'table' => array(
- 'variables' => array('header' => NULL, 'rows' => NULL, 'attributes' => array(), 'caption' => NULL, 'colgroups' => array(), 'sticky' => TRUE, 'empty' => ''),
+ 'variables' => array('header' => NULL, 'rows' => NULL, 'attributes' => array(), 'caption' => NULL, 'colgroups' => array(), 'rowgroup' => array(), 'rowgroups' => array(), 'sticky' => TRUE, 'empty' => ''),
),
'table_select_header_cell' => array(
'variables' => array(),
Index: modules/user/user.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.admin.inc,v
retrieving revision 1.99
diff -u -r1.99 user.admin.inc
--- modules/user/user.admin.inc 7 Feb 2010 17:29:09 -0000 1.99
+++ modules/user/user.admin.inc 24 Feb 2010 20:18:39 -0000
@@ -648,6 +648,27 @@
*/
function user_admin_permissions($form, $form_state, $rid = NULL) {
+ $form['permissionsfilter'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Permissions modules'),
+ '#description' => t('Enter keywords to filter the list of permissions.'),
+ );
+
+ $form['permissionsfilter']['#attached']['library'][] = array('system', 'instantfilter');
+ $form['permissionsfilter']['#attached']['js'][] = array('type' => 'setting', 'data' => array(
+ 'instantfilter' => array(
+ 'edit-permissionsfilter' => array(
+ 'container' => '#user-admin-permissions',
+ 'groups' => array(
+ 'table > tbody' => array('items' => 'tr:has(td.permission)', 'zebra' => TRUE)
+ ),
+ 'items' => array(
+ 'table > tbody > tr:has(td.permission)' => array()
+ )
+ )
+ ),
+ ));
+
// Retrieve role names for columns.
$role_names = user_roles();
if (is_numeric($rid)) {
@@ -675,14 +696,16 @@
ksort($modules);
foreach ($modules as $display_name => $module) {
+ $form['permission'][$module] = array(
+ '#id' => $module,
+ );
if ($permissions = module_invoke($module, 'permission')) {
- $form['permission'][] = array(
+ $form['permission'][$module][] = array(
'#markup' => $module_info[$module]['name'],
- '#id' => $module,
);
foreach ($permissions as $perm => $perm_item) {
$options[$perm] = '';
- $form['permission'][$perm] = array(
+ $form['permission'][$module][$perm] = array(
'#type' => 'item',
'#markup' => $perm_item['title'],
'#description' => $hide_descriptions && isset($perm_item['description']) ? $perm_item['description'] : NULL,
@@ -736,30 +759,35 @@
$form = $variables['form'];
$roles = user_roles();
- foreach (element_children($form['permission']) as $key) {
- $row = array();
- // Module name
- if (is_numeric($key)) {
- $row[] = array('data' => drupal_render($form['permission'][$key]), 'class' => array('module'), 'id' => 'module-' . $form['permission'][$key]['#id'], 'colspan' => count($form['role_names']['#value']) + 1);
- }
- else {
- // Permission row.
- $row[] = array(
- 'data' => drupal_render($form['permission'][$key]),
- 'class' => array('permission'),
- );
- foreach (element_children($form['checkboxes']) as $rid) {
- $row[] = array('data' => drupal_render($form['checkboxes'][$rid][$key]), 'class' => array('checkbox'), 'title' => $roles[$rid] . ' : ' . t($key));
+ foreach (element_children($form['permission']) as $module) {
+ $rowgroups[$module] = array('id' => 'module-' . $form['permission'][$module]['#id']);
+
+ foreach (element_children($form['permission'][$module]) as $key) {
+ $row = array();
+ // Module name
+ if (is_numeric($key)) {
+ $row[] = array('data' => drupal_render($form['permission'][$module][$key]), 'class' => array('module'), 'colspan' => count($form['role_names']['#value']) + 1);
+ }
+ else {
+ // Permission row.
+ $row[] = array(
+ 'data' => drupal_render($form['permission'][$module][$key]),
+ 'class' => array('permission'),
+ );
+ foreach (element_children($form['checkboxes']) as $rid) {
+ $row[] = array('data' => drupal_render($form['checkboxes'][$rid][$key]), 'class' => array('checkbox'), 'title' => $roles[$rid] . ' : ' . t($key));
+ }
}
+ $rows[$module][] = $row;
}
- $rows[] = $row;
}
$header[] = (t('Permission'));
foreach (element_children($form['role_names']) as $rid) {
$header[] = array('data' => drupal_render($form['role_names'][$rid]), 'class' => array('checkbox'));
}
$output = theme('system_compact_link');
- $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'permissions')));
+ $output .= drupal_render($form['permissionsfilter']);
+ $output .= theme('table', array('header' => $header, 'rows' => $rows, 'rowgroups' => $rowgroups, 'attributes' => array('id' => 'permissions')));
$output .= drupal_render_children($form);
return $output;
}
Index: modules/system/system.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v
retrieving revision 1.259
diff -u -r1.259 system.admin.inc
--- modules/system/system.admin.inc 15 Feb 2010 15:07:57 -0000 1.259
+++ modules/system/system.admin.inc 24 Feb 2010 20:18:39 -0000
@@ -50,7 +50,7 @@
// Only show blocks for items which are not containers, or those which
// are containers and do have items we can show.
$block['show'] = TRUE;
- $block['title'] = l($item['title'], $item['href'], $item['localized_options']);
+ $block['title'] = l($item['title'], $item['href'], $item['localized_options']);
if (!empty($content)) {
$block['content'] .= theme('admin_block_content', array('content' => $content));
}
@@ -848,6 +848,30 @@
return system_modules_confirm_form($files, $form_state['storage']);
}
+ $form['modulesfilter'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Filter modules'),
+ '#description' => t('Enter keywords to filter the list of modules.'),
+ );
+
+ $form['modulesfilter']['#attached']['library'][] = array('system', 'instantfilter');
+ $form['modulesfilter']['#attached']['js'][] = array('type' => 'setting', 'data' => array(
+ 'instantfilter' => array(
+ 'edit-modulesfilter' => array(
+ 'container' => '#system-modules',
+ 'groups' => array(
+ 'fieldset' => array(),
+ 'fieldset table > tbody' => array('items' => 'tr', 'zebra' => TRUE)
+ ),
+ 'items' => array(
+ 'table > tbody > tr' => array(
+ 'ignore' => 'td.version, .admin-requirements, td.help, td.permissions, td.configure',
+ )
+ )
+ )
+ ),
+ ));
+
$modules = array();
$form['modules'] = array('#tree' => TRUE);
@@ -2512,7 +2536,7 @@
$label .= ' for="' . $module['enable']['#id'] . '"';
}
$row[] = $label . '>' . drupal_render($module['name']) . '';
- $row[] = drupal_render($module['version']);
+ $row[] = array('data' => drupal_render($module['version']), 'class' => 'version');
// Add the description, along with any modules it requires.
$description = drupal_render($module['description']);
if ($module['#requires']) {
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.890
diff -u -r1.890 system.module
--- modules/system/system.module 17 Feb 2010 09:09:30 -0000 1.890
+++ modules/system/system.module 24 Feb 2010 20:18:39 -0000
@@ -1107,6 +1107,16 @@
),
);
+ // Instant Filter.
+ $libraries['instantfilter'] = array(
+ 'title' => 'Instant Filter',
+ 'website' => 'http://drupal.org/node/396478',
+ 'version' => '1.0',
+ 'js' => array(
+ 'misc/instantfilter.js' => array(),
+ ),
+ );
+
// Farbtastic.
$libraries['farbtastic'] = array(
'title' => 'Farbtastic',
Index: misc/instantfilter.js
===================================================================
RCS file: misc/instantfilter.js
diff -N misc/instantfilter.js
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ misc/instantfilter.js 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,217 @@
+// $Id$
+(function ($) {
+
+/**
+ * Attaches the instantfilter behavior.
+ */
+Drupal.behaviors.instantFilter = {
+ attach: function (context, settings) {
+ // Bind instantfilter behaviors specified in the settings.
+ for (var base in settings.instantfilter) {
+ $('#' + base + ':input', context).once('instantfilter', function () {
+ $(this).data('instantfilter', new Drupal.instantFilter(this, settings.instantfilter[base]));
+ });
+ }
+
+ // Bind instantfilter behaviors to all elements showing the class.
+ $('.instantfilter-filter:input', context).once('instantfilter', function () {
+ $(this).data('instantfilter', new Drupal.instantFilter(this));
+ });
+
+ // If context has an parent with the instantfilter class, then context is
+ // added dynamicly (e.g. using AJAX). If so, the index needs to be rebuild.
+ $(context).closest('.instantfilter-container').each(function () {
+ $(this).trigger('drupalInstantFilterIndexInvalidated');
+ });
+ }
+};
+
+/**
+ * The instantFilter object.
+ *
+ * @constructor
+ * @param element
+ * DOM input element to attach the instantfilter to.
+ * @param settings
+ * Settings for the instantfilter.
+ */
+Drupal.instantFilter = function (element, settings) {
+ var self = this;
+ this.instanceID = Drupal.instantFilter.instanceCounter++;
+
+ this.settings = $.extend({
+ container: null,
+ groups: {
+ '.instantfilter-group': { zebra: false }
+ },
+ items: {
+ '.instantfilter-item': { ignore: null }
+ },
+ empty: Drupal.t('There were no results.')
+ }, settings);
+ this.index = false;
+
+ this.element = $(element);
+ if (this.settings.container) {
+ this.container = $(this.settings.container);
+ }
+ else {
+ this.container = $(document.body);
+ }
+
+ var events = (this.element.is('[type=text]')) ? 'keyup' : 'change';
+ this.element.bind(events, function () {
+ self.applyFilter($.trim($(this).val().toLowerCase()));
+ });
+
+ this.container
+ .addClass('instantfilter-container')
+ .bind('drupalInstantFilterIndexInvalidated', function () {
+ self.index = false;
+ });
+
+ // Apply filter once if the element is not empty.
+ if (this.element.val()) {
+ self.applyFilter($.trim(this.element.val().toLowerCase()));
+ }
+};
+
+Drupal.instantFilter.instanceCounter = 0;
+
+/**
+ * Get text value of an item.
+ */
+Drupal.instantFilter.prototype.getValue = function (element, settings) {
+ var text = '';
+ if (!settings.ignore || !$(element).is(settings.ignore)) {
+ for (var i = 0; i < element.childNodes.length; i++) {
+ if (element.childNodes[i].nodeType == 1) { // ELEMENT_NODE
+ text += this.getValue(element.childNodes[i], settings);
+ }
+ else if (element.childNodes[i].nodeType == 3) { // TEXT_NODE
+ text += element.childNodes[i].nodeValue;
+ }
+ }
+ }
+ return text.toLowerCase();
+};
+
+/**
+ * Rebuild index of the filter.
+ */
+Drupal.instantFilter.prototype.rebuildIndex = function () {
+ var self = this;
+ var allitems = '';
+
+ this.items = [];
+ this.groups = [];
+
+ var i = 0;
+ for (var selector in this.settings.items) {
+ allitems += ',' + selector;
+
+ this.container.find(selector).each(function () {
+ var item = $.extend({}, self.settings.items[selector], {
+ element: $(this),
+ value: self.getValue(this, self.settings.items[selector]),
+ groups: []
+ });
+
+ self.items[i] = item;
+ item.element.data('instantfilter:' + self.instanceID + ':item', i);
+ i++;
+ });
+ }
+
+ allitems = allitems.substring(1);
+
+ var i = 0;
+ for (var selector in this.settings.groups) {
+ this.container.find(selector).each(function () {
+ var group = $.extend({}, self.settings.groups[selector], {
+ element: $(this),
+ total: 0,
+ results: 0
+ });
+
+ // Link group to items.
+ group.element.find(group.items || allitems).each(function () {
+ group.total++;
+
+ var item = $(this).data('instantfilter:' + self.instanceID + ':item');
+ if (item !== undefined && self.items[item]) {
+ self.items[item].groups.push(group);
+ }
+ });
+
+ self.groups[i] = group;
+ group.element.data('instantfilter:' + self.instanceID + ':group', i);
+ i++;
+ });
+ }
+};
+
+/**
+ * Filters all items for the given string.
+ *
+ * All items containing the string will stay visible, while other items are
+ * hidden. All groups that don't have any matching items will also be hidden.
+ *
+ * @param search
+ * The string to filter items on.
+ */
+Drupal.instantFilter.prototype.applyFilter = function (search) {
+ if (!this.index) {
+ this.rebuildIndex();
+ this.index = true;
+ }
+
+ this.search = search;
+ // Reset the total and group result counters.
+ this.results = 0;
+ for (var i in this.groups) {
+ this.groups[i].results = 0;
+ }
+
+ for (var i in this.items) {
+ var item = this.items[i];
+
+ var match = item.value.indexOf(this.search) >= 0;
+
+ if (match) {
+ // Increment the total and item's groups result counters.
+ this.results++;
+ for (var i in item.groups) {
+ item.groups[i].results++;
+ }
+ }
+
+ item.element[match ? 'show' : 'hide']();
+ }
+
+ for (var i in this.groups) {
+ var group = this.groups[i];
+
+ group.element[group.results ? 'show' : 'hide']();
+
+ if (group.zebra) {
+ group.element.children(':visible')
+ .removeClass('odd even')
+ .filter(':odd').addClass('even').end()
+ .filter(':even').addClass('odd');
+ }
+ }
+
+ // If any results are found, remove the 'no results' message.
+ // Otherwise display the 'no results message.
+ if (this.results) {
+ this.element.closest('.form-item').find('.instantfilter-no-results').remove();
+ }
+ else {
+ if (!this.element.closest('.form-item').find('.instantfilter-no-results').length) {
+ this.element.closest('.form-item').append($('').text(this.settings.empty));
+ };
+ };
+};
+
+})(jQuery);