diff --git a/core/includes/common.inc b/core/includes/common.inc index 722c1f6..f8558f6 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -7071,6 +7071,7 @@ function drupal_parse_info_file($filename) { else { $data = file_get_contents($filename); $info[$filename] = drupal_parse_info_format($data); + $info[$filename]['mtime'] = filemtime($filename); } } return $info[$filename]; diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc index 97a4673..45c43d0 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -919,6 +919,183 @@ function system_modules($form, $form_state = array()) { } /** + * Menu callback; provides module enable/disable interface. + * + * The list of modules gets populated by module.info files, which contain each + * module's name, description, and information about which modules it requires. + * See drupal_parse_info_file() for information on module.info descriptors. + * + * Dependency checking is performed to ensure that a module: + * - can not be enabled if there are disabled modules it requires. + * - can not be disabled if there are enabled modules which depend on it. + * + * @param $form_state + * An associative array containing the current state of the form. + * + * @return + * The form array. + * + * @ingroup forms + * @see theme_system_modules() + * @see system_modules_submit() + */ +function system_modules_recent($form, $form_state = array()) { + // Get current list of modules. + $files = system_rebuild_module_data(); + + // Remove hidden modules from display list. + $visible_files = $files; + foreach ($visible_files as $filename => $file) { + if (!empty($file->info['hidden'])) { + unset($visible_files[$filename]); + } + } + + uasort($visible_files, 'system_sort_modules_by_info_mtime'); + + // If the modules form was submitted, then system_modules_submit() runs first + // 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'])) { + return system_modules_confirm_form($visible_files, $form_state['storage']); + } + + $modules = array(); + $form['modules'] = array('#tree' => TRUE); + + // Used when checking if module implements a help page. + $help_arg = module_exists('help') ? drupal_help_arg() : FALSE; + + // Used when displaying modules that are required by the install profile. + require_once DRUPAL_ROOT . '/core/includes/install.inc'; + $distribution_name = check_plain(drupal_install_profile_distribution_name()); + + // Iterate through each of the modules. + foreach ($visible_files as $filename => $module) { + $extra = array(); + $extra['enabled'] = (bool) $module->status; + if (!empty($module->info['required'] )) { + $extra['disabled'] = TRUE; + $extra['required_by'][] = $distribution_name . (!empty($module->info['explanation']) ? ' ('. $module->info['explanation'] .')' : ''); + } + + // If this module requires other modules, add them to the array. + foreach ($module->requires as $requires => $v) { + if (!isset($files[$requires])) { + $extra['requires'][$requires] = t('@module (missing)', array('@module' => drupal_ucfirst($requires))); + $extra['disabled'] = TRUE; + } + // Only display visible modules. + elseif (isset($visible_files[$requires])) { + $requires_name = $files[$requires]->info['name']; + // Disable this module if it is incompatible with the dependency's version. + if ($incompatible_version = drupal_check_incompatibility($v, str_replace(DRUPAL_CORE_COMPATIBILITY . '-', '', $files[$requires]->info['version']))) { + $extra['requires'][$requires] = t('@module (incompatible with version @version)', array( + '@module' => $requires_name . $incompatible_version, + '@version' => $files[$requires]->info['version'], + )); + $extra['disabled'] = TRUE; + } + // Disable this module if the dependency is incompatible with this + // version of Drupal core. + elseif ($files[$requires]->info['core'] != DRUPAL_CORE_COMPATIBILITY) { + $extra['requires'][$requires] = t('@module (incompatible with this version of Drupal core)', array( + '@module' => $requires_name, + )); + $extra['disabled'] = TRUE; + } + elseif ($files[$requires]->status) { + $extra['requires'][$requires] = t('@module (enabled)', array('@module' => $requires_name)); + } + else { + $extra['requires'][$requires] = t('@module (disabled)', array('@module' => $requires_name)); + } + } + } + // Generate link for module's help page, if there is one. + if ($help_arg && $module->status && in_array($filename, module_implements('help'))) { + if (module_invoke($filename, 'help', "admin/help#$filename", $help_arg)) { + $extra['links']['help'] = array( + '#type' => 'link', + '#title' => t('Help'), + '#href' => "admin/help/$filename", + '#options' => array('attributes' => array('class' => array('module-link', 'module-link-help'), 'title' => t('Help'))), + ); + } + } + // Generate link for module's permission, if the user has access to it. + if ($module->status && user_access('administer permissions') && in_array($filename, module_implements('permission'))) { + $extra['links']['permissions'] = array( + '#type' => 'link', + '#title' => t('Permissions'), + '#href' => 'admin/people/permissions', + '#options' => array('fragment' => 'module-' . $filename, 'attributes' => array('class' => array('module-link', 'module-link-permissions'), 'title' => t('Configure permissions'))), + ); + } + // Generate link for module's configuration page, if the module provides + // one. + if ($module->status && isset($module->info['configure'])) { + $configure_link = menu_get_item($module->info['configure']); + if ($configure_link['access']) { + $extra['links']['configure'] = array( + '#type' => 'link', + '#title' => t('Configure'), + '#href' => $configure_link['href'], + '#options' => array('attributes' => array('class' => array('module-link', 'module-link-configure'), 'title' => $configure_link['description'])), + ); + } + } + + // If this module is required by other modules, list those, and then make it + // impossible to disable this one. + foreach ($module->required_by as $required_by => $v) { + // Hidden modules are unset already. + if (isset($visible_files[$required_by])) { + if ($files[$required_by]->status == 1 && $module->status == 1) { + $extra['required_by'][] = t('@module (enabled)', array('@module' => $files[$required_by]->info['name'])); + $extra['disabled'] = TRUE; + } + else { + $extra['required_by'][] = t('@module (disabled)', array('@module' => $files[$required_by]->info['name'])); + } + } + } + // Find the time interval to group this module under. + $interval = _system_get_interval($module->info['mtime']); + $form['modules'][$interval][$filename] = _system_modules_build_row($module->info, $extra); + } + + // Fieldset by 'today', 'yesterday', 'this week', ... + // Add basic information to the fieldsets. + foreach (element_children($form['modules']) as $interval) { + $form['modules'][$interval] += array( + '#type' => 'fieldset', + '#title' => t($interval), + '#collapsible' => TRUE, + '#theme' => 'system_modules_fieldset', + '#header' => array( + array('data' => t('Enabled'), 'class' => array('checkbox')), + t('Name'), + t('Version'), + t('Description'), + array('data' => t('Operations'), 'colspan' => 3), + ), + // Ensure that the "Core" package fieldset comes first. + '#weight' => _system_interval_weight($interval), + ); + } + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save configuration'), + ); + $form['#action'] = url('admin/modules/recent/confirm'); + + return $form; +} +/** * Array sorting callback; sorts modules or themes by their name. */ function system_sort_modules_by_info_name($a, $b) { @@ -928,6 +1105,13 @@ function system_sort_modules_by_info_name($a, $b) { /** * Array sorting callback; sorts modules or themes by their name. */ +function system_sort_modules_by_info_mtime($a, $b) { + return strcasecmp($a->info['mtime'], $b->info['mtime']); +} + +/** + * Array sorting callback; sorts modules or themes by their name. + */ function system_sort_themes($a, $b) { if ($a->is_default) { return -1; @@ -939,6 +1123,55 @@ function system_sort_themes($a, $b) { } /** + * Return a list of time intervals. + * + * Glorified define. + * + * @see: _system_interval_weight() + * @see: _system_get_timegroup() + */ +function _system_intervals() { + // Find the most recent midnight. + $today = strtotime(date('Y-m-d 00:00:00')); + return array( + 'Today' => array('timestamp' => $today, 'weight' => -10), + 'Yesterday' => array( 'timestamp' => ($today - (60 * 60 * 24)), 'weight' => -9), + 'The past week' => array( 'timestamp' => ($today - (60 * 60 * 24 * 7)), 'weight' => -5), + 'More than a week ago' => array( 'timestamp' => 0, 'weight' => -1), + ); +} + +/* + * Return the weighting for an interval. + * + * @see: system_modules_recent() + */ +function _system_interval_weight($interval) { + $intervals = _system_intervals(); + foreach ($intervals as $name => $data) { + if ($interval == $name) { + return $data['weight']; + } + } + return NULL; +} + +/** + * Return a time interval designation for a timestamp. + * + * @see: system_modules_recent() + */ +function _system_get_interval($timestamp) { + $intervals = _system_intervals(); + foreach ($intervals as $name => $data) { + if ($timestamp >= $data['timestamp']) { + return $name; + } + } + return 'Unknown'; +} + +/** * Build a table row for the system modules page. */ function _system_modules_build_row($info, $extra) { diff --git a/core/modules/system/system.module b/core/modules/system/system.module index f16b73f..8f312ee 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -671,6 +671,19 @@ function system_menu() { 'access arguments' => array('administer modules'), 'type' => MENU_VISIBLE_IN_BREADCRUMB, ); + $items['admin/modules/recent'] = array( + 'title' => 'Recently Added', + 'page arguments' => array('system_modules_recent'), + 'access arguments' => array('administer modules'), + 'type' => MENU_LOCAL_TASK, + 'file' => 'system.admin.inc', + 'weight' => 20, + ); + $items['admin/modules/recent/confirm'] = array( + 'title' => 'List', + 'access arguments' => array('administer modules'), + 'type' => MENU_VISIBLE_IN_BREADCRUMB, + ); $items['admin/modules/uninstall'] = array( 'title' => 'Uninstall', 'page arguments' => array('system_modules_uninstall'),