- Patch #331611 by sun, joshmiller, TheRec, Rob Loach, Damien Tournoud: add a poormanscron-like feature to core.
parent
06d296d5f9
commit
4c028a19fb
|
@ -32,6 +32,8 @@ Drupal 7.0, xxxx-xx-xx (development version)
|
|||
now be achieved with Field API.
|
||||
* Added additional features to the default install profile, and implemented
|
||||
a "slimmed down" install profile designed for developers.
|
||||
* Added a built-in, automated cron run feature, which is triggered by site
|
||||
visitors.
|
||||
* Added an administrator role which is assigned all permisions for
|
||||
installed modules automatically.
|
||||
* Image toolkits are now provided by modules (rather than requiring a manual
|
||||
|
|
15
INSTALL.txt
15
INSTALL.txt
|
@ -231,9 +231,18 @@ INSTALLATION
|
|||
maintenance task, including search module (to build and update the index
|
||||
used for keyword searching), aggregator module (to retrieve feeds from other
|
||||
sites), and system module (to perform routine maintenance and pruning on
|
||||
system tables). To activate these tasks, visit the page "cron.php", which
|
||||
executes maintenance tasks on behalf of installed modules. The URL of the
|
||||
cron.php page requires a "cron key" to protect against unauthorized access.
|
||||
system tables).
|
||||
|
||||
For most sites, the built-in, automated cron feature should be sufficient.
|
||||
Note, however, that cron tasks will only be executed when there are site
|
||||
visitors. You can enable the built-in cron feature at:
|
||||
|
||||
Administer > Configuration and modules > Development > Maintenance mode
|
||||
|
||||
Advanced users may want to ensure that cron tasks are executed periodically.
|
||||
To do this, visit the page "cron.php", which executes maintenance tasks on
|
||||
behalf of installed modules. The URL of the cron.php page requires a "cron
|
||||
key" to protect against unauthorized access.
|
||||
Each cron key is automatically generated during installation and is specific
|
||||
to your site. The full URL of the page, with cron key, is available in the
|
||||
"Cron maintenance tasks" section of the "Status report page" at:
|
||||
|
|
|
@ -1313,7 +1313,16 @@ function system_site_information_settings() {
|
|||
'#description' => t('Render all blocks on the default 404 (not found) page. Disabling blocks can help with performance but might leave users with a less functional site.'),
|
||||
'#default_value' => variable_get('site_404_blocks', 0)
|
||||
);
|
||||
$form['cron_safe_threshold'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Automatically run cron'),
|
||||
'#default_value' => variable_get('cron_safe_threshold', DRUPAL_CRON_DEFAULT_THRESHOLD),
|
||||
'#options' => array(0 => t('Never')) + drupal_map_assoc(array(3600, 10800, 21600, 43200, 86400, 604800), 'format_interval'),
|
||||
'#description' => t('When enabled, the site will check whether cron has been run in the configured interval and automatically run it upon the next page request. For more information visit the <a href="@status-report-url">status report page</a>.', array('@status-report-url' => url('admin/reports/status'))),
|
||||
);
|
||||
|
||||
$form['#validate'][] = 'system_site_information_settings_validate';
|
||||
$form['#submit'][] = 'system_site_information_settings_submit';
|
||||
|
||||
return system_settings_form($form);
|
||||
}
|
||||
|
@ -1333,6 +1342,18 @@ function system_site_information_settings_validate($form, &$form_state) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submit handler for the site-information form.
|
||||
*/
|
||||
function system_site_information_settings_submit($form, &$form_state) {
|
||||
// Clear caches when the cron threshold is changed to ensure that the cron
|
||||
// image is not contained in cached pages.
|
||||
$cron_threshold = variable_get('cron_safe_threshold', DRUPAL_CRON_DEFAULT_THRESHOLD);
|
||||
if (($cron_threshold > 0 && $form_state['input']['cron_safe_threshold'] == 0) || ($cron_threshold == 0 && $form_state['input']['cron_safe_threshold'] > 0)) {
|
||||
cache_clear_all();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form builder; Configure error reporting settings.
|
||||
*
|
||||
|
|
|
@ -41,6 +41,11 @@ define('DRUPAL_MINIMUM_PGSQL', '8.3');
|
|||
*/
|
||||
define('DRUPAL_MAXIMUM_TEMP_FILE_AGE', 21600);
|
||||
|
||||
/**
|
||||
* Default interval for automatic cron executions in seconds.
|
||||
*/
|
||||
define('DRUPAL_CRON_DEFAULT_THRESHOLD', 10800);
|
||||
|
||||
/**
|
||||
* New users will be set to the default time zone at registration.
|
||||
*/
|
||||
|
@ -196,6 +201,9 @@ function system_theme() {
|
|||
'arguments' => array('version' => NULL),
|
||||
),
|
||||
'system_compact_link' => array(),
|
||||
'system_run_cron_image' => array(
|
||||
'arguments' => array('image_path' => NULL),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -496,6 +504,12 @@ function system_menu() {
|
|||
'access callback' => TRUE,
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
$items['system/run-cron-image'] = array(
|
||||
'title' => 'Execute cron',
|
||||
'page callback' => 'system_run_cron_image',
|
||||
'access callback' => 'system_run_cron_image_access',
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
$items['admin'] = array(
|
||||
'title' => 'Administer',
|
||||
'access arguments' => array('access administration pages'),
|
||||
|
@ -2968,3 +2982,104 @@ function system_retrieve_file($url, $destination = NULL, $overwrite = TRUE) {
|
|||
|
||||
return $local;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement hook_page_alter().
|
||||
*/
|
||||
function system_page_alter(&$page) {
|
||||
// Automatic cron runs.
|
||||
// @see system_run_cron_image()
|
||||
if (system_run_cron_image_access()) {
|
||||
$page['page_bottom']['run_cron'] = array(
|
||||
// Trigger cron run via AJAX.
|
||||
'#attached_js' => array(
|
||||
'(function($){ $.get(' . drupal_to_js(url('system/run-cron-image')) . '); })(jQuery);' => array('type' => 'inline', 'scope' => 'header'),
|
||||
),
|
||||
// Trigger cron run for clients not supporting JavaScript (fall-back).
|
||||
'#markup' => theme('system_run_cron_image', 'system/run-cron-image'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; executes cron via an image callback.
|
||||
*
|
||||
* This callback runs cron in a separate HTTP request to prevent "mysterious"
|
||||
* slow-downs of regular HTTP requests. It is either invoked via an AJAX request
|
||||
* (if the client's browser supports JavaScript) or by an IMG tag directly in
|
||||
* the page output (for clients not supporting JavaScript). For the latter case,
|
||||
* we need to output a transparent 1x1 image, so the browser does not render the
|
||||
* image's alternate text or a 'missing image placeholder'. The AJAX request
|
||||
* does not process the returned output.
|
||||
*
|
||||
* @see system_page_alter()
|
||||
* @see theme_system_run_cron_image()
|
||||
* @see system_run_cron_image_access()
|
||||
*/
|
||||
function system_run_cron_image() {
|
||||
drupal_page_is_cacheable(FALSE);
|
||||
|
||||
// Output a transparent 1x1 image to the browser; required for clients not
|
||||
// supporting JavaScript.
|
||||
drupal_set_header('Content-Type', 'image/gif');
|
||||
echo "\x47\x49\x46\x38\x39\x61\x1\x0\x1\x0\x80\xff\x0\xc0\xc0\xc0\x0\x0\x0\x21\xf9\x4\x1\x0\x0\x0\x0\x2c\x0\x0\x0\x0\x1\x0\x1\x0\x0\x2\x2\x44\x1\x0\x3b";
|
||||
|
||||
// Cron threshold semaphore is used to avoid errors every time the image
|
||||
// callback is displayed when a previous cron is still running.
|
||||
$threshold_semaphore = variable_get('cron_threshold_semaphore', FALSE);
|
||||
if ($threshold_semaphore) {
|
||||
if (REQUEST_TIME - $threshold_semaphore > 3600) {
|
||||
// Either cron has been running for more than an hour or the semaphore
|
||||
// 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 the cron threshold semaphore.
|
||||
variable_del('cron_threshold_semaphore');
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 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)) {
|
||||
// Lock cron threshold semaphore.
|
||||
variable_set('cron_threshold_semaphore', REQUEST_TIME);
|
||||
drupal_cron_run();
|
||||
// Release the cron threshold semaphore.
|
||||
variable_del('cron_threshold_semaphore');
|
||||
}
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the feature to automatically run cron is enabled.
|
||||
*
|
||||
* Also used as a menu access callback for this feature.
|
||||
*
|
||||
* @return
|
||||
* TRUE if cron threshold is enabled, FALSE otherwise.
|
||||
*
|
||||
* @see system_run_cron_image()
|
||||
* @see system_page_alter()
|
||||
*/
|
||||
function system_run_cron_image_access() {
|
||||
return variable_get('cron_safe_threshold', DRUPAL_CRON_DEFAULT_THRESHOLD) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display image used to run cron automatically.
|
||||
*
|
||||
* Renders an image pointing to the automatic cron run menu callback for
|
||||
* graceful degradation when Javascript is not available. The wrapping NOSCRIPT
|
||||
* tag ensures that only browsers not supporting JavaScript render the image.
|
||||
*
|
||||
* @see system_page_alter()
|
||||
* @see system_run_cron_image()
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_system_run_cron_image($image_path) {
|
||||
return '<noscript><div id="system-cron-image">' . theme('image', $image_path, '', '', array(), FALSE) . '</div></noscript>';
|
||||
}
|
||||
|
||||
|
|
|
@ -372,6 +372,7 @@ class CronRunTestCase extends DrupalWebTestCase {
|
|||
*/
|
||||
function testCronRun() {
|
||||
global $base_url;
|
||||
|
||||
// Run cron anonymously without any cron key.
|
||||
$this->drupalGet($base_url . '/cron.php', array('external' => TRUE));
|
||||
$this->assertResponse(403);
|
||||
|
@ -390,14 +391,58 @@ class CronRunTestCase extends DrupalWebTestCase {
|
|||
$this->assertTrue(drupal_cron_run(), t('Cron ran successfully.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Follow every image paths in the previously retrieved content.
|
||||
*/
|
||||
function drupalGetAllImages() {
|
||||
foreach ($this->xpath('//img') as $image) {
|
||||
$this->drupalGet($this->getAbsoluteUrl($image['src']));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the cron image callback to run it automatically is working.
|
||||
*
|
||||
* In these tests we do not use REQUEST_TIME to track start time, because we
|
||||
* need the exact time when cron is triggered.
|
||||
*/
|
||||
function testCronThreshold() {
|
||||
// Ensure cron does not run when the cron threshold is enabled and was
|
||||
// not passed.
|
||||
$start_cron_last = time();
|
||||
variable_set('cron_last', $start_cron_last);
|
||||
variable_set('cron_safe_threshold', 10);
|
||||
$this->drupalGet('');
|
||||
// Follow every image path on the page.
|
||||
$this->drupalGetAllImages();
|
||||
$this->assertTrue($start_cron_last == variable_get('cron_last', NULL), t('Cron does not run when the cron threshold is not passed.'));
|
||||
|
||||
// Test if cron runs when the cron threshold was passed.
|
||||
$start_cron_last = time() - 15;
|
||||
variable_set('cron_last', $start_cron_last);
|
||||
$this->drupalGet('');
|
||||
// Follow every image path on the page.
|
||||
$this->drupalGetAllImages();
|
||||
$this->assertTrue(variable_get('cron_last', NULL) > $start_cron_last, t('Cron runs when the cron threshold is passed.'));
|
||||
|
||||
// Test if cron does not run when the cron threshold was is disabled.
|
||||
$start_cron_last = time() - 15;
|
||||
variable_set('cron_safe_threshold', 0);
|
||||
variable_set('cron_last', $start_cron_last);
|
||||
$this->drupalGet('');
|
||||
// Follow every image path on the page.
|
||||
$this->drupalGetAllImages();
|
||||
$this->assertTrue($start_cron_last == variable_get('cron_last', NULL), t('Cron does not run when the cron threshold is disabled.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that temporary files are removed.
|
||||
*
|
||||
* Create files for all the possible combinations of age and status. We are
|
||||
* using UPDATE statments rather than file_save() because it would set the
|
||||
* timestamp.
|
||||
*/
|
||||
function testTempFileCleanup() {
|
||||
// Create files for all the possible combinations of age and status. We're
|
||||
// using UPDATE statments rather than file_save() because it would set the
|
||||
// timestamp.
|
||||
|
||||
// Temporary file that is older than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
|
||||
$temp_old = file_save_data('');
|
||||
db_update('file')
|
||||
|
|
Loading…
Reference in New Issue