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