2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM Community Edition is a customer relationship management program developed by
5 * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Affero General Public License version 3 as published by the
9 * Free Software Foundation with the addition of the following permission added
10 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19 * You should have received a copy of the GNU Affero General Public License along with
20 * this program; if not, see http://www.gnu.org/licenses or write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
27 * The interactive user interfaces in modified source and object code versions
28 * of this program must display Appropriate Legal Notices, as required under
29 * Section 5 of the GNU Affero General Public License version 3.
31 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32 * these Appropriate Legal Notices must retain the display of the "Powered by
33 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34 * technical reasons, the Appropriate Legal Notices must display the words
35 * "Powered by SugarCRM".
36 ********************************************************************************/
38 /*********************************************************************************
41 ********************************************************************************/
43 class Scheduler extends SugarBean {
49 var $modified_user_id;
52 var $modified_by_name;
66 var $intervalHumanReadable;
76 var $timeOutMins = 60;
77 // standard SugarBean attrs
78 var $table_name = "schedulers";
79 var $object_name = "schedulers";
80 var $module_dir = "Schedulers";
81 var $new_schema = true;
82 var $process_save_dates = true;
86 function Scheduler($init=true) {
92 $user->retrieve('1'); // Scheduler jobs run as Admin
98 ///////////////////////////////////////////////////////////////////////////
99 //// SCHEDULER HELPER FUNCTIONS
101 * executes Scheduled job
104 if(empty($this->job)) { // only execute when valid
105 $GLOBALS['log']->fatal('Scheduler tried to fire an empty job!!');
109 $exJob = explode('::', $this->job);
110 if(is_array($exJob)) {
111 // instantiate a new SchedulersJob object and prep it
114 $trackerManager = TrackerManager::getInstance();
115 $trackerManager->pause();
116 $job = new SchedulersJob();
117 $job->scheduler_id = $this->id;
118 $job->scheduler = $this;
119 $job->execute_time = $job->handleDateFormat('now');
120 $jobId = $job->save();
121 $trackerManager->unPause();
122 $job->retrieve($jobId);
124 if($exJob[0] == 'function') {
125 $GLOBALS['log']->debug('----->Scheduler found a job of type FUNCTION');
126 require_once('modules/Schedulers/_AddJobsHere.php');
131 $GLOBALS['log']->debug('----->SchedulersJob firing '.$func);
133 $res = call_user_func($func);
142 } elseif($exJob[0] == 'url') {
143 if(function_exists('curl_init')) {
144 $GLOBALS['log']->debug('----->SchedulersJob found a job of type URL');
147 $GLOBALS['log']->debug('----->SchedulersJob firing URL job: '.$exJob[1]);
148 if($job->fireUrl($exJob[1])) {
166 * flushes dead or hung jobs
168 function flushDeadJobs() {
169 $GLOBALS['log']->debug('-----> Scheduler flushing dead jobs');
171 $lowerLimit = mktime(0, 0, 0, 1, 1, 2005); // jan 01, 2005, GMT-0
172 $now = TimeDate::getInstance()->getNow()->ts; // current timestamp
174 $q = " SELECT s.id, s.name FROM schedulers s WHERE s.deleted=0 AND s.status = 'In Progress'";
175 $r = $this->db->query($q);
178 while($a = $this->db->fetchByAssoc($r)) {
179 $q2 = " SELECT st.id, st.execute_time FROM schedulers_times st
181 AND st.scheduler_id = '{$a['id']}'
182 ORDER BY st.execute_time DESC";
183 $r2 = $this->db->query($q2);
185 $a2 = $this->db->fetchByAssoc($r2); // we only care about the newest
186 $a2['execute_time'] = $this->db->fromConvert($a2['execute_time'],'datetime');
188 $GLOBALS['log']->debug("-----> Scheduler found [ {$a['name']} ] 'In Progress' with most recent Execute Time at [ {$a2['execute_time']} GMT-0 ]");
190 $execTime = TimeDate::getInstance()->fromDB($a2['execute_time'])->ts;
192 $GLOBALS['log']->debug("-----> Checking if Scheduler execTime ($execTime) is higher than lowerLimit ($lowerLimit)");
194 if($execTime > $lowerLimit) {
195 if(($now - $execTime) >= (60 * $this->timeOutMins)) {
196 $GLOBALS['log']->info("-----> Scheduler found a dead Job. Flushing status and reseting Job");
197 $q3 = "UPDATE schedulers SET status = 'Active' WHERE id = '{$a['id']}'";
198 $this->db->query($q3);
200 $GLOBALS['log']->info("-----> Scheduler setting Job Instance status to 'failed'");
201 $q4 = "UPDATE schedulers_times SET status = 'failed' WHERE id = '{$a2['id']}';";
202 $this->db->query($q4);
204 $GLOBALS['log']->debug("-----> Scheduler will wait for job to complete - not past threshold of [ ".($this->timeOutMins * 60)."secs ] - timeDiff is ".($now - $execTime)." secs");
207 $GLOBALS['log']->fatal("-----> Scheduler got a bad execute time: [ {$a2['execute_time']} GMT-0 ]");
217 * calculates if a job is qualified to run
219 function fireQualified() {
220 if(empty($this->id)) { // execute only if we have an instance
221 $GLOBALS['log']->fatal('Scheduler called fireQualified() in a non-instance');
225 $now = TimeDate::getInstance()->getNow();
226 $now = $now->setTime($now->hour, $now->min, "00")->asDb();
227 $validTimes = $this->deriveDBDateTimes($this);
229 if(is_array($validTimes) && in_array($now, $validTimes)) {
230 $GLOBALS['log']->debug('----->Scheduler found valid job ('.$this->name.') for time GMT('.$now.')');
233 $GLOBALS['log']->debug('----->Scheduler did NOT find valid job ('.$this->name.') for time GMT('.$now.')');
239 * Checks if any jobs qualify to run at this moment
241 function checkPendingJobs() {
242 $this->cleanJobLog();
243 $allSchedulers = $this->get_full_list('', 'schedulers.status=\'Active\'');
245 $GLOBALS['log']->info('-----> Scheduler found [ '.count($allSchedulers).' ] ACTIVE jobs');
247 if(!empty($allSchedulers)) {
248 foreach($allSchedulers as $focus) {
249 if($focus->fireQualified()) {
251 $GLOBALS['log']->debug('----->Scheduler Job completed successfully');
253 $GLOBALS['log']->fatal('----->Scheduler Job FAILED');
258 $GLOBALS['log']->debug('----->No Schedulers found');
263 * This function takes a Scheduler object and uses its job_interval
264 * attribute to derive DB-standard datetime strings, as many as are
265 * qualified by its ranges. The times are from the time of calling the
268 * @param $focus Scheduler object
269 * @return $dateTimes array loaded with DB datetime strings derived from
270 * the job_interval attribute
271 * @return false If we the Scheduler is not in scope, return false.
273 function deriveDBDateTimes($focus) {
275 $GLOBALS['log']->debug('----->Schedulers->deriveDBDateTimes() got an object of type: '.$focus->object_name);
276 /* [min][hr][dates][mon][days] */
277 $dateTimes = array();
278 $ints = explode('::', str_replace(' ','',$focus->job_interval));
284 $today = getdate($timedate->getNow()->ts);
288 $GLOBALS['log']->debug('----->got * day');
290 } elseif(strstr($days, '*/')) {
291 // the "*/x" format is nonsensical for this field
292 // do basically nothing.
293 $theDay = str_replace('*/','',$days);
294 $dayName[] = $theDay;
295 } elseif($days != '*') { // got particular day(s)
296 if(strstr($days, ',')) {
297 $exDays = explode(',',$days);
298 foreach($exDays as $k1 => $dayGroup) {
299 if(strstr($dayGroup,'-')) {
300 $exDayGroup = explode('-', $dayGroup); // build up range and iterate through
301 for($i=$exDayGroup[0];$i<=$exDayGroup[1];$i++) {
304 } else { // individuals
305 $dayName[] = $dayGroup;
308 } elseif(strstr($days, '-')) {
309 $exDayGroup = explode('-', $days); // build up range and iterate through
310 for($i=$exDayGroup[0];$i<=$exDayGroup[1];$i++) {
317 // check the day to be in scope:
318 if(!in_array(($today['wday']+6)%7, $dayName)) {//$dayName starts from Monday, while $today['wday'] starts from Sunday
326 // derive months part
328 $GLOBALS['log']->debug('----->got * months');
329 } elseif(strstr($mons, '*/')) {
330 $mult = str_replace('*/','',$mons);
331 $startMon = $timedate->fromDb(date_time_start)->month;
332 $startFrom = ($startMon % $mult);
334 for($i=$startFrom;$i<=12;$i+$mult) {
335 $compMons[] = $i+$mult;
338 // this month is not in one of the multiplier months
339 if(!in_array($today['mon'],$compMons)) {
342 } elseif($mons != '*') {
343 if(strstr($mons,',')) { // we have particular (groups) of months
344 $exMons = explode(',',$mons);
345 foreach($exMons as $k1 => $monGroup) {
346 if(strstr($monGroup, '-')) { // we have a range of months
347 $exMonGroup = explode('-',$monGroup);
348 for($i=$exMonGroup[0];$i<=$exMonGroup[1];$i++) {
352 $monName[] = $monGroup;
355 } elseif(strstr($mons, '-')) {
356 $exMonGroup = explode('-', $mons);
357 for($i=$exMonGroup[0];$i<=$exMonGroup[1];$i++) {
360 } else { // one particular month
364 // check that particular months are in scope
365 if(!in_array($today['mon'], $monName)) {
372 $GLOBALS['log']->debug('----->got * dates');
373 } elseif(strstr($dates, '*/')) {
374 $mult = str_replace('*/','',$dates);
375 $startDate = $timedate->fromDb($focus->date_time_start)->day;
376 $startFrom = ($startDate % $mult);
378 for($i=$startFrom; $i<=31; $i+$mult) {
379 $dateName[] = str_pad(($i+$mult),2,'0',STR_PAD_LEFT);
383 if(!in_array($today['mday'], $dateName)) {
386 } elseif($dates != '*') {
387 if(strstr($dates, ',')) {
388 $exDates = explode(',', $dates);
389 foreach($exDates as $k1 => $dateGroup) {
390 if(strstr($dateGroup, '-')) {
391 $exDateGroup = explode('-', $dateGroup);
392 for($i=$exDateGroup[0];$i<=$exDateGroup[1];$i++) {
396 $dateName[] = $dateGroup;
399 } elseif(strstr($dates, '-')) {
400 $exDateGroup = explode('-', $dates);
401 for($i=$exDateGroup[0];$i<=$exDateGroup[1];$i++) {
405 $dateName[] = $dates;
408 // check that dates are in scope
409 if(!in_array($today['mday'], $dateName)) {
415 //$currentHour = gmdate('G');
416 //$currentHour = date('G', strtotime('00:00'));
418 $GLOBALS['log']->debug('----->got * hours');
419 for($i=0;$i<24; $i++) {
422 } elseif(strstr($hrs, '*/')) {
423 $mult = str_replace('*/','',$hrs);
424 for($i=0; $i<24; $i) { // weird, i know
428 } elseif($hrs != '*') {
429 if(strstr($hrs, ',')) {
430 $exHrs = explode(',',$hrs);
431 foreach($exHrs as $k1 => $hrGroup) {
432 if(strstr($hrGroup, '-')) {
433 $exHrGroup = explode('-', $hrGroup);
434 for($i=$exHrGroup[0];$i<=$exHrGroup[1];$i++) {
438 $hrName[] = $hrGroup;
441 } elseif(strstr($hrs, '-')) {
442 $exHrs = explode('-', $hrs);
443 for($i=$exHrs[0];$i<=$exHrs[1];$i++) {
452 //$currentMin = date('i');
453 $currentMin = $timedate->getNow()->minute;
454 if(substr($currentMin, 0, 1) == '0') {
455 $currentMin = substr($currentMin, 1, 1);
458 $GLOBALS['log']->debug('----->got * mins');
459 for($i=0; $i<60; $i++) {
460 if(($currentMin + $i) > 59) {
461 $minName[] = ($i + $currentMin - 60);
463 $minName[] = ($i+$currentMin);
466 } elseif(strstr($mins,'*/')) {
467 $mult = str_replace('*/','',$mins);
468 $startMin = $timedate->fromDb($focus->date_time_start)->minute;
469 $startFrom = ($startMin % $mult);
470 for($i=$startFrom; $i<=59; $i) {
471 if(($currentMin + $i) > 59) {
472 $minName[] = ($i + $currentMin - 60);
474 $minName[] = ($i+$currentMin);
479 } elseif($mins != '*') {
480 if(strstr($mins, ',')) {
481 $exMins = explode(',',$mins);
482 foreach($exMins as $k1 => $minGroup) {
483 if(strstr($minGroup, '-')) {
484 $exMinGroup = explode('-', $minGroup);
485 for($i=$exMinGroup[0]; $i<=$exMinGroup[1]; $i++) {
489 $minName[] = $minGroup;
492 } elseif(strstr($mins, '-')) {
493 $exMinGroup = explode('-', $mins);
494 for($i=$exMinGroup[0]; $i<=$exMinGroup[1]; $i++) {
502 // prep some boundaries - these are not in GMT b/c gmt is a 24hour period, possibly bridging 2 local days
503 if(empty($focus->time_from) && empty($focus->time_to) ) {
505 $timeToTs = $timedate->getNow(true)->get('+1 day')->ts;
507 $tfrom = $timedate->fromDbType($focus->time_from, 'time');
508 $timeFromTs = $timedate->getNow(true)->setTime($tfrom->hour, $tfrom->min)->ts;
509 $tto = $timedate->fromDbType($focus->time_to, 'time');
510 $timeToTs = $timedate->getNow(true)->setTime($tto->hour, $tto->min)->ts;
514 if(empty($focus->last_run)) {
517 $lastRunTs = $timedate->fromDb($focus->last_run)->ts;
522 * initialize return array
524 $validJobTime = array();
527 $timeStartTs = $timedate->fromDb($focus->date_time_start)->ts;
528 if(!empty($focus->date_time_end)) { // do the same for date_time_end if not empty
529 $timeEndTs = $timedate->fromDb($focus->date_time_end)->ts;
531 $timeEndTs = $timedate->getNow(true)->get('+1 day')->ts;
532 // $dateTimeEnd = '2020-12-31 23:59:59'; // if empty, set it to something ridiculous
535 /*_pp('hours:'); _pp($hrName);_pp('mins:'); _pp($minName);*/
536 $dateobj = $timedate->getNow();
537 $nowTs = $dateobj->ts;
538 $GLOBALS['log']->debug(sprintf("Constraints: start: %s from: %s end: %s to: %s now: %s",
539 gmdate('Y-m-d H:i:s', $timeStartTs), gmdate('Y-m-d H:i:s', $timeFromTs), gmdate('Y-m-d H:i:s', $timeEndTs),
540 gmdate('Y-m-d H:i:s', $timeToTs), $timedate->nowDb()
542 // _pp('currentHour: '. $currentHour);
543 // _pp('timeStartTs: '.date('r',$timeStartTs));
544 // _pp('timeFromTs: '.date('r',$timeFromTs));
545 // _pp('timeEndTs: '.date('r',$timeEndTs));
546 // _pp('timeToTs: '.date('r',$timeToTs));
547 // _pp('mktime: '.date('r',mktime()));
548 // _pp('timeLastRun: '.date('r',$lastRunTs));
554 foreach($hrName as $kHr=>$hr) {
555 foreach($minName as $kMin=>$min) {
556 $timedate->tzUser($dateobj);
557 $dateobj->setTime($hr, $min, 0);
558 $tsGmt = $dateobj->ts;
560 if( $tsGmt >= $timeStartTs ) { // start is greater than the date specified by admin
561 if( $tsGmt >= $timeFromTs ) { // start is greater than the time_to spec'd by admin
562 if($tsGmt > $lastRunTs) { // start from last run, last run should not be included
563 if( $tsGmt <= $timeEndTs ) { // this is taken care of by the initial query - start is less than the date spec'd by admin
564 if( $tsGmt <= $timeToTs ) { // start is less than the time_to
565 $validJobTime[] = $dateobj->asDb();
567 //_pp('Job Time is NOT smaller that TimeTO: '.$tsGmt .'<='. $timeToTs);
570 //_pp('Job Time is NOT smaller that DateTimeEnd: '.date('Y-m-d H:i:s',$tsGmt) .'<='. $dateTimeEnd); //_pp( $tsGmt .'<='. $timeEndTs );
574 //_pp('Job Time is NOT bigger that TimeFrom: '.$tsGmt .'>='. $timeFromTs);
577 //_pp('Job Time is NOT Bigger than DateTimeStart: '.date('Y-m-d H:i',$tsGmt) .'>='. $dateTimeStart);
581 //_ppd($validJobTime);
582 // need ascending order to compare oldest time to last_run
585 * If "Execute If Missed bit is set
587 $now = TimeDate::getInstance()->getNow();
588 $now = $now->setTime($now->hour, $now->min, "00")->asDb();
590 if($focus->catch_up == 1) {
591 if($focus->last_run == null) {
593 $validJobTime[] = $now;
595 // determine what the interval in min/hours is
596 // see if last_run is in it
598 if(!empty($validJobTime) && ($focus->last_run < $validJobTime[0]) && ($now > $validJobTime[0])) {
599 // cn: empty() bug 5914;
600 //if(!empty) should be checked, becasue if a scheduler is defined to run every day 4pm, then after 4pm, and it runs as 4pm, the $validJobTime will be empty, and it should not catch up
601 //if $focus->last_run is the the day before yesterday, it should run yesterday and tomorrow, but it hadn't run yesterday, then it should catch up today. But today is already filtered out when doing date check before. The catch up will not work on this occasion. If the scheduler runs at least one time on each day, I think this bug can be avoided.
602 $validJobTime[] = $now;
606 return $validJobTime;
609 function handleIntervalType($type, $value, $mins, $hours) {
611 /* [0]:min [1]:hour [2]:day of month [3]:month [4]:day of week */
612 $days = array ( 0 => $mod_strings['LBL_MON'],
613 1 => $mod_strings['LBL_TUE'],
614 2 => $mod_strings['LBL_WED'],
615 3 => $mod_strings['LBL_THU'],
616 4 => $mod_strings['LBL_FRI'],
617 5 => $mod_strings['LBL_SAT'],
618 6 => $mod_strings['LBL_SUN'],
619 '*' => $mod_strings['LBL_ALL']);
624 return trim($mod_strings['LBL_ON_THE']).$mod_strings['LBL_HOUR_SING'];
625 } elseif(!preg_match('/[^0-9]/', $hours) && !preg_match('/[^0-9]/', $value)) {
628 } elseif(preg_match('/\*\//', $value)) {
629 $value = str_replace('*/','',$value);
630 return $value.$mod_strings['LBL_MINUTES'];
631 } elseif(!preg_match('[^0-9]', $value)) {
632 return $mod_strings['LBL_ON_THE'].$value.$mod_strings['LBL_MIN_MARK'];
637 global $current_user;
638 if(preg_match('/\*\//', $value)) { // every [SOME INTERVAL] hours
639 $value = str_replace('*/','',$value);
640 return $value.$mod_strings['LBL_HOUR'];
641 } elseif(preg_match('/[^0-9]/', $mins)) { // got a range, or multiple of mins, so we return an 'Hours' label
643 } else { // got a "minutes" setting, so it will be at some o'clock.
644 $datef = $current_user->getUserDateTimePreferences();
645 return date($datef['time'], strtotime($value.':'.str_pad($mins, 2, '0', STR_PAD_LEFT)));
647 case 2: // day of month
648 if(preg_match('/\*/', $value)) {
651 return date('jS', strtotime('December '.$value));
655 return date('F', strtotime('2005-'.$value.'-01'));
656 case 4: // days of week
657 return $days[$value];
659 return 'bad'; // no condition to touch this branch
663 function setIntervalHumanReadable() {
664 global $current_user;
667 /* [0]:min [1]:hour [2]:day of month [3]:month [4]:day of week */
668 $ints = $this->intervalParsed;
669 $intVal = array('-', ',');
670 $intSub = array($mod_strings['LBL_RANGE'], $mod_strings['LBL_AND']);
671 $intInt = array(0 => $mod_strings['LBL_MINS'], 1 => $mod_strings['LBL_HOUR']);
675 foreach($ints['raw'] as $key => $interval) {
676 if($tempInt != $iteration) {
679 $iteration = $tempInt;
681 if($interval != '*' && $interval != '*/1') {
682 if(false !== strpos($interval, ',')) {
683 $exIndiv = explode(',', $interval);
684 foreach($exIndiv as $val) {
685 if(false !== strpos($val, '-')) {
686 $exRange = explode('-', $val);
687 foreach($exRange as $valRange) {
689 $tempInt .= $mod_strings['LBL_AND'];
691 $tempInt .= $this->handleIntervalType($key, $valRange, $ints['raw'][0], $ints['raw'][1]);
693 } elseif($tempInt != $iteration) {
694 $tempInt .= $mod_strings['LBL_AND'];
696 $tempInt .= $this->handleIntervalType($key, $val, $ints['raw'][0], $ints['raw'][1]);
698 } elseif(false !== strpos($interval, '-')) {
699 $exRange = explode('-', $interval);
700 $tempInt .= $mod_strings['LBL_FROM'];
703 foreach($exRange as $val) {
704 if($tempInt == $check) {
705 $tempInt .= $this->handleIntervalType($key, $val, $ints['raw'][0], $ints['raw'][1]);
706 $tempInt .= $mod_strings['LBL_RANGE'];
709 $tempInt .= $this->handleIntervalType($key, $val, $ints['raw'][0], $ints['raw'][1]);
713 } elseif(false !== strpos($interval, '*/')) {
714 $tempInt .= $mod_strings['LBL_EVERY'];
715 $tempInt .= $this->handleIntervalType($key, $interval, $ints['raw'][0], $ints['raw'][1]);
717 $tempInt .= $this->handleIntervalType($key, $interval, $ints['raw'][0], $ints['raw'][1]);
723 $this->intervalHumanReadable = $mod_strings['LBL_OFTEN'];
725 $tempInt = trim($tempInt);
726 if(';' == substr($tempInt, (strlen($tempInt)-1), strlen($tempInt))) {
727 $tempInt = substr($tempInt, 0, (strlen($tempInt)-1));
729 $this->intervalHumanReadable = $tempInt;
734 /* take an integer and return its suffix */
735 function setStandardArraysAttributes() {
737 global $app_list_strings; // using from month _dom list
739 $suffArr = array('','st','nd','rd');
740 for($i=1; $i<32; $i++) {
741 if($i > 3 && $i < 21) {
742 $this->suffixArray[$i] = $i."th";
743 } elseif (substr($i,-1,1) < 4 && substr($i,-1,1) > 0) {
744 $this->suffixArray[$i] = $i.$suffArr[substr($i,-1,1)];
746 $this->suffixArray[$i] = $i."th";
748 $this->datesArray[$i] = $i;
751 $this->dayInt = array('*',1,2,3,4,5,6,7);
752 $this->dayLabel = array('*',$mod_strings['LBL_MON'],$mod_strings['LBL_TUE'],$mod_strings['LBL_WED'],$mod_strings['LBL_THU'],$mod_strings['LBL_FRI'],$mod_strings['LBL_SAT'],$mod_strings['LBL_SUN']);
753 $this->monthsInt = array(0,1,2,3,4,5,6,7,8,9,10,11,12);
754 $this->monthsLabel = $app_list_strings['dom_cal_month_long'];
755 $this->metricsVar = array("*", "/", "-", ",");
756 $this->metricsVal = array(' every ','',' thru ',' and ');
760 * takes the serialized interval string and renders it into an array
762 function parseInterval() {
764 $ws = array(' ', '\r','\t');
765 $blanks = array('','','');
767 $intv = $this->job_interval;
768 $rawValues = explode('::', $intv);
769 $rawProcessed = str_replace($ws,$blanks,$rawValues); // strip all whitespace
771 $hours = $rawValues[1].':::'.$rawValues[0];
772 $months = $rawValues[3].':::'.$rawValues[2];
774 $intA = array ( 'raw' => $rawProcessed,
779 $this->intervalParsed = $intA;
783 * soft-deletes all job logs older than 24 hours
785 function cleanJobLog()
787 $GLOBALS['log']->info('DELETE FROM schedulers_times WHERE date_entered < '.db_convert("'" . TimeDate::getInstance()->getNow()->get("-1 day")->asDb() . "'", 'datetime'));
788 $this->db->query('DELETE FROM schedulers_times WHERE date_entered < '.db_convert("'" . TimeDate::getInstance()->getNow()->get("-1 day")->asDb() . "'", 'datetime'));
792 * checks for cURL libraries
794 function checkCurl() {
797 if(!function_exists('curl_init')) {
799 <table cellpadding="0" cellspacing="0" width="100%" border="0" class="list view">
801 <th width="25%" colspan="2"><slot>
802 '.$mod_strings['LBL_WARN_CURL_TITLE'].'
805 <tr class="oddListRowS1" >
806 <td scope="row" valign=TOP width="20%"><slot>
807 '.$mod_strings['LBL_WARN_CURL'].'
808 <td scope="row" valign=TOP width="80%"><slot>
809 <span class=error>'.$mod_strings['LBL_WARN_NO_CURL'].'</span>
817 function displayCronInstructions() {
819 global $sugar_config;
821 if (!isset($_SERVER['Path'])) {
822 $_SERVER['Path'] = getenv('Path');
825 if(isset($_SERVER['Path']) && !empty($_SERVER['Path'])) { // IIS IUSR_xxx may not have access to Path or it is not set
826 if(!strpos($_SERVER['Path'], 'php')) {
827 $error = '<em>'.$mod_strings['LBL_NO_PHP_CLI'].'</em>';
831 if(isset($_SERVER['Path']) && !empty($_SERVER['Path'])) { // some Linux servers do not make this available
832 if(!strpos($_SERVER['PATH'], 'php')) {
833 $error = '<em>'.$mod_strings['LBL_NO_PHP_CLI'].'</em>';
843 <table cellpadding="0" cellspacing="0" width="100%" border="0" class="list view">
846 '.$mod_strings['LBL_CRON_INSTRUCTIONS_WINDOWS'].'
849 <tr class="evenListRowS1">
850 <td scope="row" valign="top" width="70%"><slot>
851 '.$mod_strings['LBL_CRON_WINDOWS_DESC'].'<br>
852 <b>cd '.realpath('./').'<br>
853 php.exe -f cron.php</b>
860 <table cellpadding="0" cellspacing="0" width="100%" border="0" class="list view">
863 '.$mod_strings['LBL_CRON_INSTRUCTIONS_LINUX'].'
867 <td scope="row" valign=TOP class="oddListRowS1" bgcolor="#fdfdfd" width="70%"><slot>
868 '.$mod_strings['LBL_CRON_LINUX_DESC'].'<br>
869 <b>* * * * *
870 cd '.realpath('./').'; php -f cron.php > /dev/null 2>&1</b>
879 * Archives schedulers of the same functionality, then instantiates new
882 function rebuildDefaultSchedulers() {
884 // truncate scheduler-related tables
885 $this->db->query('DELETE FROM schedulers');
886 $this->db->query('DELETE FROM schedulers_times');
889 $sched3 = new Scheduler();
890 $sched3->name = $mod_strings['LBL_OOTB_TRACKER'];
891 $sched3->job = 'function::trimTracker';
892 $sched3->date_time_start = create_date(2005,1,1) . ' ' . create_time(0,0,1);
893 $sched3->date_time_end = create_date(2020,12,31) . ' ' . create_time(23,59,59);
894 $sched3->job_interval = '0::2::1::*::*';
895 $sched3->status = 'Active';
896 $sched3->created_by = '1';
897 $sched3->modified_user_id = '1';
898 $sched3->catch_up = '1';
900 $sched4 = new Scheduler();
901 $sched4->name = $mod_strings['LBL_OOTB_IE'];
902 $sched4->job = 'function::pollMonitoredInboxes';
903 $sched4->date_time_start = create_date(2005,1,1) . ' ' . create_time(0,0,1);
904 $sched4->date_time_end = create_date(2020,12,31) . ' ' . create_time(23,59,59);
905 $sched4->job_interval = '*::*::*::*::*';
906 $sched4->status = 'Active';
907 $sched4->created_by = '1';
908 $sched4->modified_user_id = '1';
909 $sched4->catch_up = '0';
912 $sched5 = new Scheduler();
913 $sched5->name = $mod_strings['LBL_OOTB_BOUNCE'];
914 $sched5->job = 'function::pollMonitoredInboxesForBouncedCampaignEmails';
915 $sched5->date_time_start = create_date(2005,1,1) . ' ' . create_time(0,0,1);
916 $sched5->date_time_end = create_date(2020,12,31) . ' ' . create_time(23,59,59);
917 $sched5->job_interval = '0::2-6::*::*::*';
918 $sched5->status = 'Active';
919 $sched5->created_by = '1';
920 $sched5->modified_user_id = '1';
921 $sched5->catch_up = '1';
924 $sched6 = new Scheduler();
925 $sched6->name = $mod_strings['LBL_OOTB_CAMPAIGN'];
926 $sched6->job = 'function::runMassEmailCampaign';
927 $sched6->date_time_start = create_date(2005,1,1) . ' ' . create_time(0,0,1);
928 $sched6->date_time_end = create_date(2020,12,31) . ' ' . create_time(23,59,59);
929 $sched6->job_interval = '0::2-6::*::*::*';
930 $sched6->status = 'Active';
931 $sched6->created_by = '1';
932 $sched6->modified_user_id = '1';
933 $sched6->catch_up = '1';
937 $sched7 = new Scheduler();
938 $sched7->name = $mod_strings['LBL_OOTB_PRUNE'];
939 $sched7->job = 'function::pruneDatabase';
940 $sched7->date_time_start = create_date(2005,1,1) . ' ' . create_time(0,0,1);
941 $sched7->date_time_end = create_date(2020,12,31) . ' ' . create_time(23,59,59);
942 $sched7->job_interval = '0::4::1::*::*';
943 $sched7->status = 'Inactive';
944 $sched7->created_by = '1';
945 $sched7->modified_user_id = '1';
946 $sched7->catch_up = '0';
955 //// END SCHEDULER HELPER FUNCTIONS
956 ///////////////////////////////////////////////////////////////////////////
959 ///////////////////////////////////////////////////////////////////////////
960 //// STANDARD SUGARBEAN OVERRIDES
962 * function overrides the one in SugarBean.php
964 function create_export_query($order_by, $where, $show_deleted = 0) {
965 return $this->create_new_list_query($order_by, $where,array(),array(), $show_deleted = 0);
969 * function overrides the one in SugarBean.php
973 * function overrides the one in SugarBean.php
975 function fill_in_additional_list_fields() {
976 $this->fill_in_additional_detail_fields();
980 * function overrides the one in SugarBean.php
982 function fill_in_additional_detail_fields() {
986 * function overrides the one in SugarBean.php
988 function get_list_view_data(){
990 $temp_array = $this->get_list_view_array();
991 $temp_array["ENCODED_NAME"]=$this->name;
992 $this->parseInterval();
993 $this->setIntervalHumanReadable();
994 $temp_array['JOB_INTERVAL'] = $this->intervalHumanReadable;
995 if($this->date_time_end == '2020-12-31 23:59' || $this->date_time_end == '') {
996 $temp_array['DATE_TIME_END'] = $mod_strings['LBL_PERENNIAL'];
998 $this->created_by_name = get_assigned_user_name($this->created_by);
999 $this->modified_by_name = get_assigned_user_name($this->modified_user_id);
1005 * returns the bean name - overrides SugarBean's
1007 function get_summary_text() {
1010 //// END STANDARD SUGARBEAN OVERRIDES
1011 ///////////////////////////////////////////////////////////////////////////
1012 } // end class definition