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 9b19bbb..9051280 100644 --- modules/system/system.admin.inc +++ modules/system/system.admin.inc @@ -562,11 +562,41 @@ 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'); + + // 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'), + // This uses a custom JS function to insert the new content into the page. + // See Drupal.behaviors.modulesFormAhahSettings in system.js for details. + '#ahah' => array( + 'callback' => 'system_modules_search_js', + 'keypress' => TRUE, + 'event' => 'click', + ), + ); + // Get current list of modules. $files = module_rebuild_cache(); @@ -583,17 +613,132 @@ 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, + '#type' => 'vertical_tabs', + ); + + // 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 (!empty($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; @@ -633,31 +778,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; } /** @@ -792,10 +915,12 @@ 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']); + if (!empty($group) && is_array($group)) { + foreach ($group as $module => $enabled) { + $modules[$module] = array('group' => $group_name, 'enabled' => $enabled['enable']); + } } } } @@ -804,7 +929,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 aedb75f..27174b4 100644 --- modules/system/system.js +++ modules/system/system.js @@ -124,4 +124,71 @@ Drupal.behaviors.poweredByPreview = { } }; -})(jQuery); +/** + * AHAH insertNewContent 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.behaviors.modulesFormAhahSettings = { + attach: function(context, settings) { + Drupal.ahah['edit-search-submit'].insertNewContent = function(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); + } + // Otherwise, append the search fieldset to the wrapper and add a vertical tab for it. + else { + $('#system-modules .vertical-tabs-panes').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); + + // Give the search tab the focus and update the description. + tab.focus(); + Drupal.modulesFormSetFieldsetSummary(tab.fieldset); + tab.updateSummary(); + + // 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.modulesFormFieldsetSummaries = { + attach: function(context) { + $('#system-modules .vertical-tabs-panes > fieldset', context).each( function() { + Drupal.modulesFormSetFieldsetSummary(this); + }); + } +}; + +/** + * Set the summary for a fieldset on the modules form. + */ +Drupal.modulesFormSetFieldsetSummary = function(fieldset) { + $(fieldset).setSummary(function(context) { + modules = $('input.form-checkbox', context).size(); + enabled = $('input.form-checkbox[checked]', context).size(); + return Drupal.t('!modules modules, !enabled enabled.', { '!modules': modules, '!enabled': enabled }); + }); +}; + +})(jQuery); \ No newline at end of file diff --git themes/garland/style.css themes/garland/style.css index 5cfd0f2..4f55380 100644 --- themes/garland/style.css +++ themes/garland/style.css @@ -899,6 +899,13 @@ div.vertical-tabs ul.vertical-tabs-list li.selected a strong { } /** + * Modules form. + */ +#system-modules #edit-search { + margin-right: 5%; +} + +/** * Syndication icons and block */ #block-node-syndicate h2 {