19501c0c4434944976bba198165e52e616deffac
[Packages/TYPO3.CMS.git] / typo3 / sysext / scheduler / mod1 / index.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2009 Francois Suter <francois@typo3.org>
6 * (c) 2005 Christian Jul Jensen <julle@typo3.org>
7 * All rights reserved
8 *
9 * This script is part of the TYPO3 project. The TYPO3 project is
10 * free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * The GNU General Public License can be found at
16 * http://www.gnu.org/copyleft/gpl.html.
17 *
18 * This script is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * This copyright notice MUST APPEAR in all copies of the script!
24 ***************************************************************/
25
26 require_once (t3lib_extMgm::extPath('scheduler') . 'interfaces/interface.tx_scheduler_additionalfieldprovider.php');
27
28 $LANG->includeLLFile('EXT:scheduler/mod1/locallang.xml');
29 $BE_USER->modAccess($MCONF, 1); // This checks permissions and exits if the users has no permission for entry.
30
31 /**
32 * Module 'TYPO3 Scheduler administration module' for the 'scheduler' extension.
33 *
34 * @author Francois Suter <francois@typo3.org>
35 * @author Christian Jul Jensen <julle@typo3.org>
36 * @author Ingo Renner <ingo@typo3.org>
37 * @package TYPO3
38 * @subpackage tx_scheduler
39 * @version $Id: index.php 1262 2009-09-15 21:04:22Z francois $
40 */
41 class tx_scheduler_Module extends t3lib_SCbase {
42
43 /**
44 * Back path to typo3 main dir
45 *
46 * @var string $backPath
47 */
48 public $backPath;
49
50 /**
51 * Array containing submitted data when editing or adding a task
52 *
53 * @var array $submittedData
54 */
55 protected $submittedData = array();
56
57 /**
58 * Array containing all messages issued by the application logic
59 * Contains the error's severity and the message itself
60 *
61 * @var array $messages
62 */
63 protected $messages = array();
64
65 /**
66 * @var string Key of the CSH file
67 */
68 protected $cshKey;
69
70 /**
71 *
72 * @var tx_scheduler Local scheduler instance
73 */
74 protected $scheduler;
75
76 /**
77 * Constructor
78 *
79 * @return void
80 */
81 public function __construct() {
82 $this->backPath = $GLOBALS['BACK_PATH'];
83 // Set key for CSH
84 $this->cshKey = '_MOD_' . $GLOBALS['MCONF']['name'];
85 }
86
87 /**
88 * Initializes the backend module
89 *
90 * @return void
91 */
92 public function init() {
93 parent::init();
94
95 // Initialize document
96 $this->doc = t3lib_div::makeInstance('template');
97 $this->doc->setModuleTemplate(t3lib_extMgm::extPath('scheduler') . 'mod1/mod_template.html');
98 $this->doc->getPageRenderer()->addCssFile(t3lib_extMgm::extRelPath('scheduler') . 'res/tx_scheduler_be.css');
99 $this->doc->backPath = $this->backPath;
100 $this->doc->bodyTagId = 'typo3-mod-php';
101 $this->doc->bodyTagAdditions = 'class="tx_scheduler_mod1"';
102
103 // Create scheduler instance
104 $this->scheduler = t3lib_div::makeInstance('tx_scheduler');
105 }
106
107 /**
108 * Adds items to the ->MOD_MENU array. Used for the function menu selector.
109 *
110 * @return void
111 */
112 public function menuConfig() {
113 $this->MOD_MENU = array(
114 'function' => array(
115 'scheduler' => $GLOBALS['LANG']->getLL('function.scheduler'),
116 'check' => $GLOBALS['LANG']->getLL('function.check'),
117 'info' => $GLOBALS['LANG']->getLL('function.info'),
118 )
119 );
120
121 parent::menuConfig();
122 }
123
124 /**
125 * Main function of the module. Write the content to $this->content
126 *
127 * @return void
128 */
129 public function main() {
130 // Access check!
131 // The page will show only if user has admin rights
132 if ($GLOBALS['BE_USER']->user['admin']) {
133
134 // Set the form
135 $this->doc->form = '<form name="tx_scheduler_form" id="tx_scheduler_form" method="post" action="">';
136
137 // JavaScript for main function menu
138 $this->doc->JScode = '
139 <script language="javascript" type="text/javascript">
140 script_ended = 0;
141 function jumpToUrl(URL) {
142 document.location = URL;
143 }
144 </script>
145 ';
146
147 // Prepare main content
148 $this->content = $this->doc->header(
149 $GLOBALS['LANG']->getLL('function.' . $this->MOD_SETTINGS['function'])
150 );
151 $this->content .= $this->doc->spacer(5);
152 $this->content .= $this->getModuleContent();
153 } else {
154 // If no access, only display the module's title
155 $this->content = $this->doc->header($GLOBALS['LANG']->getLL('title'));
156 $this->content .= $this->doc->spacer(5);
157 }
158
159 // Place content inside template
160 $content = $this->doc->startPage($GLOBALS['LANG']->getLL('title'));
161 $content .= $this->doc->moduleBody(
162 array(),
163 $this->getDocHeaderButtons(),
164 $this->getTemplateMarkers()
165 );
166 $content .= $this->doc->endPage();
167
168 // Replace content with templated content
169 $this->content = $content;
170 }
171
172 /**
173 * Generate the module's content
174 *
175 * @return string HTML of the module's main content
176 */
177 protected function getModuleContent() {
178 $content = '';
179
180 // Get submitted data
181 $this->submittedData = t3lib_div::_GPmerged('tx_scheduler');
182
183 // If a save command was submitted, handle saving now
184 if ($this->CMD == 'save') {
185 $previousCMD = t3lib_div::_GP('previousCMD');
186 // First check the submitted data
187 $result = $this->preprocessData();
188
189 // If result is ok, proceed with saving
190 if ($result) {
191 $this->saveTask();
192 // Unset command, so that default screen gets displayed
193 unset($this->CMD);
194
195 // Errors occurred
196 // Go back to previous step
197 } else {
198 $this->CMD = $previousCMD;
199 }
200 }
201
202 // Handle chosen action
203 switch((string)$this->MOD_SETTINGS['function']) {
204 case 'scheduler':
205 // Scheduler's main screen
206 $content .= $this->executeTasks();
207
208 // Execute chosen action
209 switch ($this->CMD) {
210 case 'add':
211 case 'edit':
212 try {
213 // Try adding or editing
214 $content .= $this->editTask();
215 $sectionTitle = $GLOBALS['LANG']->getLL('action.' . $this->CMD);
216 } catch (Exception $e) {
217 // An exception may happen when the task to
218 // edit could not be found. In this case revert
219 // to displaying the list of tasks
220 // It can also happend when attempting to edit a running task
221 $content .= $this->listTasks();
222 }
223 break;
224 case 'delete':
225 $this->deleteTask();
226 $content .= $this->listTasks();
227 break;
228 case 'list':
229 default:
230 $content .= $this->listTasks();
231 break;
232 }
233 break;
234 case 'check':
235 // Setup check screen
236 // TODO: move check to the report module
237 $content .= $this->displayCheckScreen();
238 break;
239 case 'info':
240 // Information screen
241 $content .= $this->displayInfoScreen();
242 break;
243 }
244
245 // Wrap the content in a section
246 return $this->doc->section($sectionTitle, '<div class="tx_scheduler_mod1">' . $content . '</div>', 0, 1);
247 }
248
249 /**
250 * This method actually prints out the module's HTML content
251 *
252 * @return void
253 */
254 public function render() {
255 $this->content .= $this->doc->endPage();
256
257 echo $this->content;
258 }
259
260 /**
261 * This method checks the status of the '_cli_scheduler' user
262 * It will differentiate between a non-existing user and an existing,
263 * but disabled user (as per enable fields)
264 *
265 * @return integer -1 if user doesn't exist
266 * 0 if user exists, but is disabled
267 * 1 if user exists and is not disabled
268 */
269 protected function checkSchedulerUser() {
270 $schedulerUserStatus = -1;
271 // Assemble base WHERE clause
272 $where = 'username = \'_cli_scheduler\' AND admin = 0' . t3lib_BEfunc::deleteClause('be_users');
273 // Check if user exists at all
274 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
275 '1',
276 'be_users',
277 $where
278 );
279 if ($GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
280 $schedulerUserStatus = 0;
281 // Check if user exists and is enabled
282 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
283 '1',
284 'be_users',
285 $where . t3lib_BEfunc::BEenableFields('be_users')
286 );
287 if ($GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
288 $schedulerUserStatus = 1;
289 }
290 }
291 $GLOBALS['TYPO3_DB']->sql_free_result($res);
292
293 return $schedulerUserStatus;
294 }
295
296 /**
297 * This method creates the "cli_scheduler" BE user if it doesn't exist
298 *
299 * @return void
300 */
301 protected function createSchedulerUser() {
302 // Check _cli_scheduler user status
303 $checkUser = $this->checkSchedulerUser();
304 // Prepare default message
305 $message = $GLOBALS['LANG']->getLL('msg.userExists');
306 $severity = t3lib_FlashMessage::WARNING;
307 // If the user does not exist, try creating it
308 if ($checkUser == -1) {
309 // Prepare necessary data for _cli_scheduler user creation
310 $password = md5(uniqid('scheduler', true));
311 $data = array('be_users' => array('NEW' => array('username' => '_cli_scheduler', 'password' => $password, 'pid' => 0)));
312 /**
313 * Create an instance of TCEmain and execute user creation
314 *
315 * @var t3lib_TCEmain
316 */
317 $tcemain = t3lib_div::makeInstance('t3lib_TCEmain');
318 $tcemain->stripslashes_values = 0;
319 $tcemain->start($data, array());
320 $tcemain->process_datamap();
321 // Check if a new uid was indeed generated (i.e. a new record was created)
322 // (counting TCEmain errors doesn't work as some failures don't report errors)
323 $numberOfNewIDs = count($tcemain->substNEWwithIDs);
324 if ($numberOfNewIDs == 1) {
325 $message = $GLOBALS['LANG']->getLL('msg.userCreated');
326 $severity = t3lib_FlashMessage::OK;
327 } else {
328 $message = $GLOBALS['LANG']->getLL('msg.userNotCreated');
329 $severity = t3lib_FlashMessage::ERROR;
330 }
331 }
332 $this->addMessage($message, $severity);
333 }
334
335 /**
336 * This method displays the result of a number of checks
337 * on whether the Scheduler is ready to run or running properly
338 *
339 * @return string further information
340 */
341 protected function displayCheckScreen() {
342 // First, check if cli_sceduler user creation was requested
343 if ($this->CMD == 'user') {
344 $this->createSchedulerUser();
345 }
346
347 // Start generating the content
348 $content = $GLOBALS['LANG']->getLL('msg.schedulerSetupCheck');
349
350 // Display information about last automated run, as stored in the system registry
351 /**
352 * @var t3lib_Registry
353 */
354 $registry = t3lib_div::makeInstance('t3lib_Registry');
355 $lastRun = $registry->get('tx_scheduler', 'lastRun');
356 if (!is_array($lastRun)) {
357 $message = $GLOBALS['LANG']->getLL('msg.noLastRun');
358 $severity = t3lib_FlashMessage::WARNING;
359 } else {
360 if (empty($lastRun['end']) || empty($lastRun['start']) || empty($lastRun['type'])) {
361 $message = $GLOBALS['LANG']->getLL('msg.incompleteLastRun');
362 $severity = t3lib_FlashMessage::WARNING;
363 } else {
364 $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
365 $startDate = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], $lastRun['start']);
366 $startTime = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $lastRun['start']);
367 $endDate = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], $lastRun['end']);
368 $endTime = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $lastRun['end']);
369 if ($lastRun['type'] == 'manual') {
370 $label = 'manually';
371 } else {
372 $label = 'automatically';
373 }
374 $type = $GLOBALS['LANG']->getLL('label.' . $label);
375 $message = sprintf($GLOBALS['LANG']->getLL('msg.lastRun'), $type, $startDate, $startTime, $endDate, $endTime);
376 $severity = t3lib_FlashMessage::INFO;
377 }
378 }
379 $flashMessage = t3lib_div::makeInstance(
380 't3lib_FlashMessage',
381 $message,
382 '',
383 $severity
384 );
385 $content .= '<div class="info-block">';
386 $content .= '<h3>' . $GLOBALS['LANG']->getLL('hdg.lastRun') . '</h3>';
387 $content .= $flashMessage->render();
388 $content .= '</div>';
389
390 // Check CLI user
391 $content .= '<div class="info-block">';
392 $content .= '<h3>' . $GLOBALS['LANG']->getLL('hdg.schedulerUser') . '</h3>';
393 $content .= '<p>' . $GLOBALS['LANG']->getLL('msg.schedulerUser') . '</p>';
394
395 $checkUser = $this->checkSchedulerUser();
396 if ($checkUser == -1) {
397 $link = $GLOBALS['MCONF']['_'] . '&SET[function]=check&CMD=user';
398 $message = sprintf($GLOBALS['LANG']->getLL('msg.schedulerUserMissing'), $link);
399 $severity = t3lib_FlashMessage::ERROR;
400 } elseif ($checkUser == 0) {
401 $message = $GLOBALS['LANG']->getLL('msg.schedulerUserFoundButDisabled');
402 $severity = t3lib_FlashMessage::WARNING;
403 } else {
404 $message = $GLOBALS['LANG']->getLL('msg.schedulerUserFound');
405 $severity = t3lib_FlashMessage::OK;
406 }
407 $flashMessage = t3lib_div::makeInstance(
408 't3lib_FlashMessage',
409 $message,
410 '',
411 $severity
412 );
413 $content .= $flashMessage->render() . '</div>';
414
415 // Check CLI script
416 $script = PATH_typo3 . 'cli_dispatch.phpsh';
417 $content .= '<div class="info-block">';
418 $content .= '<h3>' . $GLOBALS['LANG']->getLL('hdg.cliScript') . '</h3>';
419 $content .= '<p>' . sprintf($GLOBALS['LANG']->getLL('msg.cliScript'), $script) . '</p>';
420
421 if (is_executable($script)) {
422 $message = $GLOBALS['LANG']->getLL('msg.cliScriptExecutable');
423 $severity = t3lib_FlashMessage::OK;
424 } else {
425 $message = $GLOBALS['LANG']->getLL('msg.cliScriptNotExecutable');
426 $severity = t3lib_FlashMessage::ERROR;
427 }
428 $flashMessage = t3lib_div::makeInstance(
429 't3lib_FlashMessage',
430 $message,
431 '',
432 $severity
433 );
434 $content .= $flashMessage->render() . '</div>';
435
436 return $content;
437 }
438
439 /**
440 * This method gathers information about all registered tasks and displays it
441 *
442 * @return string HTML content to display
443 */
444 protected function displayInfoScreen() {
445 $registeredClasses = self::getRegisteredClasses();
446
447 // Initialise table layout
448 $tableLayout = array (
449 'table' => array ('<table border="0" cellspacing="1" cellpadding="2" style="width:auto;">', '</table>'),
450 '0' => array (
451 'tr' => array('<tr class="bgColor2" valign="top">', '</tr>'),
452 'defCol' => array('<td class="cell">', '</td>')
453 ),
454 'defRow' => array (
455 'tr' => array('<tr class="bgColor3-20">', '</tr>'),
456 'defCol' => array('<td class="cell">', '</td>')
457 )
458 );
459 $table = array();
460 $tr = 0;
461
462 // Header row
463 $table[$tr][] = $GLOBALS['LANG']->getLL('label.name');
464 $table[$tr][] = $GLOBALS['LANG']->getLL('label.extension');
465 $table[$tr][] = $GLOBALS['LANG']->getLL('label.description');
466 $table[$tr][] = '';
467 $tr++;
468
469 // Display information about each service
470 foreach ($registeredClasses as $class => $classInfo) {
471 $table[$tr][] = $classInfo['title'];
472 $table[$tr][] = $classInfo['extension'];
473 $table[$tr][] = $classInfo['description'];
474 $link = $GLOBALS['MCONF']['_'] . '&SET[function]=list&CMD=add&tx_scheduler[class]=' . $class;
475 $table[$tr][] = '<a href="' . $link . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:new') . '"><img ' . t3lib_iconWorks::skinImg($this->backPath, 'gfx/new_el.gif') . ' alt="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:new') . '" /></a>';
476 $tr++;
477 }
478
479 // Render the table and return it
480 $content = '<div>' . $GLOBALS['LANG']->getLL('msg.infoScreenIntro') . '<div>';
481 $content .= $this->doc->spacer(5);
482 $content .= $this->doc->table($table, $tableLayout);
483
484 return $content;
485 }
486
487 /**
488 * Delete a task from the execution queue
489 *
490 * @return void
491 */
492 protected function deleteTask() {
493 try {
494 // Try to fetch the task and delete it
495 /**
496 * @var tx_scheduler_Task
497 */
498 $task = $this->scheduler->fetchTask($this->submittedData['uid']);
499 // If the task is currently running, it may not be deleted
500 if ($task->isExecutionRunning()) {
501 $this->addMessage($GLOBALS['LANG']->getLL('msg.maynotDeleteRunningTask'), 3);
502 } else {
503 if ($this->scheduler->removeTask($task)) {
504 $this->addMessage($GLOBALS['LANG']->getLL('msg.deleteSuccess'));
505 } else {
506 $this->addMessage($GLOBALS['LANG']->getLL('msg.deleteError'), t3lib_FlashMessage::ERROR);
507 }
508 }
509 } catch (OutOfBoundsException $e) {
510 // The task was not found, for some reason
511 $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $this->submittedData['uid']), t3lib_FlashMessage::ERROR);
512 }
513 }
514
515 /**
516 * Return a form to add a new task or edit an existing one
517 *
518 * @return string HTML form to add or edit a task
519 */
520 protected function editTask() {
521 $registeredClasses = self::getRegisteredClasses();
522 $content = '';
523 $taskInfo = array();
524
525 if ($this->submittedData['uid'] > 0) {
526 // If editing, retrieve data for existing task
527 try {
528 $taskRecord = $this->scheduler->fetchTaskRecord($this->submittedData['uid']);
529 // If there's a registered execution, the task should not be edited
530 if (!empty($taskRecord['serialized_executions'])) {
531 $this->addMessage($GLOBALS['LANG']->getLL('msg.maynotEditRunningTask'), 3);
532 throw new LogicException('Runnings tasks cannot not be edited', 1251232849);
533 }
534 $task = unserialize($taskRecord['serialized_task_object']);
535
536 // Set some task information
537 $taskInfo['disable'] = $taskRecord['disable'];
538
539 // Check that the task class still exists
540 $class = $taskRecord['classname'];
541 // If no class was found, issue error message
542 // This can happen if the extension providing a given class was uninstalled,
543 // but the scheduler job was not cancelled
544 if (isset($registeredClasses[$class])) {
545 $taskInfo['class'] = $class;
546 } else {
547 $this->addMessage($GLOBALS['LANG']->getLL('msg.invalidTaskClass'), t3lib_FlashMessage::ERROR);
548 }
549
550 // Get execution information
551 $taskInfo['start'] = $task->getExecution()->getStart();
552 $taskInfo['end'] = $task->getExecution()->getEnd();
553 $taskInfo['interval'] = $task->getExecution()->getInterval();
554 $taskInfo['croncmd'] = $task->getExecution()->getCronCmd();
555 $taskInfo['multiple'] = $task->getExecution()->getMultiple();
556
557 if (!empty($taskInfo['interval']) || !empty($taskInfo['croncmd'])) {
558 // Guess task type from the existing information
559 // If an interval or a cron command is defined, it's a recurring task
560
561 // FIXME remove magic numbers for the type, use class constants instead
562 $taskInfo['type'] = 2;
563 $taskInfo['frequency'] = (empty($taskInfo['interval'])) ? $taskInfo['croncmd'] : $taskInfo['interval'];
564 } else {
565 // It's not a recurring task
566 // Make sure interval and cron command are both empty
567 $taskInfo['type'] = 1;
568 $taskInfo['frequency'] = '';
569 $taskInfo['end'] = 0;
570 }
571 } catch (OutOfBoundsException $e) {
572 // Add a message and continue throwing the exception
573 $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $this->submittedData['uid']), t3lib_FlashMessage::ERROR);
574 throw $e;
575 }
576 } else {
577 // If adding a new object, set some default values
578 $taskInfo['class'] = key($registeredClasses);
579 $taskInfo['type'] = 2;
580 $taskInfo['start'] = $GLOBALS['EXEC_TIME'];
581 $taskInfo['end'] = '';
582 $taskInfo['frequency'] = '';
583 $taskInfo['multiple'] = 0;
584 }
585
586 if (count($this->submittedData) > 0) {
587 // If some data was already submitted, use it to override
588 // existing data
589 $taskInfo = t3lib_div::array_merge_recursive_overrule($taskInfo, $this->submittedData);
590 }
591
592 // Get the extra fields to display for each task that needs some
593 $allAdditionalFields = array();
594 foreach ($registeredClasses as $class => $registrationInfo) {
595 if (!empty($registrationInfo['provider'])) {
596 $providerObject = t3lib_div::getUserObj($registrationInfo['provider']);
597 if ($providerObject instanceof tx_scheduler_AdditionalFieldProvider) {
598 $additionalFields = $providerObject->getAdditionalFields($taskInfo, isset($task) ? $task : null, $this);
599 $allAdditionalFields = array_merge($allAdditionalFields, array($class => $additionalFields));
600 }
601 }
602 }
603
604 // Load necessary JavaScript
605 /** @var $pageRenderer t3lib_PageRenderer */
606 $pageRenderer = $this->doc->getPageRenderer();
607 $pageRenderer->loadExtJS();
608 $pageRenderer->addJsFile(t3lib_extMgm::extRelPath('scheduler') . 'res/tx_scheduler_be.js');
609 $pageRenderer->addJsFile($this->backPath . '../t3lib/js/extjs/tceforms.js');
610
611 // Define settings for Date Picker
612 $typo3Settings = array(
613 'datePickerUSmode' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? 1 : 0,
614 'dateFormat' => array('j-n-Y', 'G:i j-n-Y'),
615 'dateFormatUS' => array('n-j-Y', 'G:i n-j-Y'),
616 );
617 $pageRenderer->addInlineSettingArray('', $typo3Settings);
618
619 // Define table layout for add/edit form
620 $tableLayout = array (
621 'table' => array ('<table border="0" cellspacing="1" cellpadding="0" id="edit_form">', '</table>'),
622 );
623
624 // Define a style for hiding
625 // Some fields will be hidden when the task is not recurring
626 if ($taskInfo['type'] == 1) {
627 $style = ' style="display: none"';
628 } else {
629 $style = '';
630 }
631
632 // Start rendering the add/edit form
633 $content .= '<input type="hidden" name="tx_scheduler[uid]" value="' . $this->submittedData['uid'] . '" />';
634 $content .= '<input type="hidden" name="previousCMD" value="' . $this->CMD . '" />';
635 $content .= '<input type="hidden" name="CMD" value="save" />';
636
637 $table = array();
638 $tr = 0;
639 $defaultCell = array('<td class="bgColor5 cell">', '</td>');
640
641 // Disable checkbox
642 $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_disable', $this->backPath, '|', false, 'margin-bottom:0px;');
643 $table[$tr][] = '<label for="task_disable">' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:disable') . '</lang>';
644 $table[$tr][] =
645 '<input type="hidden" name="tx_scheduler[disable]" value="0" />
646 <input type="checkbox" name="tx_scheduler[disable]" value="1" id="task_disable"' . ($taskInfo['disable'] == 1 ? ' checked="checked"' : '') . ' />';
647 $tableLayout[$tr] = array (
648 'tr' => array('<tr id="task_disable_row">', '</tr>'),
649 'defCol' => $defaultCell
650 );
651 $tr++;
652
653 // Task class selector
654 $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_class', $this->backPath, '|', false, 'margin-bottom:0px;');
655 $table[$tr][] = '<label for="task_class">'.$GLOBALS['LANG']->getLL('label.class').'</label>';
656 // On editing, don't allow changing of the task class, unless it was not valid
657 if ($this->submittedData['uid'] > 0 && !empty($taskInfo['class'])) {
658 $cell = $registeredClasses[$taskInfo['class']]['title'] . ' (' . $registeredClasses[$taskInfo['class']]['extension'] . ')';
659 $cell .= '<input type="hidden" name="tx_scheduler[class]" id="task_class" value="' . $taskInfo['class'] . '" />';
660 } else {
661 $cell = '<select name="tx_scheduler[class]" id="task_class" class="wide" onchange="actOnChangedTaskClass(this)">';
662 // Loop on all registered classes to display a selector
663 foreach ($registeredClasses as $class => $classInfo) {
664 $selected = ($class == $taskInfo['class']) ? ' selected="selected"' : '';
665 $cell .= '<option value="' . $class . '"' . $selected . '>' . $classInfo['title'] . ' (' . $classInfo['extension'] . ')' . '</option>';
666 }
667 $cell .= '</select>';
668 }
669 $table[$tr][] = $cell;
670 // Make sure each row has a unique id, for manipulation with JS
671 $tableLayout[$tr] = array (
672 'tr' => array('<tr id="task_class_row">', '</tr>'),
673 'defCol' => $defaultCell
674 );
675 $tr++;
676
677 // Task type selector
678 $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_type', $this->backPath, '|', false, 'margin-bottom:0px;');
679 $table[$tr][] = '<label for="task_type">' . $GLOBALS['LANG']->getLL('label.type') . '</label>';
680 $table[$tr][] =
681 '<select name="tx_scheduler[type]" id="task_type" onchange="actOnChangedTaskType(this)">' .
682 '<option value="1"' . ($taskInfo['type'] == 1 ? ' selected="selected"' : '') . '>' . $GLOBALS['LANG']->getLL('label.type.single') . '</option>' .
683 '<option value="2"' . ($taskInfo['type'] == 2 ? ' selected="selected"' : '') . '>' . $GLOBALS['LANG']->getLL('label.type.recurring') . '</option>' .
684 '</select>';
685 $tableLayout[$tr] = array (
686 'tr' => array('<tr id="task_type_row">', '</tr>'),
687 'defCol' => $defaultCell
688 );
689 $tr++;
690
691 // Start date/time field
692 // NOTE: datetime fields need a special id naming scheme
693 $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_start', $this->backPath, '|', false, 'margin-bottom:0px;');
694 $table[$tr][] = '<label for="task_start">' . $GLOBALS['LANG']->getLL('label.start') . '</lang>';
695 $table[$tr][] = '<input name="tx_scheduler[start]" type="text" id="tceforms-datetimefield-task_start" value="' . strftime('%H:%M %d-%m-%Y', $taskInfo['start']) . '" />' .
696 '<img' . t3lib_iconWorks::skinImg($this->backPath, 'gfx/datepicker.gif', '', 0) . ' style="cursor:pointer; vertical-align:middle;" alt=""' . ' id="picker-tceforms-datetimefield-task_start" />';;
697 $tableLayout[$tr] = array (
698 'tr' => array('<tr id="task_start_row">', '</tr>'),
699 'defCol' => $defaultCell
700 );
701 $tr++;
702
703 // End date/time field
704 // NOTE: datetime fields need a special id naming scheme
705 $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_end', $this->backPath, '|', false, 'margin-bottom:0px;');
706 $table[$tr][] = '<label for="task_end">' . $GLOBALS['LANG']->getLL('label.end') . '</lang>';
707 $table[$tr][] = '<input name="tx_scheduler[end]" type="text" id="tceforms-datetimefield-task_end" value="' . ((empty($taskInfo['end'])) ? '' : strftime('%H:%M %d-%m-%Y', $taskInfo['end'])) . '" />' .
708 '<img' . t3lib_iconWorks::skinImg($this->backPath, 'gfx/datepicker.gif', '', 0) . ' style="cursor:pointer; vertical-align:middle;" alt=""' . ' id="picker-tceforms-datetimefield-task_end" />';
709 $tableLayout[$tr] = array (
710 'tr' => array('<tr id="task_end_row"' . $style . '>', '</tr>'),
711 'defCol' => $defaultCell
712 );
713 $tr++;
714
715 // Frequency input field
716 $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_frequency', $this->backPath, '|', false, 'margin-bottom:0px;');
717 $table[$tr][] = '<label for="task_frequency">' . $GLOBALS['LANG']->getLL('label.frequency.long') . '</label>';
718 $cell = '<input type="text" name="tx_scheduler[frequency]" id="task_frequency" value="' . $taskInfo['frequency'] . '" />';
719 $table[$tr][] = $cell;
720 $tableLayout[$tr] = array (
721 'tr' => array('<tr id="task_frequency_row"' . $style . '>', '</tr>'),
722 'defCol' => $defaultCell
723 );
724 $tr++;
725
726 // Multiple execution selector
727 $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_multiple', $this->backPath, '|', false, 'margin-bottom:0px;');
728 $table[$tr][] = '<label for="task_multiple">' . $GLOBALS['LANG']->getLL('label.parallel.long') . '</lang>';
729 $table[$tr][] =
730 '<input type="hidden" name="tx_scheduler[multiple]" value="0" />
731 <input type="checkbox" name="tx_scheduler[multiple]" value="1" id="task_multiple"' . ($taskInfo['multiple'] == 1 ? ' checked="checked"' : '') . ' />';
732 $tableLayout[$tr] = array (
733 'tr' => array('<tr id="task_multiple_row"' . $style . '>', '</tr>'),
734 'defCol' => $defaultCell
735 );
736 $tr++;
737
738 // Display additional fields
739 foreach ($allAdditionalFields as $class => $fields) {
740 if ($class == $taskInfo['class']) {
741 $additionalFieldsStyle = '';
742 } else {
743 $additionalFieldsStyle = ' style="display: none"';
744 }
745
746 // Add each field to the display, if there are indeed any
747 if (isset($fields) && is_array($fields)) {
748 foreach ($fields as $fieldID => $fieldInfo) {
749 $table[$tr][] = t3lib_BEfunc::cshItem($fieldInfo['cshKey'], $fieldInfo['cshLabel'], $this->backPath, '|', false, 'margin-bottom:0px;');
750 $table[$tr][] = '<label for="' . $fieldID . '">' . $GLOBALS['LANG']->sL($fieldInfo['label']) . '</label>';
751 $table[$tr][] = $fieldInfo['code'];
752 $tableLayout[$tr] = array (
753 'tr' => array('<tr id="' . $fieldID . '_row"' . $additionalFieldsStyle .' class="extraFields extra_fields_' . $class . '">', '</tr>'),
754 'defCol' => $defaultCell
755 );
756 $tr++;
757 }
758 }
759 }
760
761 // Render the add/edit task form
762 $content .= $this->doc->table($table, $tableLayout);
763
764 $content .= '<input type="submit" name="save" class="button" value="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:save') . '" /> '
765 . '<input type="button" name="cancel" class="button" value="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:cancel') . '" onclick="document.location=\'' . $GLOBALS['MCONF']['_'] . '\'" />';
766
767 return $content;
768 }
769
770 /**
771 * Execute all selected tasks
772 *
773 * @return void
774 */
775 protected function executeTasks() {
776 // Continue if some elements have been chosen for execution
777 if (isset($this->submittedData['execute']) && count($this->submittedData['execute']) > 0) {
778
779 // Get list of registered classes
780 $registeredClasses = self::getRegisteredClasses();
781
782 // Loop on all selected tasks
783 foreach ($this->submittedData['execute'] as $uid) {
784
785 try {
786 // Try fetching the task
787 $task = $this->scheduler->fetchTask($uid);
788 $class = get_class($task);
789 $name = $registeredClasses[$class]['title']. ' (' . $registeredClasses[$class]['extension'] . ')';
790 // Now try to execute it and report on outcome
791 try {
792 $result = $this->scheduler->executeTask($task);
793 if ($result) {
794 $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.executed'), $name));
795 } else {
796 $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.notExecuted'), $name), t3lib_FlashMessage::ERROR);
797 }
798 }
799 catch (Exception $e) {
800 // An exception was thrown, display its message as an error
801 $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.executionFailed'), $name, $e->getMessage()), t3lib_FlashMessage::ERROR);
802 }
803 }
804 // The task was not found, for some reason
805 catch (OutOfBoundsException $e) {
806 $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $uid), t3lib_FlashMessage::ERROR);
807 }
808 }
809 // Record the run in the system registry
810 $this->scheduler->recordLastRun('manual');
811 // Make sure to switch to list view after execution
812 $this->CMD = 'list';
813 }
814 }
815
816 /**
817 * Assemble display of list of scheduled tasks
818 *
819 * @return string table of waiting schedulings
820 */
821 protected function listTasks() {
822 // Define display format for dates
823 $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
824 $content = '';
825
826 // Get all registered tasks
827 $query = array(
828 'SELECT' => '*',
829 'FROM' => 'tx_scheduler_task',
830 'ORDERBY' => 'nextexecution'
831 );
832
833 $res = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($query);
834 $numRows = $GLOBALS['TYPO3_DB']->sql_num_rows($res);
835 if ($numRows == 0) {
836 $content .= '<p>' . $GLOBALS['LANG']->getLL('msg.noTasks') . '</p>';
837 } else {
838 // Get list of registered classes
839 $registeredClasses = self::getRegisteredClasses();
840
841 // Load ExtJS framework and specific JS library
842 /** @var $pageRenderer t3lib_PageRenderer */
843 $pageRenderer = $this->doc->getPageRenderer();
844 $pageRenderer->loadExtJS();
845 $pageRenderer->addJsFile(t3lib_extMgm::extRelPath('scheduler') . 'res/tx_scheduler_be.js');
846
847 // Initialise table layout
848 $tableLayout = array(
849 'table' => array(
850 '<table border="0" cellspacing="1" cellpadding="2" class="tx_scheduler_task_list">', '</table>'
851 ),
852 '0' => array(
853 'tr' => array('<tr class="bgColor2" valign="top">', '</tr>'),
854 'defCol' => array('<td class="cell">', '</td>'),
855 '1' => array('<td style="width: 36px;" class="cell">', '</td>')
856 ),
857 'defRow' => array(
858 'tr' => array('<tr class="bgColor3-20">', '</tr>'),
859 'defCol' => array('<td class="cell">', '</td>'),
860 '2' => array('<td class="cell right">', '</td>'),
861 )
862 );
863 $disabledTaskRow = array (
864 'tr' => array('<tr class="bgColor3-20 disabled">', '</tr>'),
865 'defCol' => array('<td class="cell">', '</td>'),
866 '2' => array('<td class="cell right">', '</td>'),
867 );
868 $table = array();
869 $tr = 0;
870
871 // Header row
872 $table[$tr][] = '<a href="#" onclick="toggleCheckboxes();" title="' . $GLOBALS['LANG']->getLL('label.checkAll') . '"><img ' . t3lib_iconWorks::skinImg($this->backPath, 'gfx/clip_select.gif') . ' alt="' . $GLOBALS['LANG']->getLL('label.checkAll') . '" /></a>';
873 $table[$tr][] = '&nbsp;';
874 $table[$tr][] = $GLOBALS['LANG']->getLL('label.id');
875 $table[$tr][] = $GLOBALS['LANG']->getLL('task');
876 $table[$tr][] = $GLOBALS['LANG']->getLL('label.type');
877 $table[$tr][] = $GLOBALS['LANG']->getLL('label.frequency');
878 $table[$tr][] = $GLOBALS['LANG']->getLL('label.parallel');
879 $table[$tr][] = $GLOBALS['LANG']->getLL('label.lastExecution');
880 $table[$tr][] = $GLOBALS['LANG']->getLL('label.nextExecution');
881 $tr++;
882
883 // Loop on all tasks
884 while (($schedulerRecord = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
885 // Restore the serialized task and pass it a reference to the scheduler object
886 $task = $task = unserialize($schedulerRecord['serialized_task_object']);
887 // Set default execution status
888 $executionStatus = 'scheduled';
889 $executionStatusDetail = '';
890
891 $name = $registeredClasses[$schedulerRecord['classname']]['title']. ' (' . $registeredClasses[$schedulerRecord['classname']]['extension'] . ')';
892 $additionalInformation = $task->getAdditionalInformation();
893 if (!empty($additionalInformation)) {
894 $name .= ' [' . $additionalInformation . ']';
895 }
896
897 // Assemble information about last execution
898 if (!empty($schedulerRecord['lastexecution_time'])) {
899 $lastExecution = date($dateFormat, $schedulerRecord['lastexecution_time']);
900 if ($schedulerRecord['lastexecution_context'] == 'CLI') {
901 $context = $GLOBALS['LANG']->getLL('label.cron');
902 } else {
903 $context = $GLOBALS['LANG']->getLL('label.manual');
904 }
905 $lastExecution .= ' (' . $context . ')';
906 } else {
907 $lastExecution = '-';
908 }
909
910 // Check if task currently has a running execution
911 if (empty($schedulerRecord['serialized_executions'])) {
912 $isRunning = false;
913 } else {
914 $isRunning = true;
915 $executionStatus = 'running';
916 }
917
918 // Prepare display of next execution date
919 // If task is currently running, date is not displayed (as next hasn't been calculated yet)
920 // Also hide the date if task is disabled (the information doesn't make sense, as it will not run anyway)
921 if ($isRunning || $schedulerRecord['disable'] == 1) {
922 $nextDate = '-';
923 }
924 else {
925 $nextDate = date($dateFormat, $schedulerRecord['nextexecution']);
926 if (empty($schedulerRecord['nextexecution'])) {
927 $nextDate = $GLOBALS['LANG']->getLL('none');
928 } elseif ($schedulerRecord['nextexecution'] < $GLOBALS['EXEC_TIME']) {
929 // Next execution is overdue, highlight date
930 $nextDate = '<span class="late" title="' . $GLOBALS['LANG']->getLL('status.legend.scheduled') . '">' . $nextDate . '</span>';
931 $executionStatus = 'late';
932 }
933 }
934
935 // Get execution type
936 if ($task->getExecution()->getInterval() == 0 && $task->getExecution()->getCronCmd() == '') {
937 $execType = $GLOBALS['LANG']->getLL('label.type.single');
938 $frequency = '-';
939 } else {
940 $execType = $GLOBALS['LANG']->getLL('label.type.recurring');
941 if ($task->getExecution()->getCronCmd() == '') {
942 $frequency = $task->getExecution()->getInterval();
943 } else {
944 $frequency = $task->getExecution()->getCronCmd();
945 }
946 }
947
948 // Get multiple executions setting
949 if ($task->getExecution()->getMultiple()) {
950 $multiple = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:yes');
951 } else {
952 $multiple = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:no');
953 }
954
955 // Define checkbox
956 $startExecutionElement = '<input type="checkbox" name="tx_scheduler[execute][]" value="' . $schedulerRecord['uid'] . '" id="task_' . $schedulerRecord['uid'] . '" class="checkboxes" />';
957
958 // Assemble links for actions (edit, delete),
959 // only if task is not running
960 if ($isRunning) {
961 $actions = '&nbsp;';
962 } else {
963 $actions = '<a href="' . $GLOBALS['MCONF']['_'] . '&CMD=edit&tx_scheduler[uid]=' . $schedulerRecord['uid'] . '" title="'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:edit') . '"><img ' . t3lib_iconWorks::skinImg($this->backPath, 'gfx/edit2.gif') . ' alt="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:edit') . '" /></a> ';
964 $deleteLink = $GLOBALS['MCONF']['_'] . '&CMD=delete&tx_scheduler[uid]=' . $schedulerRecord['uid'];
965 $actions .= '<a href="' . $deleteLink . '" onclick="return confirm(\'' . $GLOBALS['LANG']->getLL('msg.delete') . '\');" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:delete') . '"><img ' . t3lib_iconWorks::skinImg($this->backPath, 'gfx/garbage.gif') . ' alt="'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:delete') . '" /></a>';
966 }
967
968 // Check the disable status
969 if ($schedulerRecord['disable'] == 1) {
970 $tableLayout[$tr] = $disabledTaskRow;
971 $executionStatus = 'disabled';
972 }
973
974 // A failure is the worst thing that could happen, so it must overwrite all other statuses
975 if (!empty($schedulerRecord['lastexecution_failure'])) {
976 $exception = unserialize($schedulerRecord['lastexecution_failure']);
977
978 $executionStatus = 'failure';
979 $executionStatusDetail = sprintf($GLOBALS['LANG']->getLL('msg.executionFailureReport'), $exception->getCode(), $exception->getMessage());
980 }
981
982
983 // Format the execution status
984 $executionStatus = '<img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_' . $executionStatus . '.png') . ' alt="' . $GLOBALS['LANG']->getLL('status.' . $executionStatus) . '" title="' . $executionStatusDetail . '" />';
985
986 $table[$tr][] = $startExecutionElement;
987 $table[$tr][] = $actions;
988 $table[$tr][] = $schedulerRecord['uid'];
989 $table[$tr][] = $executionStatus . ' ' . $name;
990 $table[$tr][] = $execType;
991 $table[$tr][] = $frequency;
992 $table[$tr][] = $multiple;
993 $table[$tr][] = $lastExecution;
994 $table[$tr][] = $nextDate;
995
996 $tr++;
997 }
998 // Render table
999 $content .= $this->doc->table($table, $tableLayout);
1000
1001 $content .= '<input type="submit" class="button" name="go" value="' . $GLOBALS['LANG']->getLL('label.executeSelected') . '" />';
1002 }
1003
1004 // Display add new task link
1005 $link = $GLOBALS['MCONF']['_'] . '&CMD=add';
1006 $content .= '<p><a href="' . $link .'"><img '
1007 . t3lib_iconWorks::skinImg($this->backPath, 'gfx/new_el.gif')
1008 . ' alt="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:new')
1009 . '" /> ' . $GLOBALS['LANG']->getLL('action.add') . '</a></p>';
1010
1011 // Display legend, if there's at least one registered task
1012 if ($numRows > 0) {
1013 $content .= '<p class="status-legend">' . $GLOBALS['LANG']->getLL('status.legend') . ':</p>
1014 <ul>
1015 <li><img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_failure.png') . ' alt="' . $GLOBALS['LANG']->getLL('status.failure') . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.failure') . '</li>
1016 <li><img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_late.png') . ' alt="' . $GLOBALS['LANG']->getLL('status.late') . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.late') . '</li>
1017 <li><img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_running.png') . ' alt="' . $GLOBALS['LANG']->getLL('status.running') . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.running') . '</li>
1018 <li><img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_scheduled.png') . ' alt="' . $GLOBALS['LANG']->getLL('status.scheduled') . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.scheduled') . '</li>
1019 <li><img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_disabled.png') . ' alt="' . $GLOBALS['LANG']->getLL('status.disabled') . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.disabled') . '</li>
1020 </ul>';
1021 }
1022
1023
1024 $GLOBALS['TYPO3_DB']->sql_free_result($res);
1025
1026 return $content;
1027 }
1028
1029 /**
1030 * Saves a task specified in the backend form to the database
1031 *
1032 * @return void
1033 */
1034 protected function saveTask() {
1035
1036 // If a task is being edited fetch old task data
1037 if (!empty($this->submittedData['uid'])) {
1038 try {
1039 $taskRecord = $this->scheduler->fetchTaskRecord($this->submittedData['uid']);
1040 /**
1041 * @var tx_scheduler_Task
1042 */
1043 $task = unserialize($taskRecord['serialized_task_object']);
1044 } catch (OutOfBoundsException $e) {
1045 // If the task could not be fetched, issue an error message
1046 // and exit early
1047 $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $this->submittedData['uid']), t3lib_FlashMessage::ERROR);
1048 return;
1049 }
1050
1051 // Register single execution
1052 if ($this->submittedData['type'] == 1) {
1053 $task->registerSingleExecution($this->submittedData['start']);
1054
1055 // Else, it's a recurring task
1056 } else {
1057 if (!empty($this->submittedData['croncmd'])) {
1058 // Definition by cron-like syntax
1059
1060 $interval = 0;
1061 $cronCmd = $this->submittedData['croncmd'];
1062 } else {
1063 // Definition by interval
1064
1065 $interval = $this->submittedData['interval'];
1066 $cronCmd = '';
1067 }
1068
1069 // Register recurring execution
1070 $task->registerRecurringExecution($this->submittedData['start'], $interval, $this->submittedData['end'], $this->submittedData['multiple'], $cronCmd);
1071 }
1072
1073 // Set disable flag
1074 $task->setDisabled($this->submittedData['disable']);
1075
1076 // Save additional input values
1077 if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields'])) {
1078 $providerObject = t3lib_div::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields']);
1079 if ($providerObject instanceof tx_scheduler_AdditionalFieldProvider) {
1080 $providerObject->saveAdditionalFields($this->submittedData, $task);
1081 }
1082 }
1083
1084 // Save to database
1085 $result = $this->scheduler->saveTask($task);
1086 if ($result) {
1087 $this->addMessage($GLOBALS['LANG']->getLL('msg.updateSuccess'));
1088 } else {
1089 $this->addMessage($GLOBALS['LANG']->getLL('msg.updateError'), t3lib_FlashMessage::ERROR);
1090 }
1091 } else {
1092 // A new task is being created
1093
1094 // Create an instance of chosen class
1095 $task = t3lib_div::makeInstance($this->submittedData['class']);
1096
1097 if ($this->submittedData['type'] == 1) {
1098 // Set up single execution
1099 $task->registerSingleExecution($this->submittedData['start']);
1100 } else {
1101 // Set up recurring execution
1102 $task->registerRecurringExecution($this->submittedData['start'], $this->submittedData['interval'], $this->submittedData['end'], $this->submittedData['multiple'], $this->submittedData['croncmd']);
1103 }
1104
1105 // Save additional input values
1106 if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields'])) {
1107 $providerObject = t3lib_div::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields']);
1108 if ($providerObject instanceof tx_scheduler_AdditionalFieldProvider) {
1109 $providerObject->saveAdditionalFields($this->submittedData, $task);
1110 }
1111 }
1112
1113 // Set disable flag
1114 $task->setDisabled($this->submittedData['disable']);
1115
1116 // Add to database
1117 $result = $this->scheduler->addTask($task);
1118 if ($result) {
1119 $this->addMessage($GLOBALS['LANG']->getLL('msg.addSuccess'));
1120 } else {
1121 $this->addMessage($GLOBALS['LANG']->getLL('msg.addError'), t3lib_FlashMessage::ERROR);
1122 }
1123 }
1124 }
1125
1126
1127 /*************************
1128 *
1129 * INPUT PROCESSING UTILITIES
1130 *
1131 *************************/
1132
1133 /**
1134 * Checks the submitted data and performs some preprocessing on it
1135 *
1136 * @return boolean True if everything was ok, false otherwise
1137 */
1138 protected function preprocessData() {
1139 $result = true;
1140
1141 // Validate id
1142 $this->submittedData['uid'] = (empty($this->submittedData['uid'])) ? 0 : intval($this->submittedData['uid']);
1143
1144 // Validate selected task class
1145 if (!class_exists($this->submittedData['class'])) {
1146 $this->addMessage($GLOBALS['LANG']->getLL('msg.noTaskClassFound'), t3lib_FlashMessage::ERROR);
1147 }
1148
1149 // Check start date
1150 if (empty($this->submittedData['start'])) {
1151 $this->addMessage($GLOBALS['LANG']->getLL('msg.noStartDate'), t3lib_FlashMessage::ERROR);
1152 $result = false;
1153 } else {
1154 try {
1155 $timestamp = $this->checkDate($this->submittedData['start']);
1156 $this->submittedData['start'] = $timestamp;
1157 } catch (Exception $e) {
1158 $this->addMessage($GLOBALS['LANG']->getLL('msg.invalidStartDate'), t3lib_FlashMessage::ERROR);
1159 $result = false;
1160 }
1161 }
1162
1163 // Check end date, if recurring task
1164 if ($this->submittedData['type'] == 2 && !empty($this->submittedData['end'])) {
1165 try {
1166 $timestamp = $this->checkDate($this->submittedData['end']);
1167 $this->submittedData['end'] = $timestamp;
1168
1169 if ($this->submittedData['end'] < $this->submittedData['start']) {
1170 $this->addMessage($GLOBALS['LANG']->getLL('msg.endDateSmallerThanStartDate'), t3lib_FlashMessage::ERROR);
1171 $result = false;
1172 }
1173 } catch (Exception $e) {
1174 $this->addMessage($GLOBALS['LANG']->getLL('msg.invalidEndDate'), t3lib_FlashMessage::ERROR);
1175 $result = false;
1176 }
1177 }
1178
1179 // Set default values for interval and cron command
1180 $this->submittedData['interval'] = 0;
1181 $this->submittedData['croncmd'] = '';
1182
1183 // Check type and validity of frequency, if recurring
1184 if ($this->submittedData['type'] == 2) {
1185 $parts = t3lib_div::trimExplode(' ', $this->submittedData['frequency']);
1186 $numParts = count($parts);
1187
1188 if ($numParts == 0) {
1189 // No parts, empty frequency, not valid
1190
1191 $this->addMessage($GLOBALS['LANG']->getLL('msg.noFrequency'), t3lib_FlashMessage::ERROR);
1192 $result = false;
1193 } else if ($numParts == 1) {
1194 // One part, assume it is an interval
1195 // Make sure it has a valid value
1196 $interval = intval($this->submittedData['frequency']);
1197 if ($interval > 0) {
1198 $this->submittedData['interval'] = $interval;
1199 } else {
1200 $this->addMessage($GLOBALS['LANG']->getLL('msg.invalidFrequency'), t3lib_FlashMessage::ERROR);
1201 $result = false;
1202 }
1203 } else if ($numParts == 5) {
1204 // Five parts, assume it is a valid cron command
1205 $this->submittedData['croncmd'] = $this->submittedData['frequency'];
1206 } else {
1207 // Some other number of parts, assume it is an invalid cron command
1208
1209 $this->addMessage($GLOBALS['LANG']->getLL('msg.invalidFrequency'), t3lib_FlashMessage::ERROR);
1210 $result = false;
1211 }
1212 }
1213
1214 // Validate additional input fields
1215 if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields'])) {
1216 $providerObject = t3lib_div::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields']);
1217 if ($providerObject instanceof tx_scheduler_AdditionalFieldProvider) {
1218 // The validate method will return true if all went well, but that must not
1219 // override previous false values => AND the returned value with the existing one
1220 $result &= $providerObject->validateAdditionalFields($this->submittedData, $this);
1221 }
1222 }
1223
1224 return $result;
1225 }
1226
1227 /**
1228 * This method checks whether the given string can be considered a valid date or not
1229 * Allowed values are anything that matches natural language (see PHP function strtotime())
1230 * or TYPO3's date syntax: HH:ii yyyy-mm-dd
1231 * If the string is a valid date, the corresponding timestamp is returned.
1232 * Otherwise an exception is thrown
1233 *
1234 * @param string $string: string to check
1235 * @return integer Unix timestamp
1236 */
1237 protected function checkDate($string) {
1238 // Try with strtotime
1239 $timestamp = strtotime($string);
1240
1241 // That failed. Try TYPO3's standard date/time input format
1242 if ($timestamp === false) {
1243 // Split time and date
1244 $dateParts = t3lib_div::trimExplode(' ', $string, true);
1245 // Proceed if there are indeed two parts
1246 // Extract each component of date and time
1247 if (count($dateParts) == 2) {
1248 list($time, $date) = $dateParts;
1249 list($hour, $minutes) = t3lib_div::trimExplode(':', $time, true);
1250 list($day, $month, $year) = t3lib_div::trimExplode('-', $date, true);
1251 // Get a timestamp from all these parts
1252 $timestamp = mktime($hour, $minutes, 0, $month, $day, $year);
1253 }
1254 // If the timestamp is still false, throw an exception
1255 if ($timestamp === false) {
1256 throw new Exception;
1257 }
1258 }
1259 return $timestamp;
1260 }
1261
1262 /*************************
1263 *
1264 * APPLICATION LOGIC UTILITIES
1265 *
1266 *************************/
1267
1268 /**
1269 * This method is used to add a message to the internal queue
1270 *
1271 * @param string the message itself
1272 * @param integer message level (-1 = success (default), 0 = info, 1 = notice, 2 = warning, 3 = error)
1273 * @return void
1274 */
1275 public function addMessage($message, $severity = t3lib_FlashMessage::OK) {
1276 $message = t3lib_div::makeInstance(
1277 't3lib_FlashMessage',
1278 $message,
1279 '',
1280 $severity
1281 );
1282
1283 t3lib_messageQueue::addMessage($message);
1284 }
1285
1286 /**
1287 * This method a list of all classes that have been registered with the Scheduler
1288 * For each item the following information is provided, as an associative array:
1289 *
1290 * ['extension'] => Key of the extension which provides the class
1291 * ['filename'] => Path to the file containing the class
1292 * ['title'] => String (possibly localized) containing a human-readable name for the class
1293 * ['provider'] => Name of class that implements the interface for additional fields, if necessary
1294 *
1295 * The name of the class itself is used as the key of the list array
1296 *
1297 * @return array List of registered classes
1298 */
1299 protected static function getRegisteredClasses() {
1300 $list = array();
1301 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'])) {
1302 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'] as $class => $registrationInformation) {
1303
1304 $title = isset($registrationInformation['title']) ? $GLOBALS['LANG']->sL($registrationInformation['title']) : '';
1305 $description = isset($registrationInformation['description']) ? $GLOBALS['LANG']->sL($registrationInformation['description']) : '';
1306
1307 $list[$class] = array(
1308 'extension' => $registrationInformation['extension'],
1309 'title' => $title,
1310 'description' => $description,
1311 'provider' => isset($registrationInformation['additionalFields']) ? $registrationInformation['additionalFields'] : ''
1312 );
1313 }
1314 }
1315
1316 return $list;
1317 }
1318
1319
1320 /*************************
1321 *
1322 * RENDERING UTILITIES
1323 *
1324 *************************/
1325
1326 /**
1327 * Gets the filled markers that are used in the HTML template.
1328 *
1329 * @return array The filled marker array
1330 */
1331 protected function getTemplateMarkers() {
1332 $markers = array(
1333 'FUNC_MENU' => $this->getFunctionMenu(),
1334 'CONTENT' => $this->content,
1335 'TITLE' => $GLOBALS['LANG']->getLL('title'),
1336 );
1337
1338 return $markers;
1339 }
1340
1341 /**
1342 * Gets the function menu selector for this backend module.
1343 *
1344 * @return string The HTML representation of the function menu selector
1345 */
1346 protected function getFunctionMenu() {
1347 $functionMenu = t3lib_BEfunc::getFuncMenu(
1348 0,
1349 'SET[function]',
1350 $this->MOD_SETTINGS['function'],
1351 $this->MOD_MENU['function']
1352 );
1353
1354 return $functionMenu;
1355 }
1356
1357 /**
1358 * Gets the buttons that shall be rendered in the docHeader.
1359 *
1360 * @return array Available buttons for the docHeader
1361 */
1362 protected function getDocHeaderButtons() {
1363 $buttons = array(
1364 'csh' => t3lib_BEfunc::cshItem('_MOD_tools_txschedulerM1', '', $this->backPath),
1365 'reload' => '',
1366 'shortcut' => $this->getShortcutButton(),
1367 );
1368
1369 if (empty($this->CMD) || $this->CMD == 'list') {
1370 $buttons['reload'] = '<a href="' . $GLOBALS['MCONF']['_'] . '">' .
1371 '<img' . t3lib_iconWorks::skinImg($this->backPath, 'gfx/refresh_n.gif') . ' title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.reload', 1) . '" alt="" />' .
1372 '</a>';
1373 }
1374
1375 return $buttons;
1376 }
1377
1378 /**
1379 * Gets the button to set a new shortcut in the backend (if current user is allowed to).
1380 *
1381 * @return string HTML representiation of the shortcut button
1382 */
1383 protected function getShortcutButton() {
1384 $result = '';
1385 if ($GLOBALS['BE_USER']->mayMakeShortcut()) {
1386 $result = $this->doc->makeShortcutIcon('', 'function', $this->MCONF['name']);
1387 }
1388
1389 return $result;
1390 }
1391 }
1392
1393
1394 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/mod1/index.php']) {
1395 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/mod1/index.php']);
1396 }
1397
1398
1399
1400
1401 // Make instance:
1402 $SOBE = t3lib_div::makeInstance('tx_scheduler_Module');
1403 $SOBE->init();
1404
1405 // Include files?
1406 foreach($SOBE->include_once as $INC_FILE) {
1407 include_once($INC_FILE);
1408 }
1409
1410 $SOBE->main();
1411 $SOBE->render();
1412
1413 ?>