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