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'),