| 1 |
<?php
|
| 2 |
// $Id: dbcron.module,v 1.4 2006/04/13 15:49:31 unconed Exp $
|
| 3 |
|
| 4 |
/**
|
| 5 |
* @file
|
| 6 |
* Runs a set of user-defined SQL queries at regular intervals using cron.
|
| 7 |
*/
|
| 8 |
|
| 9 |
/**
|
| 10 |
* Implementation of hook_help().
|
| 11 |
*/
|
| 12 |
function dbcron_help($section) {
|
| 13 |
switch ($section) {
|
| 14 |
case 'admin/modules#description':
|
| 15 |
return t('Runs a set of user-defined SQL queries at regular intervals using cron.');
|
| 16 |
case 'admin/dbcron':
|
| 17 |
return t('<p>The DB cron module allows you to create and manage a set of SQL queries. Each query is run at a regular interval using the cron system, and you can specify a different interval for each query. To create a new query, click the <em>add</em> link on this page. If you have existing queries, you will see them listed on this page, and you can modify them if you wish.</p>');
|
| 18 |
case 'admin/help#dbcron':
|
| 19 |
return t('<p>The DB cron module allows you to create and manage a set of SQL queries. Each query is run at a regular interval using the cron system, and you can specify a different interval for each query. As this module allows you to directly run SQL queries on your site\'s database, it should only be accessible to advanced users who have a sound knowledge of the Drupal database schema, and who are competent in ANSI SQL. Do not give inexperienced or non-trustworthy users the \'administer cron queries\' permission under any circumstances!</p>');
|
| 20 |
}
|
| 21 |
}
|
| 22 |
|
| 23 |
/**
|
| 24 |
* Implementation of hook_perm().
|
| 25 |
*/
|
| 26 |
function dbcron_perm() {
|
| 27 |
return array('administer cron queries');
|
| 28 |
}
|
| 29 |
|
| 30 |
/**
|
| 31 |
* Implementation of hook_menu().
|
| 32 |
*/
|
| 33 |
function dbcron_menu($may_cache) {
|
| 34 |
$items = array();
|
| 35 |
|
| 36 |
if ($may_cache) {
|
| 37 |
$items[] = array(
|
| 38 |
'path' => 'admin/dbcron',
|
| 39 |
'title' => t('cron queries'),
|
| 40 |
'callback' => 'dbcron_admin',
|
| 41 |
'access' => user_access('administer cron queries'),
|
| 42 |
);
|
| 43 |
$items[] = array(
|
| 44 |
'path' => 'admin/dbcron/edit',
|
| 45 |
'title' => t('edit'),
|
| 46 |
'callback' => 'dbcron_edit',
|
| 47 |
'access' => user_access('administer cron queries'),
|
| 48 |
'type' => MENU_CALLBACK,
|
| 49 |
);
|
| 50 |
$items[] = array(
|
| 51 |
'path' => 'admin/dbcron/delete',
|
| 52 |
'title' => t('delete'),
|
| 53 |
'callback' => 'dbcron_delete_confirm',
|
| 54 |
'access' => user_access('administer cron queries'),
|
| 55 |
'type' => MENU_CALLBACK,
|
| 56 |
);
|
| 57 |
$items[] = array(
|
| 58 |
'path' => 'admin/dbcron/list',
|
| 59 |
'title' => t('list'),
|
| 60 |
'type' => MENU_DEFAULT_LOCAL_TASK,
|
| 61 |
'weight' => -10,
|
| 62 |
);
|
| 63 |
$items[] = array(
|
| 64 |
'path' => 'admin/dbcron/add',
|
| 65 |
'title' => t('add'),
|
| 66 |
'callback' => 'dbcron_edit',
|
| 67 |
'access' => user_access('administer cron queries'),
|
| 68 |
'type' => MENU_LOCAL_TASK,
|
| 69 |
);
|
| 70 |
}
|
| 71 |
|
| 72 |
return $items;
|
| 73 |
}
|
| 74 |
|
| 75 |
/**
|
| 76 |
* Implementation of hook_cron().
|
| 77 |
*
|
| 78 |
* Runs all cron queries that are due to run when this hook is fired. If any
|
| 79 |
* queries have multiple parts, the multiple parts are broken up and run
|
| 80 |
* separately. Each query has its execution speed timed, and this speed (along
|
| 81 |
* with the current time) is saved to the database after execution.
|
| 82 |
*/
|
| 83 |
function dbcron_cron() {
|
| 84 |
static $reset;
|
| 85 |
|
| 86 |
$leeway = (int)variable_get('dbcron_interval_leeway', 10);
|
| 87 |
$result = db_query('SELECT * FROM {dbcron} WHERE (%d - last_run >= (run_interval - %d)) AND run_interval > 0', time(), $leeway);
|
| 88 |
|
| 89 |
if (!isset($reset)) {
|
| 90 |
$reset = FALSE;
|
| 91 |
}
|
| 92 |
|
| 93 |
while ($dq = db_fetch_object($result)) {
|
| 94 |
$queries = preg_split('/;(\n|\r)/', $dq->body);
|
| 95 |
|
| 96 |
timer_start('dbcron');
|
| 97 |
foreach ($queries as $query) {
|
| 98 |
$query = preg_replace('/\n|\r/', '', $query);
|
| 99 |
if (!empty($query)) {
|
| 100 |
db_query($query);
|
| 101 |
|
| 102 |
if (!$reset && variable_get('dbcron_search_reset', 0)) {
|
| 103 |
search_wipe();
|
| 104 |
$reset = TRUE;
|
| 105 |
}
|
| 106 |
}
|
| 107 |
}
|
| 108 |
$timer = timer_stop('dbcron');
|
| 109 |
$speed = (int)$timer['time'];
|
| 110 |
|
| 111 |
db_query('UPDATE {dbcron} SET last_run = %d, exec_speed = %d WHERE dqid = %d', time(), $speed, $dq->dqid);
|
| 112 |
}
|
| 113 |
}
|
| 114 |
|
| 115 |
/**
|
| 116 |
* Implementation of hook_settings().
|
| 117 |
*/
|
| 118 |
function dbcron_settings() {
|
| 119 |
$form = array();
|
| 120 |
$options = array(0 => t('None'), 5 => format_interval(5), 10 => format_interval(10), 15 => format_interval(15), 20 => format_interval(20), 30 => format_interval(30), 60 => format_interval(60));
|
| 121 |
|
| 122 |
$form['dbcron_interval_leeway'] = array(
|
| 123 |
'#type' => 'select',
|
| 124 |
'#title' => t('Leeway in cron run intervals'),
|
| 125 |
'#default_value' => variable_get('dbcron_interval_leeway', 10),
|
| 126 |
'#options' => $options,
|
| 127 |
'#description' => t('The amount of leeway (in seconds) to be given to each cron query when determining (from its running interval) if it\'s time for the query to run.')
|
| 128 |
);
|
| 129 |
|
| 130 |
$options = array(
|
| 131 |
1 => t('Enabled'),
|
| 132 |
0 => t('Disabled'),
|
| 133 |
);
|
| 134 |
$form['dbcron_search_reset'] = array(
|
| 135 |
'#type' => 'radios',
|
| 136 |
'#title' => t('Reset search index on cron run'),
|
| 137 |
'#default_value' => variable_get('dbcron_search_reset', 0),
|
| 138 |
'#options' => $options,
|
| 139 |
'#description' => t('If enabled, the site\'s search index will be reset every time a cron query is executed. Useful for cron queries that clear the site\'s content.')
|
| 140 |
);
|
| 141 |
|
| 142 |
return $form;
|
| 143 |
}
|
| 144 |
|
| 145 |
/**
|
| 146 |
* Menu callback; return a listing of all defined cron queries.
|
| 147 |
*/
|
| 148 |
function dbcron_admin() {
|
| 149 |
$sql = 'SELECT dqid, title, run_interval, last_run, exec_speed FROM {dbcron}';
|
| 150 |
$header = array(
|
| 151 |
array('data' => t('Title'), 'field' => 'title', 'sort' => 'asc'),
|
| 152 |
array('data' => t('Interval'), 'field' => 'run_interval'),
|
| 153 |
array('data' => t('Last run'), 'field' => 'last_run'),
|
| 154 |
array('data' => t('Last exec speed'), 'field' => 'exec_speed'),
|
| 155 |
array('data' => t('Operations'), 'colspan' => '2')
|
| 156 |
);
|
| 157 |
$sql .= tablesort_sql($header);
|
| 158 |
$result = pager_query($sql, 50);
|
| 159 |
|
| 160 |
$destination = drupal_get_destination();
|
| 161 |
while ($data = db_fetch_object($result)) {
|
| 162 |
$rows[] = array($data->title, ($data->run_interval ? format_interval($data->run_interval) : t('Never')), ($data->last_run ? format_date($data->last_run) : t('N/A')), ($data->exec_speed ? $data->exec_speed . 'ms' : t('N/A')), l(t('edit'), "admin/dbcron/edit/$data->dqid", array(), $destination), l(t('delete'), "admin/dbcron/delete/$data->dqid", array(), $destination));
|
| 163 |
}
|
| 164 |
|
| 165 |
if (!$rows) {
|
| 166 |
$rows[] = array(array('data' => t('No cron queries available.'), 'colspan' => '6'));
|
| 167 |
}
|
| 168 |
|
| 169 |
$output = theme('table', $header, $rows);
|
| 170 |
$output .= theme('pager', NULL, 50, 0);
|
| 171 |
return $output;
|
| 172 |
}
|
| 173 |
|
| 174 |
/**
|
| 175 |
* Menu callback; handles pages for creating and editing cron queries.
|
| 176 |
*/
|
| 177 |
function dbcron_edit($dqid = 0) {
|
| 178 |
if ($dqid) {
|
| 179 |
$dq = dbcron_load($dqid);
|
| 180 |
drupal_set_title($dq->title);
|
| 181 |
$output = dbcron_form(dbcron_load($dqid));
|
| 182 |
}
|
| 183 |
else {
|
| 184 |
$output = dbcron_form();
|
| 185 |
}
|
| 186 |
|
| 187 |
return $output;
|
| 188 |
}
|
| 189 |
|
| 190 |
/**
|
| 191 |
* Menu callback; confirms deleting a cron query
|
| 192 |
**/
|
| 193 |
function dbcron_delete_confirm($dqid) {
|
| 194 |
$output = '';
|
| 195 |
$dq = dbcron_load($dqid);
|
| 196 |
if (user_access('administer cron queries')) {
|
| 197 |
$form['dqid'] = array(
|
| 198 |
'#type' => 'value',
|
| 199 |
'#value' => $dqid,
|
| 200 |
);
|
| 201 |
$output = confirm_form('dbcron_delete_confirm', $form,
|
| 202 |
t('Are you sure you want to delete cron query %title?', array('%title' => theme('placeholder', $dq->title))),
|
| 203 |
$_GET['destination'] ? $_GET['destination'] : 'admin/dbcron', t('This action cannot be undone.'),
|
| 204 |
t('Delete'), t('Cancel') );
|
| 205 |
}
|
| 206 |
|
| 207 |
return $output;
|
| 208 |
}
|
| 209 |
|
| 210 |
/**
|
| 211 |
* Execute cron query deletion
|
| 212 |
**/
|
| 213 |
function dbcron_delete_confirm_submit($form_id, $form_values) {
|
| 214 |
if ($form_values['confirm']) {
|
| 215 |
dbcron_delete($form_values['dqid']);
|
| 216 |
drupal_goto('admin/dbcron');
|
| 217 |
}
|
| 218 |
}
|
| 219 |
|
| 220 |
/**
|
| 221 |
* Post-confirmation; delete a cron query.
|
| 222 |
*/
|
| 223 |
function dbcron_delete($dqid = 0) {
|
| 224 |
db_query('DELETE FROM {dbcron} WHERE dqid = %d', $dqid);
|
| 225 |
drupal_set_message(t('The cron query has been deleted.'));
|
| 226 |
}
|
| 227 |
|
| 228 |
/**
|
| 229 |
* Save a new or existing cron query to the database.
|
| 230 |
*/
|
| 231 |
function dbcron_save_query($dq) {
|
| 232 |
$period = _dbcron_get_run_intervals();
|
| 233 |
$run_interval = $period[$dq->run_interval];
|
| 234 |
|
| 235 |
if ($dq->dqid) {
|
| 236 |
db_query("UPDATE {dbcron} SET title = '%s', body = '%s', run_interval = %d, last_run = 0 WHERE dqid = %d", $dq->title, $dq->body, $run_interval, $dq->dqid);
|
| 237 |
}
|
| 238 |
else {
|
| 239 |
db_query("INSERT INTO {dbcron} (title, body, run_interval) VALUES ('%s', '%s', %d)", $dq->title, $dq->body, $run_interval);
|
| 240 |
}
|
| 241 |
}
|
| 242 |
|
| 243 |
/**
|
| 244 |
* Return a form for editing or creating an individual cron query.
|
| 245 |
*/
|
| 246 |
function dbcron_form($dq = '') {
|
| 247 |
if (is_array($dq)) {
|
| 248 |
$dq = (object)$dq;
|
| 249 |
}
|
| 250 |
|
| 251 |
$form['title'] = array(
|
| 252 |
'#type' => 'textfield',
|
| 253 |
'#title' => t('Title'),
|
| 254 |
'#default_value' => $dq->title,
|
| 255 |
'#maxlength' => 128,
|
| 256 |
'#description' => t('Specify a unique title for this cron query. The title is simply used to help you identify the query on the overview page.'),
|
| 257 |
'#required' => TRUE,
|
| 258 |
);
|
| 259 |
$form['body'] = array(
|
| 260 |
'#type' => 'textarea',
|
| 261 |
'#title' => t('Query'),
|
| 262 |
'#default_value' => $dq->body,
|
| 263 |
'#description' => t('Enter the actual query here. Ensure that you have tested the query thoroughly, as it will be run regularly, and will result in numerous errors if it has problems. If you wish to run multiple queries, separate each query with a semicolon followed by a new line, and then each query will be run separately. Ensure that all table names are wrapped in curly brackets (e.g. <em>{users}</em>, not <em>users</em>), especially if your site uses database prefixing.'),
|
| 264 |
'#rows' => 20,
|
| 265 |
'#required' => TRUE,
|
| 266 |
);
|
| 267 |
|
| 268 |
$period = _dbcron_get_run_intervals('form');
|
| 269 |
$period_vals = array_flip(_dbcron_get_run_intervals());
|
| 270 |
$form['run_interval'] = array(
|
| 271 |
'#type' => 'select',
|
| 272 |
'#title' => t('Running interval'),
|
| 273 |
'#default_value' => $period_vals[$dq->run_interval],
|
| 274 |
'#options' => $period,
|
| 275 |
'#description' => t('Select the interval at which you would like this query to run. Note that if your site\'s cron runs less often than the interval that you specify, then the query will only run as often as your cron does. Set to \'never\' to disable this query, if you wish to leave it in the system and possibly re-enable it at a later date. Requires crontab.'),
|
| 276 |
);
|
| 277 |
|
| 278 |
if ($dq->dqid) {
|
| 279 |
$form['dqid'] = array(
|
| 280 |
'#type' => 'hidden',
|
| 281 |
'#value' => $dq->dqid,
|
| 282 |
);
|
| 283 |
$form['submit'] = array(
|
| 284 |
'#type' => 'submit',
|
| 285 |
'#value' => t('Update query'),
|
| 286 |
);
|
| 287 |
}
|
| 288 |
else {
|
| 289 |
$form['submit'] = array(
|
| 290 |
'#type' => 'submit',
|
| 291 |
'#value' => t('Create new query'),
|
| 292 |
);
|
| 293 |
}
|
| 294 |
|
| 295 |
return drupal_get_form('dbcron_form', $form);
|
| 296 |
}
|
| 297 |
|
| 298 |
/**
|
| 299 |
* Verify that a cron query is valid.
|
| 300 |
*/
|
| 301 |
function dbcron_form_validate($form_id, $edit) {
|
| 302 |
$dq = _dbcron_edit_into_object($edit);
|
| 303 |
|
| 304 |
if (db_result(db_query("SELECT COUNT(run_interval) FROM {dbcron} WHERE dqid != %d AND title = '%s'", $dq->dqid, $dq->title))) {
|
| 305 |
form_set_error('title', t('The title %title is already in use.', array('%title' => theme('placeholder', $dq->title))));
|
| 306 |
}
|
| 307 |
}
|
| 308 |
|
| 309 |
/**
|
| 310 |
* Save a cron query to the database.
|
| 311 |
*/
|
| 312 |
function dbcron_form_submit($form_id, $edit) {
|
| 313 |
$dq = _dbcron_edit_into_object($edit);
|
| 314 |
|
| 315 |
dbcron_save_query($dq);
|
| 316 |
|
| 317 |
drupal_set_message(t('The cron query has been saved.'));
|
| 318 |
drupal_goto('admin/dbcron');
|
| 319 |
}
|
| 320 |
|
| 321 |
/**
|
| 322 |
* Fetch a specific cron query from the database.
|
| 323 |
*/
|
| 324 |
function dbcron_load($dqid) {
|
| 325 |
return db_fetch_object(db_query('SELECT * FROM {dbcron} WHERE dqid = %d', $dqid));
|
| 326 |
}
|
| 327 |
|
| 328 |
/**
|
| 329 |
* Transform an array of edit values for a cron query into an object.
|
| 330 |
*
|
| 331 |
* @param $edit
|
| 332 |
* The array of values, as returned by form interfaces.
|
| 333 |
*
|
| 334 |
* @return
|
| 335 |
* Equivalent values in an object.
|
| 336 |
*/
|
| 337 |
function _dbcron_edit_into_object($edit) {
|
| 338 |
$dq = new stdClass();
|
| 339 |
$dq->title = $edit['title'];
|
| 340 |
$dq->body = $edit['body'];
|
| 341 |
$dq->run_interval = $edit['run_interval'];
|
| 342 |
$dq->dqid = $edit['dqid'];
|
| 343 |
|
| 344 |
return $dq;
|
| 345 |
}
|
| 346 |
|
| 347 |
/**
|
| 348 |
* Get an associative array of all the running intervals.
|
| 349 |
*/
|
| 350 |
function _dbcron_get_run_intervals($type = NULL) {
|
| 351 |
$period = array(300, 600, 900, 1800, 2700, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 432000, 604800);
|
| 352 |
|
| 353 |
if ($type == 'form') {
|
| 354 |
$period = drupal_map_assoc($period, 'format_interval');
|
| 355 |
return array_merge(array(0 => t('Never')), $period);
|
| 356 |
}
|
| 357 |
else {
|
| 358 |
return array_merge(array(0 => 0), $period);
|
| 359 |
}
|
| 360 |
}
|