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