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