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