1: <?php
2:
3: /*****************************************************************************************
4: * X2Engine Open Source Edition is a customer relationship management program developed by
5: * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
6: *
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 X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
12: * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13: *
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
17: * details.
18: *
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
22: * 02110-1301 USA.
23: *
24: * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley,
25: * California 95067, USA. or at email address [email protected].
26: *
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.
30: *
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: * X2Engine" 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 X2Engine".
36: *****************************************************************************************/
37:
38: /**
39: * Widget-ized wrapper methods for rendering cron forms and processing input.
40: *
41: * @property array $jobTags Array of cron line tags for which to generate the form.
42: * @property array $displayCmds Array of commands for display purposes only.
43: * @package application.components
44: * @author Demitri Morgan <[email protected]>
45: */
46: class CronForm extends X2Widget {
47:
48: /**
49: * Array of commands for display purposes only
50: * @var array
51: */
52: private $_displayCmds;
53:
54: /**
55: * Stores the value of {@link jobTags}
56: * @param array
57: */
58: private $_jobTags;
59:
60: /**
61: * Command utility object
62: * @var CommandUtil
63: */
64: protected $cmdU;
65:
66: /**
67: * Current cron table
68: * @var string
69: */
70: protected $crontab;
71:
72: /**
73: * Array storing cron jobs configuration
74: */
75: protected $j = array();
76:
77: /**
78: * Flag for executing or skipping due to inadequate permissions or
79: * unavailability of cron on the system.
80: * @var bool
81: */
82: protected $execute = false;
83:
84: /**
85: * If set to true, this will enable receiving input from the "cmd" input
86: * field on the form, thus allowing the user free reign to schedule any
87: * cron job they want.
88: * @var bool
89: */
90: public $allowUserCmdInput = false;
91:
92: /**
93: * Array containing form data, i.e. $_POST
94: * @var type
95: */
96: public $formData = array();
97:
98: /**
99: * An array specifying cron jobs for which to generate inputs.
100: *
101: * The array is key-value pairs where keys are job tags and values are each
102: * an array with:
103: *
104: * cmd: the command to run (absolutely required, unless
105: * {@link allowUserCmdInput} input is set to true)
106: * title: title of the cron job
107: * desc: the short description, to be saved in the cron table as a comment
108: * longdesc: long user-friendly description
109: * instructions: additional info about the cron job, i.e. what exactly it
110: * does and any disclaimers they should know about
111: *
112: * @var type
113: */
114: public $jobs;
115:
116: /**
117: * In the case that form inputs for multiple cron jobs are being rendered,
118: * this is the string that will be included in output, separating them.
119: * @var type
120: */
121: public $jobSeparator = '<hr />';
122:
123: /**
124: * CSS class used by the label/title of each cron job section.
125: * @var type
126: */
127: public $labelCssClass ='cron-checkitem';
128:
129: /**
130: * Name (and thus index in form data) of the cron jobs.
131: * @var type
132: */
133: public $name = 'cron';
134:
135: public $titles = array();
136:
137: /**
138: * Override that skips anything and everything if it's not possible to
139: * control the cron table
140: * @param type $name
141: * @param type $parameters
142: */
143: public function __call($name, $parameters){
144: if($this->execute)
145: parent::__call($name, $parameters);
146: }
147:
148: public function __construct($owner = null){
149: $this->execute = true;
150: parent::__construct($owner);
151: try{
152: // Initialize command utility and load the cron table:
153: $this->cmdU = new CommandUtil();
154: $this->crontab = $this->cmdU->loadCrontab();
155: $this->j = CrontabUtil::crontabToArray($this->crontab);
156: }catch(Exception $e){
157: $this->execute = false;
158: }
159: }
160:
161: public function getDisplayCmds() {
162: if(!isset($this->_displayCmds)) {
163: $this->_displayCmds = array();
164: foreach($this->jobs as $tag => $attributes) {
165: $this->_displayCmds[$tag] = isset($attributes['cmd']) ? $attributes['cmd'] : '';
166: }
167: }
168: return $this->_displayCmds;
169: }
170:
171: /**
172: * Getter for {@link jobTags}
173: * @return type
174: */
175: public function getJobTags() {
176: if(!isset($this->_jobTags)) {
177: $this->_jobTags = array_keys($this->jobs);
178: }
179: return $this->_jobTags;
180: }
181:
182: /**
183: * Function to retrieve default initial values for job attributes.
184: * @param string $tag Tag of the job
185: * @param string $index Index in the job configuration
186: * @param mixed $ini Initial value if none specified
187: * @return type
188: */
189: public function jobAttr($tag,$index,$ini=null){
190: return isset($this->jobs[$tag][$index]) ? $this->jobs[$tag][$index] : $ini;
191: }
192:
193: /**
194: * Process form data and save in the cron table
195: */
196: public function save($formData){
197: // Nothing to save
198: if(!isset($formData[$this->name]))
199: $jobs = array();
200: else
201: $jobs = $formData[$this->name];
202: // Add/update all cron jobs for which there is form data present:
203: foreach($jobs as $tag => $job) {
204: if(is_array($job)) {
205: if(in_array($tag, $this->jobTags)) {
206: $this->j[$tag] = CrontabUtil::processForm($job);
207: // Overwrite cmd/desc with the default as defined in the widget declaration/job config:
208: if(!$this->allowUserCmdInput){
209: $this->j[$tag]['cmd'] = $this->jobAttr($tag, 'cmd');
210: $this->j[$tag]['desc'] = $this->jobAttr($tag, 'desc');
211: }
212: }
213: }
214: }
215:
216: // Delete any cron jobs not accounted for in form data, but expected:
217: foreach($this->jobTags as $tag) {
218: if(!isset($jobs[$tag]) && isset($this->j[$tag])) {
219: unset($this->j[$tag]);
220: }
221: }
222:
223: // Save the cron table:
224: CrontabUtil::arrayToCrontab($this->crontab, $this->j);
225: $ctFile = implode(DIRECTORY_SEPARATOR, array(Yii::app()->basePath, '.crontab.tmp'));
226: file_put_contents($ctFile, $this->crontab);
227: $this->cmdU->run("crontab $ctFile")->complete();
228: unlink($ctFile);
229: }
230:
231: /**
232: * Run/render a set of inputs for each cron job that this form will manage.
233: *
234: * "enable_$tag" is the value stored in the cron job enablement checkbox.
235: * "$tag" will be the form data loaded from crontab.
236: */
237: public function run() {
238: // Script for toggle checkboxes:
239: Yii::app()->clientScript->registerScript('cronForm', '
240: var toggleCronJobInputs = function(checkbox,initial) {
241: var tag = checkbox.prop("id").replace("cron-job-","");
242: if (typeof initial == "undefined")
243: initial = false;
244: if(checkbox.is(":checked")) {
245: $("#"+checkbox.attr("id")+"-form").each(function(){
246: if(initial) {
247: $("#"+checkbox.attr("id")+"-form").show();
248: } else {
249: $("#"+checkbox.attr("id")+"-form").slideDown().find("input,select").prop("disabled",false);
250: cronForm[cronForm._nameSpaces[tag]].setup();
251: }
252: });
253: } else {
254: $("#"+checkbox.attr("id")+"-form").each(function(){
255: if(initial) {
256: $(this).hide();
257: } else {
258: $(this).slideUp();
259: }
260: $(this).find("input,select").prop("disabled",true);
261: });
262: }
263: }
264: $(".cron-enabled").each(function() {
265: toggleCronJobInputs($(this),1);
266: }).change(function() {
267: toggleCronJobInputs($(this));
268: });
269: ',CClientScript::POS_READY);
270:
271: // Render form fields for each cron job managed by this widget:
272: CrontabUtil::$printHint = false;
273: $jobSections = array();
274: foreach($this->jobTags as $tag){
275: // Populate initial form data.
276: //
277: // The job is initially disabled if not found in the table
278: $enabled = isset($this->j[$tag]);
279: $this->formData[$tag] = CrontabUtil::cronJobToForm($enabled ? $this->j[$tag] : array());
280:
281: // Overwrite cmd/desc with default as defined in the widget declaration/job config:
282: if(!$this->allowUserCmdInput) {
283: $this->formData[$tag]['cmd'] = $this->jobAttr($tag,'cmd');
284: $this->formData[$tag]['desc'] = $this->jobAttr($tag,'desc');
285: }
286:
287: // Render the job form inputs for this particular job:
288: $viewData = array();
289: foreach(array('title', 'longdesc', 'instructions') as $attr){
290: $viewData[$attr] = $this->jobAttr($tag, $attr);
291: }
292: $jobSections[] = $this->render(
293: 'application.components.views.cronJobForm', array_merge($viewData, array(
294:
295: 'userCmd' => $this->allowUserCmdInput,
296: 'cmd' => $this->formData[$tag]['cmd'],
297: 'displayCmd' => isset($this->displayCmds[$tag])?$this->displayCmds[$tag]:'',
298: 'enabled' => $enabled,
299: 'labelClass' => $this->labelCssClass,
300: 'name' => $this->name,
301: 'tag' => $tag,
302: 'initialCron' => $this->formData[$tag],
303: )),true);
304: }
305: echo implode($this->jobSeparator,$jobSections);
306: }
307:
308: public function setDisplayCmds(array $value) {
309: $this->_displayCmds = $value;
310: }
311:
312: /**
313: * Setter for {@link jobTags}
314: * @param array $value
315: */
316: public function setJobTags(array $value) {
317: $this->_jobTags = $value;
318: }
319: }
320:
321: ?>
322: