diff --git modules/system/admin.css modules/system/admin.css index 0a4d01a..3fb8b7e 100644 --- modules/system/admin.css +++ modules/system/admin.css @@ -135,3 +135,23 @@ table.screenshot { html.js .custom-container label { visibility: hidden; } + +/** + * Formatting of the modules form. + */ +#system-modules #edit-search { + margin-bottom: 0; + border-bottom: none; +} + +#system-modules #edit-search #edit-search-string-wrapper, #system-modules #edit-search #edit-search-submit { + float: left; +} + +#system-modules #edit-search #edit-search-string-wrapper { + margin: 0 1em 0 0; +} + +#system-modules div.vertical-tabs { + margin-top: 0; +} \ No newline at end of file diff --git modules/system/system.admin.inc modules/system/system.admin.inc index 82eef0a..06d38a3 100644 --- modules/system/system.admin.inc +++ modules/system/system.admin.inc @@ -570,11 +570,44 @@ function _system_is_incompatible(&$incompatible, $files, $file) { * The form array. */ function system_modules($form_state = array()) { - // Clear all caches. - registry_rebuild(); - drupal_theme_rebuild(); - node_types_rebuild(); - cache_clear_all('schema', 'cache'); + drupal_add_js(drupal_get_path('module', 'system') . '/system.js'); + drupal_add_js('misc/vertical-tabs.js', array('weight' => 0)); + drupal_add_css('misc/vertical-tabs.css'); + + // Clear all caches, but not when searching the modules page to save time. + if (!isset($form_state['storage']['search_string'])) { + registry_rebuild(); + drupal_theme_rebuild(); + node_types_rebuild(); + cache_clear_all('schema', 'cache'); + } + + // Fieldset to search the modules form. + $form['search'] = array( + '#type' => 'fieldset', + '#weight' => -5, + '#collapsible' => FALSE, + '#title' => t('Search modules'), + ); + + $form['search']['search_string'] = array( + '#type' => 'textfield', + ); + + $form['search']['search_submit'] = array( + '#type' => 'submit', + '#value' => t('Search'), + '#submit' => array('system_modules_search_submit'), + '#ahah' => array( + 'callback' => 'system_modules_search_js', + 'wrapper' => 'edit-modules-search-pane', + 'method' => 'replace', + 'success_callback' => 'moduleFormSearch', + 'keypress' => TRUE, + 'event' => 'click', + ), + ); + // Get current list of modules. $files = module_rebuild_cache(); @@ -591,17 +624,133 @@ function system_modules($form_state = array()) { // and if there are unfilled required modules, then form_state['storage'] is // filled, triggering a rebuild. In this case we need to display a // confirmation form. - if (!empty($form_state['storage'])) { + if (!empty($form_state['storage']) && !isset($form_state['storage']['search_string'])) { return system_modules_confirm_form($files, $form_state['storage']); } $modules = array(); - $form['modules'] = array('#tree' => TRUE); + $form['modules'] = array( + '#tree' => TRUE, + '#prefix' => '
', + '#suffix' => '
', + ); + + // Add dependencies and additional information to the files array. + $files = _system_modules_build_files_for_form($files); + + // If the search button was clicked, add the search fieldset with matching modules. + if (isset($form_state['storage']['search_string'])) { + $form['modules']['search_pane'] = _system_modules_search_form($files, $form_state['storage']['search_string']); + } + // Otherwise, add all modules, with a fieldset for each category. + else { + // Iterate through each of the modules. + foreach ($files as $filename => $module) { + $form['modules'][$module->info['package']][$filename] = _system_modules_build_row($module->info, $module->extra); + } + + // Add basic information to the fieldsets. + foreach (element_children($form['modules']) as $package) { + $form['modules'][$package] += array( + '#type' => 'fieldset', + '#title' => t($package), + '#theme' => 'system_modules_fieldset', + '#header' => array( + array('data' => t('Enabled'), 'class' => 'checkbox'), + t('Name'), + t('Version'), + t('Description'), + ), + ); + } + } + + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Save configuration'), + ); + $form['#action'] = url('admin/build/modules/list/confirm'); + + return $form; +} +/** + * Submit callback for the search button on the modules form. + * + * If a search string is present, store it in $form_state['storage']. + */ +function system_modules_search_submit($form, &$form_state) { + if (isset($form_state['values']['search_string'])) { + $form_state['storage']['search_string'] = $form_state['values']['search_string']; + } + else { + unset($form_state['storage']['search_string']); + } +} + +/** + * AHAH callback for the search button on the modules form. + */ +function system_modules_search_js($form, &$form_state) { + if (isset($form['modules']['search_pane'])) { + $search_form = $form['modules']['search_pane']; + drupal_json(array('status' => TRUE, 'data' => drupal_render($search_form))); + } +} + +/** + * Creates the search result fieldset for the modules form. + * + * This returns a fieldset similar to the normal module category fieldsets, + * but containing modules whose names or descriptions match $search_string. + */ +function _system_modules_search_form($files, $search_string) { + $search_form = array( + '#type' => 'fieldset', + '#weight' => 9999, + '#title' => t('Search results'), + ); + + if (!empty($search_string)) { + // Iterate over the module files, and add rows for matching names or + // descriptions. + foreach ($files as $filename => $module) { + if (stripos($module->info['name'], $search_string) !== FALSE || + stripos($module->info['description'], $search_string) !== FALSE || + stripos($module->filename, $search_string) !== FALSE) { + $search_form[$filename] = _system_modules_build_row($module->info, $module->extra); + } + } + } + + if (element_children($search_form)) { + $search_form += array( + '#theme' => 'system_modules_fieldset', + '#header' => array( + array('data' => t('Enabled'), 'class' => 'checkbox'), + t('Name'), + t('Version'), + t('Description'), + ), + ); + } + else { + $search_form['info']['#markup'] = t('Your search did not return any results.'); + } + + return $search_form; +} + +/** + * Add information to the module files array that is needed on the modules form. + * + * Iterates over the module files and adds dependency information to be used + * in the modules form. + */ +function _system_modules_build_files_for_form($files) { // Used when checking if module implements a help page. $help_arg = module_exists('help') ? drupal_help_arg() : FALSE; - // Iterate through each of the modules. foreach ($files as $filename => $module) { $extra = array(); $extra['enabled'] = (bool) $module->status; @@ -641,31 +790,9 @@ function system_modules($form_state = array()) { } } } - $form['modules'][$module->info['package']][$filename] = _system_modules_build_row($module->info, $extra); + $files[$filename]->extra = $extra; } - // Add basic information to the fieldsets. - foreach (element_children($form['modules']) as $package) { - $form['modules'][$package] += array( - '#type' => 'fieldset', - '#title' => t($package), - '#collapsible' => TRUE, - '#theme' => 'system_modules_fieldset', - '#header' => array( - array('data' => t('Enabled'), 'class' => 'checkbox'), - t('Name'), - t('Version'), - t('Description'), - ), - ); - } - - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Save configuration'), - ); - $form['#action'] = url('admin/build/modules/list/confirm'); - - return $form; + return $files; } /** @@ -800,7 +927,7 @@ function system_modules_submit($form, &$form_state) { include_once DRUPAL_ROOT . '/includes/install.inc'; $modules = array(); // If we're not coming from the confirmation form, build the list of modules. - if (!isset($form_state['storage'])) { + if (!isset($form_state['storage']['modules'])) { foreach ($form_state['values']['modules'] as $group_name => $group) { foreach ($group as $module => $enabled) { $modules[$module] = array('group' => $group_name, 'enabled' => $enabled['enable']); @@ -812,7 +939,6 @@ function system_modules_submit($form, &$form_state) { // the modules out of $form_state. $modules = $form_state['storage']['modules']; } - // Get a list of all modules, it will be used to find which module requires // which. $files = module_rebuild_cache(); diff --git modules/system/system.js modules/system/system.js index 9ff6c08..f19b6dd 100644 --- modules/system/system.js +++ modules/system/system.js @@ -134,4 +134,69 @@ Drupal.behaviors.poweredByPreview = { } }; +/** + * AHAH success callback for the search button on the modules form. + * + * If no search fieldset is present, this inserts the search fieldset into + * the form and adds a vertical tab for it. If there is already a search fieldset, + * it is replaced by the new one. + */ +Drupal.ahah.prototype.successCallbacks.moduleFormSearch = function(element, response, status) { + new_content = $(response.data); + + // Check if there is already a search tab present, and if so, replace it with + // the new one. + // We copy over the existing verticalTab data property to reduce flickering. + if ($('#system-modules #edit-modules-search-pane').size()) { + var tab = $('#edit-modules-search-pane').data('verticalTab') + tab.fieldset = $(new_content); + $('#system-modules #edit-modules-search-pane').replaceWith(new_content); + } + else { + // Append the search fieldset to the wrapper and add a vertical tab for it. + $('#modules-form-wrapper').append(new_content); + var tab = new Drupal.verticalTab({ title: $('> legend', new_content).text(), fieldset: $(new_content) }); + var list = new_content.parents().find('.vertical-tabs-panes').siblings('ul.vertical-tabs-list'); + list.append(tab.item); + } + + // Add the tab behavior and styling to the new fieldset. + $(new_content) + .addClass('vertical-tabs-pane') + .data('verticalTab', tab) + .data('verticalTabCallback', Drupal.moduleFormVerticalTabCallback) + .find('input, textarea, select') + .change(function() { + tab.updateDescription(); + }); + + // Give the search tab the focus and update the description. + tab.focus(); + tab.updateDescription(); + + // Attach behaviors to the new content. + Drupal.attachBehaviors(new_content); +} + +/** + * Assing the vertical tab callback to update the description to all fieldsets + * on the modules form. + */ +Drupal.behaviors.moduleFormVTab = { + attach: function(context) { + $('#modules-form-wrapper > fieldset', context).each( function() { + $(this).data('verticalTabCallback', Drupal.moduleFormVerticalTabCallback); + }); + } +}; + +/** + * Vertical tab callback to update the descriptions on the modules form. + */ +Drupal.moduleFormVerticalTabCallback = function() { + modules = $('input.form-checkbox', this.fieldset).size(); + enabled = $('input.form-checkbox[checked]', this.fieldset).size(); + return modules + ' modules, ' + enabled + ' enabled.'; +} + })(jQuery); \ No newline at end of file diff --git themes/garland/style.css themes/garland/style.css index a0ffe44..2627313 100644 --- themes/garland/style.css +++ themes/garland/style.css @@ -888,6 +888,13 @@ div.vertical-tabs ul.vertical-tabs-list li.selected a { } /** + * Modules form. + */ +#system-modules #edit-search { + margin-right: 5%; +} + +/** * Syndication icons and block */ #block-node-syndicate h2 {