18 require_once SQ_INCLUDE_PATH.
'/asset.inc';
20 define(
'SQ_CRON_JOB_ERROR', 1);
21 define(
'SQ_CRON_JOB_COMPLETED', 2);
22 define(
'SQ_CRON_JOB_NOT_COMPLETE', 4);
23 define(
'SQ_CRON_JOB_REMOVE', 8);
24 define(
'SQ_CRON_JOB_RETRY', 16);
51 parent::__construct($assetid);
68 if (!parent::_preCreateCheck($link))
return FALSE;
70 if (!$this->
attr(
'type') || !$this->
attr(
'when')) {
71 trigger_localised_error(
'CRON0002', E_USER_ERROR);
90 SQ_LINK_TYPE_1 => Array(),
91 SQ_LINK_TYPE_2 => Array(),
92 SQ_LINK_TYPE_3 => Array(),
93 SQ_LINK_NOTICE => Array(
'user' => Array(
'card' => 1)),
122 public function morph($new_type_code)
124 trigger_localised_error(
'CRON0024', E_USER_WARNING, $GLOBALS[
'SQ_SYSTEM']->am->getTypeInfo($this->type(),
'name'));
143 trigger_localised_error(
'CRON0041', E_USER_WARNING, $GLOBALS[
'SQ_SYSTEM']->am->getTypeInfo($this->type(),
'name'));
176 if ($name ==
'when') {
178 $value = (string) $value;
179 if ($this->
attr(
'type') !=
'one_off' && substr($value, 0, 2) ==
'OO') {
180 trigger_localised_error(
'CRON0009', E_USER_NOTICE);
183 if ($this->
attr(
'type') ==
'one_off' && substr($value, 0, 2) !=
'OO') {
184 trigger_localised_error(
'CRON0043', E_USER_NOTICE);
198 if ($this->
attr(
'type') !=
'repeating' && substr($value, 0, 2) ==
'TI') {
199 trigger_localised_error(
'CRON0045', E_USER_NOTICE);
206 if ($this->
attr(
'type') ==
'one_off') {
210 $cron_mgr = $GLOBALS[
'SQ_SYSTEM']->am->getSystemAsset(
'cron_manager');
211 if (is_null($cron_mgr))
return FALSE;
214 if (strpos($when_str,
'!')) {
217 $duration = substr($value, strpos($value,
'!') + 2);
218 $period = substr($value, strpos($value,
'!') + 1, 1);
222 $then = $now + $duration*60;
225 $then = $now + $duration*3600;
228 $then = $now + $duration*86400;
231 $then = $now + $duration*604800;
234 $then = strtotime(
'+'.$duration.
' months');
237 $then = strtotime(
'+'.$duration.
' years');
243 if ($now == $then && $cron_mgr->attr(
'last_run') == strtotime(date(
'Y-m-d H:i', $now))) {
245 $then += $cron_mgr->attr(
'refresh_time');
250 foreach (Array(
'year' =>
'Y',
'month' =>
'm',
'day' =>
'd',
'hour' =>
'H',
'minute' =>
'i') as $field => $format_string) {
251 $fields[$field] = date($format_string, $then);
254 $when_str .= $fields[$field].
'-';
257 $when_str .= $fields[$field].
'-';
260 $when_str .= $fields[$field].
' ';
263 $when_str .= $fields[$field].
':';
266 $minutes = date($format_string, $then);
267 $interval = $cron_mgr->attr(
'refresh_time') / 60;
268 while ($minutes % $interval != 0) {
276 $fields[$field] = $minutes;
277 $when_str .= $fields[$field];
288 $when_ts = mktime($when[
'hour'], $when[
'minute'], 0, $when[
'month'], $when[
'day'], $when[
'year']);
289 if ($this->
attr(
'type') ==
'repeating' && substr($value, 0, 2) ==
'TI') {
290 $when_ts = $when[
'duration'];
293 if ($when_ts < (
int) $cron_mgr->attr(
'last_run')) {
294 require_once SQ_FUDGE_PATH.
'/general/datetime.inc';
295 trigger_localised_error(
'CRON0042', E_USER_WARNING, $GLOBALS[
'SQ_SYSTEM']->am->getTypeInfo($this->type(),
'name'), easy_datetime((
int) $cron_mgr->attr(
'last_run')));
304 $value =
'OO='.date(
'Y-m-d H:i', $when_ts);
309 if (0 === strpos($value,
'OO=')) {
310 $value = substr($value, 0, 19);
316 return parent::setAttrValue($name, $value);
332 if ($this->
attr(
'read_only'))
return FALSE;
335 if (!is_null($user) && $GLOBALS[
'SQ_SYSTEM']->currentUser($user)) {
340 if (!is_null($user) && $GLOBALS[
'SQ_SYSTEM']->userRoot($user) && !$GLOBALS[
'SQ_SYSTEM']->userRoot()) {
345 if ($GLOBALS[
'SQ_SYSTEM']->userRoot() || $GLOBALS[
'SQ_SYSTEM']->userSystemAdmin()) {
363 $userid = $this->
attr(
'running_as');
364 if (!$userid)
return $null;
365 $user = $GLOBALS[
'SQ_SYSTEM']->am->getAsset($userid,
'', TRUE);
366 if (is_null($user))
return $null;
384 case 'ET' :
return translate(
'every_time');
385 case 'OO' :
return translate(
'one_off');
386 case 'HR' :
return translate(
'hourly');
387 case 'DL' :
return translate(
'daily');
388 case 'WK' :
return translate(
'weekly');
389 case 'MT' :
return translate(
'monthly');
390 case 'YR' :
return translate(
'yearly');
391 case 'TI' :
return translate(
'repeating');
393 trigger_localised_error(
'CRON0005', $type, E_USER_NOTICE);
411 switch ((
int) $wday) {
412 case 0 :
return translate(
'sunday');
413 case 1 :
return translate(
'monday');
414 case 2 :
return translate(
'tuesday');
415 case 3 :
return translate(
'wednesday');
416 case 4 :
return translate(
'thursday');
417 case 5 :
return translate(
'friday');
418 case 6 :
return translate(
'saturday');
420 trigger_localised_error(
'CRON0034', E_USER_NOTICE, $wday);
438 $ret_val = ($inc_type) ?
Cron_Job::whenTypeName($when_arr[
'type']).
' '.strtolower(translate(
'at')).
' ' :
'';
439 if ($when_arr[
'type'] !=
'TI') {
441 if (intval($when_arr[
'minute']) < 10 && strlen($when_arr[
'minute']) == 1) {
442 $when_arr[
'minute'] =
'0'.$when_arr[
'minute'];
445 $when_arr[
'hour'] = str_replace(
':',
'', $when_arr[
'hour']);
446 switch ($when_arr[
'type']) {
448 $ret_val .= translate(
'cron_run_other', $when_arr[
'hour'].
':'.$when_arr[
'minute'], ltrim($when_arr[
'day'],
'0').
' '.date(
'M', mktime(0, 0, 0, (
int) $when_arr[
'month'], 1, 2000)).
', '.$when_arr[
'year']);
452 $ret_val .= translate(
'cron_run_other', $when_arr[
'hour'].
':'.$when_arr[
'minute'], $when_arr[
'day'].
' '.date(
'M', mktime(0, 0, 0, (
int) $when_arr[
'month'], 1, 2000)));
456 $ret_val .= translate(
'cron_run_month' ,$when_arr[
'hour'].
':'.$when_arr[
'minute'], date(
'jS', mktime(0, 0, 0, 1, (
int) $when_arr[
'day'], 2000)));
460 $ret_val .= translate(
'cron_run_other', $when_arr[
'hour'].
':'.$when_arr[
'minute'],
Cron_Job::whenWeekDayName($when_arr[
'day']).
'\'s
');
464 $ret_val .= $when_arr['hour
'].':
'.$when_arr['minute
'];
468 $ret_val .= translate('cron_run_hourly
', $when_arr['minute
']);
472 $ret_val = translate('cron_run_always
');
476 if ($when_arr['minute
'] != '') {
477 $interval_unit = 'minute
';
478 } else if ($when_arr['hour
'] != '') {
479 $interval_unit = 'hour
';
480 } else if ($when_arr['day
'] != '') {
481 $interval_unit = 'day
';
483 // format date, eg. 02:00 on 21 Jan, 2006
484 $next_run = date('H:i \o\n j M, Y
',$when_arr['duration
']);
485 $ret_val = translate('cron_run_repeat
',$next_run, $when_arr[$interval_unit], translate($interval_unit.'s
'));
492 }//end readableWhen()
504 public static function getWhenArr($when_str)
506 $when_arr = Array('type' => '', 'year
' => '', 'month
' => '', 'day
' => '', 'hour
' => '', 'minute
' => '', 'duration
' => '', 'period
' => '');
509 if (strpos($when_str, '!
')) {
510 $when_arr['type'] = substr($when_str, 0, 2);
511 $when_arr['duration
'] = substr($when_str, strpos($when_str, '!
') + 2);
512 $when_arr['period
'] = substr($when_str, strpos($when_str, '!
') + 1, 1);
514 $when_arr['type'] = substr($when_str, 0, 2);
516 switch ($when_arr['type']) {
524 $when = substr($when_str, $offset);
525 $val = explode(',
', $when);
528 $interval_unit = (int)$val[2];
529 // store the next run time and interval info of this repeating job
530 $when_arr['duration
'] = $next_run;
531 $when_arr[$time_unit[$interval_unit]] = $interval;
534 $when_arr['year
'] = substr($when_str, $offset, 4);
536 // deliberately fall through
538 $when_arr['month
'] = substr($when_str, $offset, 2);
540 // deliberately fall through
543 // because the weekly type uses only one char, we do this
544 if ($when_arr['type'] == 'WK
') {
545 $when_arr['day
'] = substr($when_str, $offset, 1);
547 // else the monthly, yearly and one off use 2 chars
549 $when_arr['day
'] = substr($when_str, $offset, 2);
552 // deliberately fall through
554 $when_arr['hour
'] = substr($when_str, $offset, 2);
556 // deliberately fall through
558 $when_arr['minute
'] = substr($when_str, $offset, 2);
560 // deliberately fall through
562 $when_arr['period
'] = substr($when_str, $offset, 1);
564 // deliberately fall through
566 $when_arr['duration
'] = substr($when_str, $offset);
589 public function run()
591 $this->_tmp['running_errors
'] = '';
597 // First let's see
if we can find someone to
run as
599 if (!is_null($user)) {
602 if ($GLOBALS[
'SQ_SYSTEM']->am->acquireLock($this->id,
'all')) {
604 if ($GLOBALS[
'SQ_SYSTEM']->setCurrentUser($user)) {
607 $when_str = $this->
attr(
'when');
608 $start_time_ok = time() > $this->
getNextRun($when_str);
609 if (($this->
attr(
'type') ==
'repeating' && substr($when_str, 0, 2) ==
'TI') && !$start_time_ok) {
611 $exec_result = SQ_CRON_JOB_NOT_COMPLETE;
613 $exec_result = $this->
_exec($exec_msg);
617 $GLOBALS[
'SQ_SYSTEM']->restoreCurrentUser();
621 trigger_localised_error(
'CRON0027', E_USER_WARNING, $this->name, $this->
id, $user->name, $user->id);
623 $exec_result = SQ_CRON_JOB_ERROR | SQ_CRON_JOB_RETRY;
626 $GLOBALS[
'SQ_SYSTEM']->am->releaseLock($this->
id,
'all');
630 trigger_localised_error(
'CRON0025', E_USER_WARNING, $this->name, $this->
id);
632 $exec_result = SQ_CRON_JOB_ERROR | SQ_CRON_JOB_RETRY;
638 trigger_localised_error(
'CRON0026', E_USER_WARNING, $this->name, $this->
id);
640 $exec_result = SQ_CRON_JOB_ERROR | SQ_CRON_JOB_REMOVE;
644 restore_error_handler();
646 $ms = $GLOBALS[
'SQ_SYSTEM']->getMessagingService();
648 $msg = $ms->newMessage();
651 'cron_type_code' => $GLOBALS[
'SQ_SYSTEM']->am->getTypeInfo($this->type(),
'name'),
652 'cron_job_name' => $this->name,
659 if (is_null($user)) {
660 $root_user = $GLOBALS[
'SQ_SYSTEM']->am->getSystemAsset(
'root_user');
661 $sys_admins = $GLOBALS[
'SQ_SYSTEM']->am->getSystemAsset(
'system_user_group');
662 $msg->to = Array($root_user->id, $sys_admins->id);
664 $msg->to = Array($user->id);
668 if ($exec_result & SQ_CRON_JOB_ERROR) {
669 $msg->type =
'cron.job.fail';
672 }
else if ($exec_result & SQ_CRON_JOB_COMPLETED) {
673 $msg->type =
'cron.job.success';
676 if ($this->
attr(
'type') ==
'one_off' && !($exec_result & SQ_CRON_JOB_REMOVE)) {
677 if ($exec_result & SQ_CRON_JOB_RETRY) {
679 $exec_result &= ~SQ_CRON_JOB_RETRY;
681 $exec_result |= SQ_CRON_JOB_REMOVE;
685 if ($this->
attr(
'type') ==
'repeating') {
686 $when_str = $this->
attr(
'when');
687 $type = substr($when_str, 0, 2);
692 }
else if ($exec_result & SQ_CRON_JOB_NOT_COMPLETE) {
693 $msg->type =
'cron.job.success.incomplete';
697 require_once SQ_INCLUDE_PATH.
'/general_occasional.inc';
698 $msg_reps[
'result'] = $exec_result;
699 $msg_reps[
'result_bits'] = implode(
', ', get_bit_names(
'SQ_CRON_JOB_', $exec_result, TRUE));
700 $msg->type =
'cron.job.error';
702 $root_user = $GLOBALS[
'SQ_SYSTEM']->am->getSystemAsset(
'root_user');
703 $sys_admins = $GLOBALS[
'SQ_SYSTEM']->am->getSystemAsset(
'system_user_group');
705 $msg->to[] = $root_user->id;
706 $msg->to[] = $sys_admins->id;
710 $msg_reps[
'exec_msg'] = $exec_msg;
712 if (!empty($this->_tmp[
'running_errors'])) {
713 $msg_reps[
'errors'] = $this->_tmp[
'running_errors'];
715 $msg_reps[
'errors'] =
'Nil';
718 $msg->replacements = $msg_reps;
719 $ms->enqueueMessage($msg);
741 $terminate = ((E_USER_ERROR | E_ERROR) & $err_no);
745 if ((error_reporting() & $err_no) || $terminate) {
748 $err_file = hide_system_root($err_file);
749 $err_msg = hide_system_root($err_msg);
751 $err_name = get_error_name($err_no);
753 $err_msg = strip_tags(preg_replace(Array(
'/<br\\/?>/i',
'/<p[^>]*>/i'), Array(
"\n",
"\n\n"), $err_msg));
756 if (ini_get(
'log_errors')) {
757 $cron_mgr = $GLOBALS[
'SQ_SYSTEM']->am->getSystemAsset(
'cron_manager');
758 log_error($err_msg, $err_no, $err_file, $err_line, $cron_mgr->error_log_file_name);
762 $lines = explode(
"\n", $err_msg);
763 $len = 7 + strlen($err_file);
764 $len = max($len, 7 + strlen($err_line));
765 foreach ($lines as $line) {
766 $len = max($len, strlen($line));
769 $str =
'+'.str_repeat(
'-', $len).
"+\n".
770 '| '.$err_name.str_repeat(
' ', $len - 2 - strlen($err_name)).
" |\n".
771 '|'.str_repeat(
'-', $len).
"|\n".
772 '| File : '.$err_file.str_repeat(
' ', $len - 9 - strlen($err_file)).
" |\n".
773 '| Line : '.$err_line.str_repeat(
' ', $len - 9 - strlen($err_line)).
" |\n".
774 '|'.str_repeat(
'-', $len).
"|\n";
775 foreach ($lines as $line) {
776 $str .=
'| '.$line.str_repeat(
' ', $len - 2 - strlen($line)).
" |\n";
779 $str .=
'+'.str_repeat(
'-', $len).
"+\n";
781 if (!isset($this->_tmp[
'running_errors'])) {
782 $this->_tmp[
'running_errors'] =
'';
784 $this->_tmp[
'running_errors'] .= $str;
789 if ($terminate && empty($this->_tmp[
'terminate_error'])) {
790 $this->_tmp[
'terminate_error'] = TRUE;
792 $cm = $GLOBALS[
'SQ_SYSTEM']->am->getSystemAsset(
'cron_manager');
793 $cm->removeJob($this);
796 $root_user = $GLOBALS[
'SQ_SYSTEM']->am->getSystemAsset(
'root_user');
797 $sys_admins = $GLOBALS[
'SQ_SYSTEM']->am->getSystemAsset(
'system_user_group');
799 $ms = $GLOBALS[
'SQ_SYSTEM']->getMessagingService();
800 $msg = $ms->newMessage();
803 'cron_type_code' => $GLOBALS[
'SQ_SYSTEM']->am->getTypeInfo($this->type(),
'name'),
804 'asset_name' => $this->name,
805 'assetid' => $this->
id,
806 'errors' => $this->_tmp[
'running_errors'],
810 $msg->to = Array(0, $user->id, $root_user->id, $sys_admins->id);
812 $msg->type =
'cron.job.term';
813 $msg->replacements = $msg_reps;
840 trigger_localised_error(
'CRON0028', E_USER_WARNING, $this->name, $this->
id);
842 return SQ_CRON_JOB_ERROR | SQ_CRON_JOB_REMOVE;
862 if ($when[
'type'] ==
'OO') {
863 return mktime($when[
'hour'], $when[
'minute'], 0, $when[
'month'], $when[
'day'], $when[
'year']);
866 if (is_null($start_ts)) $start_ts = time();
867 $start = getdate($start_ts);
870 if ($when[
'type'] ==
'ET')
return $start_ts + 1;
873 if ($when[
'type'] ==
'TI')
return $when[
'duration'];
875 $add = Array(
'minutes' => 0,
'hours' => 0,
'days' => 0);
877 $hour_loops = ($when[
'minute'] < $start[
'minutes']);
878 $day_loops = ($when[
'hour'] < $start[
'hours'] || ($when[
'hour'] == $start[
'hours'] && $hour_loops));
879 $week_loops = ($when[
'day'] < $start[
'wday'] || ($when[
'day'] == $start[
'wday'] && $day_loops));
880 $month_loops = ($when[
'day'] < $start[
'mday'] || ($when[
'day'] == $start[
'mday'] && $day_loops));
881 $year_loops = ($when[
'month'] < $start[
'mon'] || ($when[
'month'] == $start[
'mon'] && $month_loops));
883 switch ($when[
'type']) {
888 $add[
'minutes'] += (60 - $start[
'minutes']) + $when[
'minute'];
890 $add[
'minutes'] += $when[
'minute'] - $start[
'minutes'];
899 $add[
'hours'] += (24 - $start[
'hours']) + $when[
'hour'];
901 $add[
'hours'] += $when[
'hour'] - $start[
'hours'];
904 $add[
'minutes'] += $when[
'minute'] - $start[
'minutes'];
912 $add[
'days'] += (7 - $start[
'wday']) + $when[
'day'];
914 $add[
'days'] += $when[
'day'] - $start[
'wday'];
917 $add[
'hours'] += $when[
'hour'] - $start[
'hours'];
918 $add[
'minutes'] += $when[
'minute'] - $start[
'minutes'];
926 require_once SQ_FUDGE_PATH.
'/general/datetime.inc';
927 $add[
'days'] += (days_in_month($start[
'mon'], $start[
'year']) - $start[
'mday']) + $when[
'day'];
929 $add[
'days'] += $when[
'day'] - $start[
'mday'];
932 $add[
'hours'] += $when[
'hour'] - $start[
'hours'];
933 $add[
'minutes'] += $when[
'minute'] - $start[
'minutes'];
940 require_once SQ_FUDGE_PATH.
'/general/datetime.inc';
946 for ($i = $start[
'mon']; $i <= 12; $i++) {
947 $add[
'days'] += days_in_month($i, $start[
'year']);
950 for ($i = 1; $i < $when[
'month']; $i++) {
951 $add[
'days'] += days_in_month($i, $start[
'year'] + 1);
957 for ($i = $start[
'mon']; $i < $when[
'month']; $i++) {
958 $add[
'days'] += days_in_month($i, $start[
'year']);
963 $add[
'days'] += $when[
'day'] - $start[
'mday'];
964 $add[
'hours'] += $when[
'hour'] - $start[
'hours'];
965 $add[
'minutes'] += $when[
'minute'] - $start[
'minutes'];
972 $add[
'hours'] += $add[
'days'] * 24;
973 $add[
'minutes'] += $add[
'hours'] * 60;
975 return $start_ts + ($add[
'minutes'] * 60);
990 if ($this->
attr(
'type') ==
'repeating') {
991 $when_str = $this->
attr(
'when');
992 $type = substr($when_str, 0, 2);
994 $when = substr($when_str, 3);
995 $val = explode(
',', $when);
998 $interval_unit = $val[2];
999 $next_run = $old_run + ($interval * $interval_unit);
1001 while ($next_run < time()) {
1005 $next_run = $old_run + ($interval * $interval_unit);
1006 $old_run = $next_run;
1009 if ($next_run < time()) {
1012 $root_user = $GLOBALS[
'SQ_SYSTEM']->am->getSystemAsset(
'root_user');
1013 $sys_admins = $GLOBALS[
'SQ_SYSTEM']->am->getSystemAsset(
'system_user_group');
1015 $ms = $GLOBALS[
'SQ_SYSTEM']->getMessagingService();
1017 $msg = $ms->newMessage();
1019 'cron_type_code' => $GLOBALS[
'SQ_SYSTEM']->am->getTypeInfo($this->type(),
'name'),
1020 'asset_name' => $this->name,
1021 'assetid' => $this->
id,
1022 'run_time' => easy_datetime($next_run),
1024 $msg->to = Array($root_user->id, $sys_admins->id);
1026 $msg->type =
'cron.job.startfail';
1027 $msg->replacements = $msg_reps;
1029 $ms->enqueueMessage($msg);
1034 $GLOBALS[
'SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED + SQ_SECURITY_LINK_INTEGRITY);
1035 $GLOBALS[
'SQ_SYSTEM']->doTransaction(
'BEGIN');
1036 if ($GLOBALS[
'SQ_SYSTEM']->am->acquireLock($this->id,
'links')) {
1037 $this->
setAttrValue(
'when',
"TI=$next_run,$interval,$interval_unit");
1039 $GLOBALS[
'SQ_SYSTEM']->doTransaction(
'COMMIT');
1040 $GLOBALS[
'SQ_SYSTEM']->am->releaseLock($this->
id,
'links');
1042 $GLOBALS[
'SQ_SYSTEM']->restoreRunLevel();
1058 $am = $GLOBALS[
'SQ_SYSTEM']->am;
1061 $cron_manager_links = $am->getLinks($this->
id, SQ_LINK_TYPE_2,
'cron_manager', TRUE,
'minor');
1062 if (empty($cron_manager_links)) {
1064 $notice_links = $am->getLinks($this->
id, SQ_LINK_NOTICE, NULL, NULL,
'major');
1066 foreach ($notice_links as $notice_link) {
1067 $am->deleteAssetLink($notice_link[
'linkid']);
1070 $trash = $am->getAsset($am->getSystemAssetid(
'trash_folder'));
1071 $trash->createLink($this, SQ_LINK_TYPE_1);