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