1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35:
36:
37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64:
65: class CrontabUtil {
66:
67: const CRONTAB_FIELD_DELIM = '#@X2CRM@';
68:
69: const CRONTAB_MANAGED_BEGIN = '#<X2CRM>';
70:
71: const CRONTAB_MANAGED_END = '#</X2CRM>';
72:
73: 74: 75: 76:
77: public static $printHint = true;
78:
79: 80: 81: 82:
83: public static $taskFields = array('cmd', 'tag', 'desc');
84:
85: public static $schedules = array('hourly','daily','weekly','monthly','yearly');
86:
87: 88: 89:
90: public static $schedFields = array('min', 'hour', 'dayOfMonth', 'month', 'dayOfWeek');
91:
92: 93: 94: 95: 96: 97: 98:
99: public static function addCronMarker(&$crontab, $check = false){
100: if(strpos($crontab, self::CRONTAB_MANAGED_BEGIN) === false){
101: if($check)
102: throw new Exception('X2Engine management delimiters not found in cron table.');
103: $crontab .= implode("\n", array('', self::CRONTAB_MANAGED_BEGIN, self::CRONTAB_MANAGED_END, ''));
104: }
105: }
106:
107: 108: 109: 110: 111: 112:
113: public static function arrayToCrontab(&$crontab, $crontabArray){
114: self::addCronMarker($crontab);
115: $crontabLines = explode("\n", $crontab);
116: $newCrontabLines = array();
117: $managed = false;
118: $finished = false;
119: foreach($crontabLines as $line){
120: if(strpos($line, self::CRONTAB_MANAGED_BEGIN) !== false)
121: $managed = true;
122: if(strpos($line, self::CRONTAB_MANAGED_END) !== false && $finished && $managed)
123: $managed = false;
124: if($managed){
125: if($finished)
126: continue;
127:
128:
129: $newCrontabLines[] = self::CRONTAB_MANAGED_BEGIN;
130:
131: foreach($crontabArray as $lineCfg)
132: $newCrontabLines[] = self::crontabLine($lineCfg);
133: $finished = true;
134: } else
135: $newCrontabLines[] = $line;
136: }
137: $crontab = implode("\n", $newCrontabLines);
138: }
139:
140: 141: 142: 143: 144:
145: public static function cronJobToForm($crontabLine) {
146: $data = $crontabLine;
147: $data['use_schedule'] = 0;
148: foreach(self::$schedFields as $name) {
149:
150: $data["all_$name"] = 1;
151: if(isset($data[$name])) {
152: if($data[$name] == '*') {
153: $data[$name] = array();
154: } else {
155: $data["all_$name"] = 0;
156: }
157: } else {
158: $data[$name] = array();
159: }
160: }
161: if(isset($data['schedule'])) {
162: $data['use_schedule'] = 1;
163: }
164: return $data;
165: }
166:
167: 168: 169: 170: 171: 172:
173: public static function crontabLine($l){
174: if(isset($l['schedule'])) {
175: return "@{$l['schedule']} {$l['cmd']} ".self::CRONTAB_FIELD_DELIM.$l['tag'].self::CRONTAB_FIELD_DELIM.$l['desc'];
176: }else{
177: foreach(self::$schedFields as $f){
178: ${$f} = is_array($l[$f]) ? implode(',', $l[$f]) : $l[$f];
179: }
180: return "$min $hour $dayOfMonth $month $dayOfWeek {$l['cmd']} ".self::CRONTAB_FIELD_DELIM.$l['tag'].self::CRONTAB_FIELD_DELIM.$l['desc'];
181: }
182: }
183:
184: 185: 186: 187: 188: 189: 190: 191: 192: 193:
194: public static function crontabToArray(&$crontab){
195: try{
196: self::addCronMarker($crontab,true);
197: }catch(Exception $e){
198: return array();
199: }
200: $cron = explode(self::CRONTAB_MANAGED_BEGIN."\n", $crontab);
201: $cron = explode("\n".self::CRONTAB_MANAGED_END, $cron[1]);
202: $cron = explode("\n", $cron[0]);
203: $cronArray = array();
204: foreach($cron as $line){
205: if(!($lineCfg = self::parseCrontabLine($line)))
206: continue;
207: $cronArray[$lineCfg['tag']] = $lineCfg;
208: }
209: return $cronArray;
210: }
211:
212: 213: 214: 215: 216: 217: 218: 219: 220:
221: public static function inputName($formName,$tag,$name,$isArray=0) {
222: return $formName."[$tag][$name]".($isArray?'[]':'');
223: }
224:
225: public static function jsName($name,$tag) {
226: return (strpos($name,'[') ? strtr($name,array('['=>'_',']'=>'_')) : $name)."_$tag";
227: }
228:
229: 230: 231: 232: 233: 234: 235: 236: 237: 238:
239: public static function parseCrontabLine($line) {
240: $configTagDesc = explode(self::CRONTAB_FIELD_DELIM, $line);
241: $nFields = count($configTagDesc);
242: if(strpos($line,'#') === 0)
243: return false;
244: if($nFields < 2)
245: return false;
246:
247: $config = mb_ereg_replace('\s+', ' ', $configTagDesc[0]);
248: $configArr = explode(' ', $config);
249: $tag = $configTagDesc[1];
250: $cronLine = array(
251: 'tag' => $tag,
252: 'desc' => $nFields == 3 ? $configTagDesc[2] : ''
253: );
254: if(preg_match('/^@('.implode('|',self::$schedules).')/',$config,$matches)){
255:
256: $cronLine['schedule'] = $matches[1];
257: $cronLine['cmd'] = implode(' ', array_slice($configArr, 1, -1));
258: }else if(preg_match('/^(?:[\*0-9,]+ ){5}/',$config,$matches)){
259:
260: foreach(self::$schedFields as $i => $field){
261: $cronLine[$field] = $configArr[$i] == '*' ? '*' : explode(',', $configArr[$i]);
262: }
263: $cronLine['cmd'] = implode(' ', array_slice($configArr, 5, -1));
264: } else {
265:
266: return false;
267: }
268: return $cronLine;
269: }
270:
271: 272: 273: 274: 275: 276:
277: public static function processForm($form=array()){
278:
279: $ca = array_intersect_key($form,array_fill_keys(array_merge(self::$taskFields,self::$schedFields),''));
280:
281: if(isset($form['schedule'],$form['use_schedule'])?$form['use_schedule']:false){
282:
283: $ca['schedule'] = $form['schedule'];
284: }else{
285:
286:
287: foreach(self::$schedFields as $schedPart){
288:
289:
290:
291:
292: if((isset($form["all_$schedPart"]) ? (bool)(int)$form["all_$schedPart"] : false) || empty($form[$schedPart]))
293: $ca[$schedPart] = '*';
294: }
295: }
296: return $ca;
297: }
298:
299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314:
315: public static function schedForm($data = array(), $name = 'cron',
316: $cmd = null, $tag = 'default', $desc = null){
317: $jsName = self::jsName($name,$tag);
318:
319: $jsns = "cronForm.$jsName";
320: $ns = function($return = 0) use($jsns) {if($return) return $jsns; else echo $jsns;};
321: if(empty($cmd))
322: $cmd = $desc = "";
323: foreach(array('cmd', 'tag', 'desc') as $var){
324: if(!empty(${$var}) && empty($data[$var])){
325: $data[$var] = ${$var};
326: }
327: }
328:
329:
330:
331: $formDisabled = function($n)use($data){
332: (isset($data[$n]) ? $data[$n] : true) ? 'disabled' : '';};
333:
334:
335:
336:
337:
338:
339:
340: $installer_t = function_exists('installer_t')
341: ? function($m){return call_user_func('installer_t',$m);}
342: : (class_exists('Yii')
343: ? function($m){return Yii::t('install',$m);}
344: : function($m){return $m;}
345: );
346:
347: $inlineStyles = array(
348: '#cron-form-top' => 'display: block;',
349: '#top-form' => 'padding-bottom: 10px;',
350: '#cron-text-value' => 'margin-left: 125px;',
351: '#cron-form-textarea' => 'max-width: 420px;',
352: '#cron-form-value' => 'padding-left: 125px;',
353: '#cron-cmd-value' => 'display: table-cell; padding-left: 26px;',
354: '#cron-form-pair' => 'display: table;',
355: '#cron-form-label' => 'float: left; display: table-cell;',
356: '#cron-form-value' => 'display: table-cell; position: relative; padding-left: 20px;',
357: '#schedFormTitle' => 'width: 100%;',
358: '#simpleTitle' => 'padding-bottom: 15px;',
359: '#mainTitle' => 'display: table; margin-top: -10px;',
360: '#schedInputs' => 'width: 120px;display: table-cell;margin-top: -50px;padding-bottom: 15px;',
361: '#cron-ui-submit' => 'position: relative; top: 7px; left: 450px; color: buttontext;',
362: '#ui-radio' => 'margin-left: -50px;',
363: '#cron-bot' => 'padding-bottom: 10px;',
364: '#startCron' => 'margin-top: 20px;'
365: );
366: $checked = function($n,$tf) use($data) {
367: if((bool)(int)$data[$n] == (bool)(int)$tf)
368: echo 'checked';
369: };
370:
371: ob_start();
372: ?>
373: <div style="#cron-form-top">
374: <?php if(empty($cmd)): ?>
375: <div style="#cron-form-pair">
376: <div style="#cron-form-label"><?php echo $installer_t('Command'); ?></div>
377: <div style="#cron-cmd-value">
378: <input style="#cron-textbox" name="<?php echo self::inputName($name,$tag,'cmd'); ?>" size="60" value="<?php echo $cmd; ?>" />
379: <input type="hidden" value="<?php echo $tag; ?>" name="<?php echo self::inputName($name,$tag,'tag'); ?>" />
380: </div>
381: </div>
382: <div style="#cron-form-pair">
383: <div style="#cron-form-label"><?php echo $installer_t('Description'); ?></div>
384: <div style="#cron-form-value">
385: <input style="#cron-textbox" name="<?php echo self::inputName($name,$tag,'desc'); ?>" size="60" value="<?php echo $desc; ?>" />
386: <span style="#cron-error"><?php echo $desc; ?></span>
387: <input type="hidden" name="<?php echo self::inputName($name,$tag,'desc'); ?>" value="" />
388: </div>
389: </div>
390: <input type="hidden" name="<?php echo self::inputName($name,$tag,'tag'); ?>" value="<?php echo htmlentities($data['tag'],ENT_QUOTES); ?>" />
391: <?php else: ?>
392: <input type="hidden" name="<?php echo self::inputName($name,$tag,'cmd'); ?>" value="<?php echo htmlentities($cmd, ENT_QUOTES); ?>" />
393: <input type="hidden" name="<?php echo self::inputName($name,$tag,'desc'); ?>" value="<?php echo htmlentities($desc, ENT_QUOTES); ?>" />
394: <input type="hidden" name="<?php echo self::inputName($name,$tag,'tag'); ?>" value="<?php echo $tag; ?>" />
395: <?php endif; ?>
396: </div>
397:
398: <div>
399: <input type='radio' value='1' name="<?php echo self::inputName($name,$tag,'use_schedule'); ?>" <?php $checked('use_schedule',1); ?> onclick="<?php $ns(); ?>.scheduleMode(form,1);" />
400: <?php echo $installer_t('Simple Schedule');
401: $scheduleList = array(
402: 'hourly' => $installer_t('Hourly'),
403: 'daily' => $installer_t('Daily (at Midnight)'),
404: 'weekly' => $installer_t('Weekly (on Sunday)'),
405: 'monthly' => $installer_t('Monthly (on the 1st)'),
406: 'yearly' => $installer_t('Yearly (on Jan 1st)'),
407: );
408: $schedule = isset($data['schedule']) ? $data['schedule'] : 'hourly';
409: ?>
410: <select name="<?php echo self::inputName($name,$tag,'schedule'); ?>">
411: <?php
412: foreach($scheduleList as $scheduleName => $scheduleLabel){
413: $sel = $scheduleName == $schedule ? ' selected' : '';
414: echo "<option value=\"$scheduleName\"$sel>$scheduleLabel</option>";
415: }
416: ?>
417: </select>
418: <input type="radio" name="<?php echo self::inputName($name,$tag,'use_schedule'); ?>" value="0" <?php $checked('use_schedule',0); ?> onchange="<?php $ns(); ?>.scheduleMode(form,0);" />
419: <?php echo $installer_t('Times and Dates Selected'); ?>
420: <?php echo '<p>'.(self::$printHint ? $installer_t('Note: hold down the control key (or command key, on Macintosh) to select or deselect multiple values.'):'').'</p>'; ?>
421: <div style="#schedFormTitle">
422: <div style="#mainTitle">
423: <div style="#schedInputs">
424: <strong><?php echo $installer_t('Minutes'); ?></strong><br />
425: <div>
426: <input type="radio" name="<?php echo self::inputName($name,$tag,'all_min'); ?>" value="1" <?php $checked('all_min',1); ?> onclick="<?php $ns(); ?>.enableField(form,'<?php echo self::inputName($name,$tag,'min',1); ?>',0);" />
427: <?php echo $installer_t('All'); ?>
428: <br>
429: <input type="radio" name="<?php echo self::inputName($name,$tag,'all_min'); ?>" value="0" <?php $checked('all_min',0); ?> onclick="<?php $ns(); ?>.enableField(form,'<?php echo self::inputName($name,$tag,'min',1); ?>',1);" />
430: <?php echo $installer_t('Selected'); ?>
431: </div>
432: <select multiple size="10" name="<?php echo self::inputName($name,$tag,'min',1); ?>" <?php echo $formDisabled(self::inputName($name,$tag,'min',1)); ?>>
433: <?php
434: $minList = range(0,59);
435: $minSel = self::timeList(isset($data['min']) ? $data['min'] : array());
436: foreach($minList as $min){
437: $sel = in_array($min,$minSel) ? ' selected' : '';
438: echo "<option value=\"$min\"$sel>$min</option>";
439: }
440: ?>
441: </select>
442: </div>
443: <div style="#schedInputs">
444: <strong><?php echo $installer_t('Hours'); ?></strong><br />
445: <div>
446: <input type="radio" name="<?php echo self::inputName($name,$tag,'all_hour'); ?>" value="1" <?php $checked('all_hour',1); ?> onclick="<?php $ns(); ?>.enableField(form,'<?php echo self::inputName($name,$tag,'hour',1); ?>',0);" />
447: <?php echo $installer_t('All'); ?>
448: <br>
449: <input type="radio" name="<?php echo self::inputName($name,$tag,'all_hour'); ?>" value="0" <?php $checked('all_hour',0); ?> onclick="<?php $ns(); ?>.enableField(form,'<?php echo self::inputName($name,$tag,'hour',1); ?>',1);" />
450: <?php echo $installer_t('Selected'); ?>
451: </div>
452: <select multiple size="10" name="<?php echo self::inputName($name,$tag,'hour',1); ?>" <?php echo $formDisabled(self::inputName($name,$tag,'hour',1)); ?>>
453: <?php
454: $hourList = range(0,23);
455: $hourSel = self::timeList(isset($data['hour']) ? $data['hour'] : array(),$hourList);
456: foreach($hourList as $hour){
457: $sel = in_array($hour,$hourSel) ? ' selected' : '';
458: echo "<option value=\"$hour\"$sel>$hour</option>";
459: }
460: ?>
461: </select>
462: </div>
463: <div style="#schedInputs">
464: <strong><?php echo $installer_t('Days'); ?></strong><br />
465: <div>
466: <input type="radio" name="<?php echo self::inputName($name,$tag,'all_dayOfMonth'); ?>" value="1" <?php $checked('all_dayOfMonth',1); ?> onclick="<?php $ns(); ?>.enableField(form,'<?php echo self::inputName($name,$tag,'dayOfMonth',1); ?>',0);" />
467: <?php echo $installer_t('All'); ?>
468: <br>
469: <input type="radio" name="<?php echo self::inputName($name,$tag,'all_dayOfMonth'); ?>" value="0" <?php $checked('all_dayOfMonth',0); ?> onclick="<?php $ns(); ?>.enableField(form,'<?php echo self::inputName($name,$tag,'dayOfMonth',1); ?>',1);" />
470: <?php echo $installer_t('Selected'); ?>
471: </div>
472: <select multiple size="10" name="<?php echo self::inputName($name,$tag,'dayOfMonth',1); ?>" <?php echo $formDisabled(self::inputName($name,$tag,'days')); ?>>
473: <?php
474: $daysList = range(1,31);
475: $daysSel = self::timeList(isset($data['dayOfMonth']) ? $data['dayOfMonth'] : array(), $daysList);
476: foreach($daysList as $day){
477: $sel = in_array($day,$daysSel) ? ' selected' : '';
478: echo "<option value=\"$day\"$sel>$day</option>";
479: }
480: ?>
481: </select>
482: </div>
483: <div style="#schedInputs">
484: <strong><?php echo $installer_t('Months'); ?></strong><br />
485: <input type="radio" name="<?php echo self::inputName($name,$tag,'all_month'); ?>" value="1" <?php $checked('all_month',1); ?> onclick="<?php $ns(); ?>.enableField(form,'<?php echo self::inputName($name,$tag,'month',1); ?>',0);" />
486: <?php echo $installer_t('All'); ?>
487: <br>
488: <input type="radio" name="<?php echo self::inputName($name,$tag,'all_month'); ?>" value="0" <?php $checked('all_month',0); ?> onclick="<?php $ns(); ?>.enableField(form,'<?php echo self::inputName($name,$tag,'month',1); ?>',1);" />
489: <?php echo $installer_t('Selected'); ?>
490: <select multiple size="10" name="<?php echo self::inputName($name,$tag,'month',1); ?>" <?php echo $formDisabled(self::inputName($name,$tag,'all_month')); ?>>
491: <?php
492: $monthList = array(
493: $installer_t('January'),
494: $installer_t('Febraury'),
495: $installer_t('March'),
496: $installer_t('April'),
497: $installer_t('May'),
498: $installer_t('June'),
499: $installer_t('July'),
500: $installer_t('August'),
501: $installer_t('September'),
502: $installer_t('October'),
503: $installer_t('November'),
504: $installer_t('December'),
505: );
506: $month = self::timeList(isset($data['month']) ? $data['month'] : array());
507: foreach($monthList as $i => $m){
508: $ii = $i+1;
509: $sel = in_array($ii, $month) ? ' selected' : '';
510: echo "<option value=\"$ii\"$sel>$m</option>";
511: }
512: ?>
513: </select>
514: </div>
515: <div style="#schedInputs">
516: <strong><?php echo $installer_t('Weekdays'); ?></strong><br />
517: <input type="radio" name="<?php echo self::inputName($name,$tag,'all_dayOfWeek'); ?>" value="1" <?php $checked('all_dayOfWeek',1); ?> onclick="<?php $ns(); ?>.enableField(form,'<?php echo self::inputName($name,$tag,'dayOfWeek',1); ?>',0);" />
518: <?php echo $installer_t('All'); ?>
519: <br>
520: <input type="radio" name="<?php echo self::inputName($name,$tag,'all_dayOfWeek'); ?>" value="0" <?php $checked('all_dayOfWeek',0); ?> onclick="<?php $ns(); ?>.enableField(form,'<?php echo self::inputName($name,$tag,'dayOfWeek',1); ?>',1);" />
521: <?php echo $installer_t('Selected'); ?>
522: <select multiple size="7" name="<?php echo self::inputName($name,$tag,'dayOfWeek',1); ?>" <?php echo $formDisabled(self::inputName($name,$tag,'dayOfWeek')); ?>>
523: <?php
524: $dayOfWeekList = array(
525: $installer_t('Sunday'),
526: $installer_t('Monday'),
527: $installer_t('Tuesday'),
528: $installer_t('Wednesday'),
529: $installer_t('Thursday'),
530: $installer_t('Friday'),
531: $installer_t('Sautrday')
532: );
533: $dayOfWeek = self::timeList(isset($data['dayOfWeek']) ? $data['dayOfWeek'] : array());
534:
535: foreach($dayOfWeekList as $i => $w){
536: $sel = in_array($i, $dayOfWeek) ? ' selected' : '';
537: echo "<option value=\"$i\"$sel>$w</option>";
538: }
539: ?>
540: </select>
541: </div>
542: </div>
543: </div>
544: <script>
545: if(typeof cronForm == 'undefined')
546: cronForm = {};
547: if(typeof cronForm._namespaces == 'undefined')
548: cronForm._nameSpaces = {};
549: if(typeof(cronForm._getSubForm) == 'undefined') {
550: cronForm._subForm = function(tag) {
551: var ns = cronForm._nameSpaces[tag];
552: if(typeof ns == 'undefined')
553: return undefined;
554: if(typeof cronForm[ns] == 'undefined')
555: return {};
556: return cronForm[ns];
557: }
558: }
559: if(typeof <?php $ns(); ?> == 'undefined') {
560: <?php $ns(); ?> = {};
561: cronForm._nameSpaces["<?php echo $tag; ?>"] = "<?php echo $jsName; ?>";
562: }
563: <?php
564: $schedParts = self::$schedFields;
565: $schedAllParts = array_map(function($n){return "all_$n";},$schedParts);
566: $schedNames = array();
567: $schedAllNames = array();
568: foreach($schedParts as $part) {
569: $schedNames[$part] = self::inputName($name,$tag,$part);
570: $schedAllNames["all_$part"] = self::inputName($name,$tag,"all_$part");
571: }
572: foreach(array('Parts','Names') as $array) {
573: foreach(array('','All') as $type) {
574: $jsVarName = "sched$type$array";
575: echo $ns(1).".$jsVarName = ".json_encode(${"$jsVarName"}).";\n".str_repeat(' ',16);
576: }
577: }
578: ?>
579: <?php $ns(); ?>.scheduleField = <?php echo json_encode(self::inputName($name,$tag,'schedule')); ?>;
580: <?php $ns(); ?>.scheduleModeField = <?php echo json_encode(self::inputName($name,$tag,'use_schedule')); ?>;
581: 582: 583:
584: <?php $ns(); ?>.enableField = function(form, name, enable) {
585: var elts = form.elements[name];
586: elts.disabled = !enable;
587: for(var i=0; i<elts.length; i++) {
588: elts[i].disabled = !enable;
589: }
590: }
591: 592: 593:
594: <?php $ns(); ?>.radioButtonValue = function(form,elt) {
595: for(var i=0;i<form[elt].length;i++)
596: if(form[elt][i].checked)
597: return form[elt][i].value
598: }
599:
600: 601: 602: 603: 604:
605: <?php $ns(); ?>.scheduleMode = function(form,mode) {
606: var allElt,i;
607: for(var i=0;i<<?php $ns(); ?>.schedParts.length;i++) {
608: allElt = <?php $ns(); ?>.schedAllNames[<?php $ns(); ?>.schedAllParts[i]];
609: <?php $ns(); ?>.enableField(form,allElt,!mode);
610: <?php $ns(); ?>.enableField(form,<?php $ns(); ?>.schedNames[<?php $ns(); ?>.schedParts[i]]+'[]',(<?php $ns(); ?>.radioButtonValue(form,allElt) == '0' && !mode));
611: }
612: form.elements[<?php $ns(); ?>.scheduleField].disabled = !mode;
613: }
614:
615:
616: 617: 618:
619: <?php $ns(); ?>.setup = function () {
620: for(var i=0;i<document.forms.length;i++) {
621: if(typeof document.forms[i].elements[<?php $ns(); ?>.scheduleField] != 'undefined') {
622: <?php $ns(); ?>.scheduleMode(document.forms[i],<?php $ns(); ?>.radioButtonValue(document.forms[i],<?php $ns(); ?>.scheduleModeField)=='1');
623: var schedModeButton = document.forms[i].elements[<?php $ns(); ?>.scheduleModeField];
624: }
625: }
626: }
627:
628:
629: <?php $ns(); ?>.setup();
630: </script>
631: </div>
632: <br />
633: <?php
634: $inputs = strtr(ob_get_clean(),$inlineStyles);
635:
636: return $inputs;
637: }
638:
639: 640: 641: 642: 643: 644:
645: public static function timeList($timeField){
646: if(is_string($timeField)) {
647: if($timeField == '*') {
648:
649: return array();
650: } else {
651: return explode(',',$timeField);
652: }
653: } else {
654: return $timeField;
655: }
656: }
657:
658: }
659: ?>
660: