? .DS_Store
? drupal_cron-19173-40-axyjo.patch
? drupal_cron-19173-43-axyjo.patch
? drupal_cron-19173-44-axyjo.patch
? drupal_cron-19173-47-axyjo.patch
? drupal_cron-19173-54-axyjo.patch
? drupal_cron-19173-55-axyjo.patch
? drupal_cron-19173-57-axyjo.patch
? drupal_cron-19173-59-axyjo.patch
? drupal_cron-19173-60-axyjo.patch
? drupal_cron-19173-61-axyjo.patch
? drupal_cron-19173-63-axyjo.patch
? drupal_cron-19173-66-axyjo.patch
? sites/default/files
? sites/default/private
? sites/default/settings.php
Index: cron.php
===================================================================
RCS file: /cvs/drupal/drupal/cron.php,v
retrieving revision 1.42
diff -u -p -r1.42 cron.php
--- cron.php	8 Feb 2009 20:27:51 -0000	1.42
+++ cron.php	5 Sep 2009 11:41:55 -0000
@@ -4,6 +4,8 @@
 /**
  * @file
  * Handles incoming requests to fire off regularly-scheduled tasks (cron jobs).
+ * Takes one parameter, include or exclude, which contains a comma separated
+ * list of modules to either include or exclude from the cron run.
  */
 
 /**
@@ -14,7 +16,27 @@ define('DRUPAL_ROOT', getcwd());
 include_once DRUPAL_ROOT . '/includes/bootstrap.inc';
 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
 if (isset($_GET['cron_key']) && variable_get('cron_key', 'drupal') == $_GET['cron_key']) {
-  drupal_cron_run();
+  $include = (isset($_GET['include'])) ? $_GET['include'] : '';
+  $exclude = (isset($_GET['exclude'])) ? $_GET['exclude'] : '';
+
+  if ($include && $exclude) {
+    watchdog('cron', t('Cron has been issued both the include and the exclude parameters.'), array(), WATCHDOG_ERROR);
+    return FALSE;
+  }
+  elseif ($include && !$exclude) {
+    $cron_param = $include;
+    $mode = 'include';
+  }
+  elseif (!$include && $exclude) {
+    $cron_param = $exclude;
+    $mode = 'exclude';
+  }
+  else {
+    $cron_param = NULL;
+    $mode = NULL;
+  }
+
+  drupal_cron_run($cron_param, $mode);
 }
 else {
   watchdog('cron', 'Cron could not run because an invalid key was used.', array(), WATCHDOG_NOTICE);
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.982
diff -u -p -r1.982 common.inc
--- includes/common.inc	5 Sep 2009 10:05:38 -0000	1.982
+++ includes/common.inc	5 Sep 2009 11:42:03 -0000
@@ -3744,11 +3744,25 @@ function drupal_page_set_cache() {
 }
 
 /**
- * Executes a cron run when called
+ * Executes a cron run when called.
+ *
+ * If the function is called with no arguments, then cron for all modules is
+ * run. If only one argument is passed in, the result is FALSE. If both
+ * arguments are provided, then the function includes or excludes the modules
+ * specified in the first argument.
+ *
+ * @param $list
+ *   A comma separated list of modules to act upon.
+ * @param $mode
+ *   The mode of the operation. One of the following values are possible:
+ *   - '' or NULL
+ *   - 'include'
+ *   - 'exclude'
+ *
  * @return
- * Returns TRUE if ran successfully
+ *   Returns TRUE if ran successfully.
  */
-function drupal_cron_run() {
+function drupal_cron_run($list = NULL, $mode = NULL) {
   // Allow execution to continue even if the request gets canceled.
   @ignore_user_abort(TRUE);
 
@@ -3764,7 +3778,7 @@ function drupal_cron_run() {
       // was not reset due to a database error.
       watchdog('cron', 'Cron has been running for more than an hour and is most likely stuck.', array(), WATCHDOG_ERROR);
 
-      // Release cron semaphore
+      // Release cron semaphore.
       variable_del('cron_semaphore');
     }
     else {
@@ -3773,23 +3787,63 @@ function drupal_cron_run() {
     }
   }
   else {
-    // Register shutdown callback
+    // Register shutdown callback.
     register_shutdown_function('drupal_cron_cleanup');
 
-    // Lock cron semaphore
+    // Lock cron semaphore.
     variable_set('cron_semaphore', REQUEST_TIME);
 
+    // Create $cron_message so that it is defined.
+    $cron_message = '';
+
+    // Retrieve cron_last array to modify.
+    $cron_last = variable_get('cron_last', array());
+
     // Iterate through the modules calling their cron handlers (if any):
-    module_invoke_all('cron');
+    if (!$list && !$mode) {
+      // No parameters were passed, so we're running all modules.
+      module_invoke_all('cron');
+      foreach (module_implements('cron') as $module) {
+        $cron_last[$module] = REQUEST_TIME;
+      }
+      watchdog('cron', t('Cron run completed for all modules.'), array(), WATCHDOG_NOTICE);
+    }
+    elseif ($list) {
+      $module_list = module_implements('cron');
+      $cron_modules = explode(',', $list);
+      $run_array = array();
+      if ($mode == 'include') {
+        // Get only a list of modules that are specified AND enabled.
+        $run_array = array_intersect($module_list, $cron_modules);
+      }
+      elseif ($mode == 'exclude') {
+        // Get only a list of modules that are NOT specified but enabled.
+        $run_array = array_diff($module_list, $cron_modules);
+      }
+      if ($run_array) {
+        foreach ($run_array as $module) {
+          module_invoke($module, 'cron');
+          $cron_last[$module] = REQUEST_TIME;
+        }
+        $msg = implode(', ', $run_array);
+        watchdog('cron', t('Cron run completed for modules: @ran.', array('@ran' => $msg)), array(), WATCHDOG_NOTICE);
+      }
+      else {
+        watchdog('cron', t('Cron did not run because no valid modules were specified.'), array(), WATCHDOG_ERROR);
+      }
+    }
+    else {
+      // Catch anything that's not covered already.
+      return FALSE;
+    }
 
-    // Record cron time
-    variable_set('cron_last', REQUEST_TIME);
-    watchdog('cron', 'Cron run completed.', array(), WATCHDOG_NOTICE);
+    // Record cron time for each module.
+    variable_set('cron_last', $cron_last);
 
-    // Release cron semaphore
+    // Release cron semaphore.
     variable_del('cron_semaphore');
 
-    // Return TRUE so other functions can check if it did run successfully
+    // Return TRUE so other functions can check if it did run successfully.
     return TRUE;
   }
 }
Index: includes/module.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/module.inc,v
retrieving revision 1.157
diff -u -p -r1.157 module.inc
--- includes/module.inc	24 Aug 2009 00:14:18 -0000	1.157
+++ includes/module.inc	5 Sep 2009 11:42:03 -0000
@@ -255,6 +255,9 @@ function module_enable($module_list, $di
  */
 function module_disable($module_list) {
   $invoke_modules = array();
+
+  // Get the $cron_last variable so that we can remove the disabled modules.
+  $cron_last = variable_get('cron_last', array());
   foreach ($module_list as $module) {
     if (module_exists($module)) {
       // Check if node_access table needs rebuilding.
@@ -264,6 +267,7 @@ function module_disable($module_list) {
 
       module_load_install($module);
       module_invoke($module, 'disable');
+      unset($cron_last[$module]);
       db_update('system')
         ->fields(array('status' => 0))
         ->condition('type', 'module')
@@ -274,6 +278,9 @@ function module_disable($module_list) {
     }
   }
 
+  // Store the cleaned-up version of $cron_last.
+  variable_set('cron_last', $cron_last);
+
   if (!empty($invoke_modules)) {
     // Refresh the module list to exclude the disabled modules.
     module_list(TRUE);
Index: modules/system/system.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.install,v
retrieving revision 1.383
diff -u -p -r1.383 system.install
--- modules/system/system.install	29 Aug 2009 10:38:58 -0000	1.383
+++ modules/system/system.install	5 Sep 2009 11:42:08 -0000
@@ -160,9 +160,12 @@ function system_requirements($phase) {
     // Cron configuration help text.
     $help = $t('For more information, see the online handbook entry for <a href="@cron-handbook">configuring cron jobs</a>.', array('@cron-handbook' => 'http://drupal.org/cron'));
 
-    // Determine when cron last ran.
-    $cron_last = variable_get('cron_last');
-    if (!is_numeric($cron_last)) {
+    // Determine when cron last ran (even partially).
+    $cron_last_array = variable_get('cron_last');
+    if (is_array($cron_last_array)) {
+      $cron_last = max($cron_last_array);
+    }
+    else {
       $cron_last = variable_get('install_time', 0);
     }
 
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.784
diff -u -p -r1.784 system.module
--- modules/system/system.module	1 Sep 2009 16:50:12 -0000	1.784
+++ modules/system/system.module	5 Sep 2009 11:42:12 -0000
@@ -3150,7 +3150,7 @@ function system_run_cron_image() {
     // Run cron automatically if it has never run or threshold was crossed.
     $cron_last = variable_get('cron_last', NULL);
     $cron_threshold = variable_get('cron_safe_threshold', DRUPAL_CRON_DEFAULT_THRESHOLD);
-    if (!isset($cron_last) || (REQUEST_TIME - $cron_last > $cron_threshold)) {
+    if (!isset($cron_last) || (REQUEST_TIME - min($cron_last) > $cron_threshold)) {
       // Lock cron threshold semaphore.
       variable_set('cron_threshold_semaphore', REQUEST_TIME);
       drupal_cron_run();
Index: modules/system/system.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.test,v
retrieving revision 1.75
diff -u -p -r1.75 system.test
--- modules/system/system.test	1 Sep 2009 16:50:12 -0000	1.75
+++ modules/system/system.test	5 Sep 2009 11:42:14 -0000
@@ -387,6 +387,51 @@ class CronRunTestCase extends DrupalWebT
     $this->drupalGet($base_url . '/cron.php', array('external' => TRUE, 'query' => 'cron_key=' . $key));
     $this->assertResponse(200);
 
+    // Run cron with a single, valid include parameter.
+    $this->drupalGet($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key, 'include' => 'system')));
+    $this->assertResponse(200);
+
+    // Run cron with a single, valid exclude parameter.
+    $this->drupalGet($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key, 'exclude' => 'system')));
+    $this->assertResponse(200);
+
+    // Run cron with a single, invalid include parameter.
+    $name = $this->randomName(8);
+    $this->drupalGet($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key, 'include' => $name)));
+    $this->assertResponse(200);
+
+    // Run cron with a single, invalid exclude parameter.
+    $name = $this->randomName(8);
+    $this->drupalGet($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key, 'exclude' => $name)));
+    $this->assertResponse(200);
+
+    // Run cron with two include parameters where both are valid.
+    $this->drupalGet($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key, 'include' => 'system,node')));
+    $this->assertResponse(200);
+
+    // Run cron with two exclude parameters where both are valid.
+    $this->drupalGet($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key, 'exclude' => 'system,node')));
+    $this->assertResponse(200);
+
+    // Run cron with two include parameters where only one is valid.
+    $name = $this->randomName(8);
+    $this->drupalGet($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key, 'include' => 'system,'.$name)));
+    $this->assertResponse(200);
+
+    // Run cron with two exclude parameters where only one is valid.
+    $name = $this->randomName(8);
+    $this->drupalGet($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key, 'exclude' => 'system,'.$name)));
+    $this->assertResponse(200);
+
+    /*
+     * We can test all of these much better once we get assertLogMessage() into
+     * DrupalWebTestCase (see issue #500394).
+     */
+
+    // Test $cron_last variable.
+    $cron_last = variable_get('cron_last');
+    $this->assertTrue(is_array($cron_last), t('Make sure that $cron_last is an array.'));
+
     // Execute cron directly.
     $this->assertTrue(drupal_cron_run(), t('Cron ran successfully.'));
   }
