‪TYPO3CMS  11.5
SchedulerModuleController.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\Http\Message\ResponseInterface;
21 use Psr\Http\Message\ServerRequestInterface;
26 use TYPO3\CMS\Backend\Utility\BackendUtility;
36 use TYPO3\CMS\Core\Page\PageRenderer;
38 use ‪TYPO3\CMS\Core\SysLog\Action\Database as SystemLogDatabaseAction;
39 use ‪TYPO3\CMS\Core\SysLog\Error as SystemLogErrorClassification;
40 use ‪TYPO3\CMS\Core\SysLog\Type as SystemLogType;
52 
58 {
64  protected ‪$submittedData = [];
65 
72  protected ‪$messages = [];
73 
77  protected ‪$cshKey = '_MOD_system_txschedulerM1';
78 
82  protected ‪$backendTemplatePath = '';
83 
87  protected ‪$view;
88 
92  protected ‪$moduleUri;
93 
99  protected ‪$moduleTemplate;
100 
104  protected ‪$action;
105 
111  protected ‪$MOD_MENU = [
112  'function' => [],
113  ];
114 
120  protected ‪$MOD_SETTINGS = [];
121 
122  protected ‪Scheduler ‪$scheduler;
124  protected PageRenderer ‪$pageRenderer;
125  protected ‪UriBuilder ‪$uriBuilder;
127 
128  public function ‪__construct(
131  PageRenderer ‪$pageRenderer,
134  ) {
135  $this->scheduler = ‪$scheduler;
136  $this->iconFactory = ‪$iconFactory;
137  $this->pageRenderer = ‪$pageRenderer;
138  $this->uriBuilder = ‪$uriBuilder;
139  $this->moduleTemplateFactory = ‪$moduleTemplateFactory;
140  $this->‪getLanguageService()->‪includeLLFile('EXT:scheduler/Resources/Private/Language/locallang.xlf');
141  $this->backendTemplatePath = ‪ExtensionManagementUtility::extPath('scheduler') . 'Resources/Private/Templates/Backend/SchedulerModule/';
142  $this->view = GeneralUtility::makeInstance(StandaloneView::class);
143  $this->view->getRequest()->setControllerExtensionName('scheduler');
144  $this->view->setPartialRootPaths(['EXT:scheduler/Resources/Private/Partials/Backend/SchedulerModule/']);
145  $this->moduleUri = (string)$this->uriBuilder->buildUriFromRoute('system_txschedulerM1');
146  }
147 
154  public function ‪mainAction(ServerRequestInterface $request): ResponseInterface
155  {
156  $this->moduleTemplate = $this->moduleTemplateFactory->create($request);
157  $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Modal');
158  $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/MultiRecordSelection');
159  $parsedBody = $request->getParsedBody();
160  $queryParams = $request->getQueryParams();
161 
162  $this->‪setCurrentAction(‪Action::cast($parsedBody['CMD'] ?? $queryParams['CMD'] ?? null));
163  $this->MOD_MENU = [
164  'function' => [
165  'scheduler' => $this->‪getLanguageService()->‪getLL('function.scheduler'),
166  'check' => $this->‪getLanguageService()->‪getLL('function.check'),
167  'info' => $this->‪getLanguageService()->‪getLL('function.info'),
168  ],
169  ];
170  $settings = $parsedBody['SET'] ?? $queryParams['SET'] ?? null;
171  $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, $settings, 'system_txschedulerM1', '', '', '');
172 
173  $previousCMD = ‪Action::cast($parsedBody['previousCMD'] ?? $queryParams['previousCMD'] ?? null);
174  // Prepare main content
175  $content = $this->‪getModuleContent($previousCMD, $request->getAttribute('normalizedParams')->getRequestUri());
176 
177  $this->view->setTemplatePathAndFilename(
178  GeneralUtility::getFileAbsFileName('EXT:scheduler/Resources/Private/Templates/Backend/SchedulerModule/Index.html')
179  );
180  $this->view->assignMultiple([
181  'headline' => $this->‪getLanguageService()->getLL('function.' . $this->MOD_SETTINGS['function']),
182  'sectionTitle' => $this->‪getSectionTitle(),
183  'content' => $content,
184  ]);
185 
186  $this->‪getButtons($request);
187  $this->‪getModuleMenu();
188 
189  $this->moduleTemplate->setContent($this->view->render());
190  $this->moduleTemplate->setTitle(
191  $this->‪getLanguageService()->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang_mod.xlf:mlang_tabs_tab'),
192  $this->‪getLanguageService()->getLL('function.' . $this->MOD_SETTINGS['function'])
193  );
194  return new ‪HtmlResponse($this->moduleTemplate->renderContent());
195  }
196 
202  public function ‪getCurrentAction(): ‪Action
203  {
204  return ‪$this->action;
205  }
206 
210  protected function ‪getModuleMenu(): void
211  {
212  $menu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
213  $menu->setIdentifier('SchedulerJumpMenu');
214  foreach ($this->MOD_MENU['function'] as $controller => $title) {
215  $item = $menu
216  ->makeMenuItem()
217  ->setHref(
218  (string)$this->uriBuilder->buildUriFromRoute(
219  'system_txschedulerM1',
220  [
221  'id' => 0,
222  'SET' => [
223  'function' => $controller,
224  ],
225  ]
226  )
227  )
228  ->setTitle($title);
229  if ($controller === $this->MOD_SETTINGS['function']) {
230  $item->setActive(true);
231  }
232  $menu->addMenuItem($item);
233  }
234  $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
235  }
236 
244  protected function ‪getModuleContent(‪Action $previousAction, string $requestUri): string
245  {
246  $content = '';
247  // Get submitted data
248  $this->submittedData = GeneralUtility::_GPmerged('tx_scheduler');
249  $this->submittedData['uid'] = (int)($this->submittedData['uid'] ?? 0);
250  // If a save command was submitted, handle saving now
251  if (in_array((string)$this->‪getCurrentAction(), [‪Action::SAVE, ‪Action::SAVE_CLOSE, ‪Action::SAVE_NEW], true)) {
252  // First check the submitted data
253  $result = $this->‪preprocessData();
254 
255  // If result is ok, proceed with saving
256  if ($result) {
257  $this->‪saveTask();
258 
259  if ($this->action->equals(‪Action::SAVE_CLOSE)) {
260  // Display default screen
262  } elseif ($this->action->equals(‪Action::SAVE)) {
263  // After saving a "add form", return to edit
265  } elseif ($this->action->equals(‪Action::SAVE_NEW)) {
266  // Unset submitted data, so that empty form gets displayed
267  unset($this->submittedData);
268  // After saving a "add/edit form", return to add
270  } else {
271  // Return to edit form
272  $this->‪setCurrentAction($previousAction);
273  }
274  } else {
275  $this->‪setCurrentAction($previousAction);
276  }
277  }
278 
279  // Handle chosen action
280  switch ((string)$this->MOD_SETTINGS['function']) {
281  case 'scheduler':
282  $this->‪executeTasks();
283 
284  switch ((string)$this->‪getCurrentAction()) {
285  case ‪Action::ADD:
286  case ‪Action::EDIT:
287  try {
288  // Try adding or editing
289  $content .= $this->‪editTaskAction($requestUri);
290  } catch (\LogicException|\UnexpectedValueException|\OutOfBoundsException $e) {
291  // Catching all types of exceptions that were previously handled and
292  // converted to messages
293  $content .= $this->‪listTasksAction();
294  } catch (\Exception $e) {
295  // Catching all "unexpected" exceptions not previously handled
296  $this->‪addMessage($e->getMessage(), ‪FlashMessage::ERROR);
297  $content .= $this->‪listTasksAction();
298  }
299  break;
300  case ‪Action::DELETE:
301  $this->‪deleteTask();
302  $content .= $this->‪listTasksAction();
303  break;
304  case ‪Action::STOP:
305  $this->‪stopTask();
306  $content .= $this->‪listTasksAction();
307  break;
309  $this->‪toggleDisableAction();
310  $content .= $this->‪listTasksAction();
311  break;
314  $content .= $this->‪listTasksAction();
315  break;
316  case ‪Action::LIST:
317  $content .= $this->‪listTasksAction();
318  }
319  break;
320 
321  // Setup check screen
322  case 'check':
323  // @todo move check to the report module
324  $content .= $this->‪checkScreenAction();
325  break;
326 
327  // Information screen
328  case 'info':
329  $content .= $this->‪infoScreenAction();
330  break;
331  }
332 
333  return $content;
334  }
335 
342  protected function ‪checkScreenAction(): string
343  {
344  $this->view->setTemplatePathAndFilename($this->backendTemplatePath . 'CheckScreen.html');
345 
346  // Display information about last automated run, as stored in the system registry
347  $registry = GeneralUtility::makeInstance(Registry::class);
348  $lastRun = $registry->get('tx_scheduler', 'lastRun');
349  if (!is_array($lastRun)) {
350  $message = $this->‪getLanguageService()->‪getLL('msg.noLastRun');
352  } else {
353  if (empty($lastRun['end']) || empty($lastRun['start']) || empty($lastRun['type'])) {
354  $message = $this->‪getLanguageService()->‪getLL('msg.incompleteLastRun');
356  } else {
357  $startDate = date(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], $lastRun['start']);
358  $startTime = date(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $lastRun['start']);
359  $endDate = date(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], $lastRun['end']);
360  $endTime = date(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $lastRun['end']);
361  $label = 'automatically';
362  if ($lastRun['type'] === 'manual') {
363  $label = 'manually';
364  }
365  $type = $this->‪getLanguageService()->‪getLL('label.' . $label);
366  $message = sprintf($this->‪getLanguageService()->getLL('msg.lastRun'), $type, $startDate, $startTime, $endDate, $endTime);
368  }
369  }
370  $this->view->assign('lastRunMessage', $message);
371  $this->view->assign('lastRunSeverity', $severity);
372 
373  $script = $this->‪determineExecutablePath();
374  if (!$script && ‪Environment::isComposerMode()) {
375  $this->view->assign('composerMode', true);
376  } else {
377  // Check if CLI script is executable or not
378  $this->view->assign('script', $script);
379  // Skip this check if running Windows, as rights do not work the same way on this platform
380  // (i.e. the script will always appear as *not* executable)
382  $isExecutable = true;
383  } else {
384  $isExecutable = $script ? is_executable($script) : false;
385  }
386  if ($isExecutable) {
387  $message = $this->‪getLanguageService()->‪getLL('msg.cliScriptExecutable');
388  $severity = ‪InfoboxViewHelper::STATE_OK;
389  } else {
390  $message = $this->‪getLanguageService()->‪getLL('msg.cliScriptNotExecutable');
392  }
393  $this->view->assign('isExecutableMessage', $message);
394  $this->view->assign('isExecutableSeverity', $severity);
395  }
396 
397  $this->view->assign('now', $this->‪getServerTime());
398 
399  return $this->view->render();
400  }
401 
402  private function ‪determineExecutablePath(): ?string
403  {
405  return GeneralUtility::getFileAbsFileName('EXT:core/bin/typo3');
406  }
407  $composerJsonFile = getenv('TYPO3_PATH_COMPOSER_ROOT') . '/composer.json';
408  if (!file_exists($composerJsonFile) || !($jsonContent = file_get_contents($composerJsonFile))) {
409  return null;
410  }
411  $jsonConfig = @json_decode($jsonContent, true);
412  if (empty($jsonConfig) || !is_array($jsonConfig)) {
413  return null;
414  }
415  $vendorDir = trim($jsonConfig['config']['vendor-dir'] ?? 'vendor', '/');
416  $binDir = trim($jsonConfig['config']['bin-dir'] ?? $vendorDir . '/bin', '/');
417 
418  return sprintf('%s/%s/typo3', getenv('TYPO3_PATH_COMPOSER_ROOT'), $binDir);
419  }
420 
426  protected function ‪infoScreenAction(): string
427  {
428  $registeredClasses = $this->‪getRegisteredClasses();
429  // No classes available, display information message
430  if (empty($registeredClasses)) {
431  $this->view->setTemplatePathAndFilename($this->backendTemplatePath . 'InfoScreenNoClasses.html');
432  return $this->view->render();
433  }
434 
435  $this->view->setTemplatePathAndFilename($this->backendTemplatePath . 'InfoScreen.html');
436  $this->view->assign('registeredClasses', $registeredClasses);
437 
438  return $this->view->render();
439  }
440 
444  protected function ‪deleteTask(): void
445  {
446  try {
447  // Try to fetch the task and delete it
448  $task = $this->scheduler->fetchTask($this->submittedData['uid']);
449  // If the task is currently running, it may not be deleted
450  if ($task->isExecutionRunning()) {
451  $this->‪addMessage($this->‪getLanguageService()->getLL('msg.maynotDeleteRunningTask'), ‪FlashMessage::ERROR);
452  } else {
453  if ($this->scheduler->removeTask($task)) {
454  $this->‪getBackendUser()->‪writelog(SystemLogType::EXTENSION, SystemLogDatabaseAction::DELETE, SystemLogErrorClassification::MESSAGE, 0, 'Scheduler task "%s" (UID: %s, Class: "%s") was deleted', [$task->getTaskTitle(), $task->getTaskUid(), $task->getTaskClassName()]);
455  $this->‪addMessage($this->‪getLanguageService()->getLL('msg.deleteSuccess'));
456  } else {
457  $this->‪addMessage($this->‪getLanguageService()->getLL('msg.deleteError'), ‪FlashMessage::ERROR);
458  }
459  }
460  } catch (\UnexpectedValueException $e) {
461  // The task could not be unserialized properly, simply update the database record
462  $taskUid = (int)$this->submittedData['uid'];
463  $result = GeneralUtility::makeInstance(ConnectionPool::class)
464  ->getConnectionForTable('tx_scheduler_task')
465  ->update('tx_scheduler_task', ['deleted' => 1], ['uid' => $taskUid]);
466  if ($result) {
467  $this->‪addMessage($this->‪getLanguageService()->getLL('msg.deleteSuccess'));
468  } else {
469  $this->‪addMessage($this->‪getLanguageService()->getLL('msg.deleteError'), ‪FlashMessage::ERROR);
470  }
471  } catch (\OutOfBoundsException $e) {
472  // The task was not found, for some reason
473  $this->‪addMessage(sprintf($this->‪getLanguageService()->getLL('msg.taskNotFound'), $this->submittedData['uid']), ‪FlashMessage::ERROR);
474  }
475  }
476 
483  protected function ‪stopTask(): void
484  {
485  try {
486  // Try to fetch the task and stop it
487  $task = $this->scheduler->fetchTask($this->submittedData['uid']);
488  if ($task->isExecutionRunning()) {
489  // If the task is indeed currently running, clear marked executions
490  $result = $task->unmarkAllExecutions();
491  if ($result) {
492  $this->‪addMessage($this->‪getLanguageService()->getLL('msg.stopSuccess'));
493  } else {
494  $this->‪addMessage($this->‪getLanguageService()->getLL('msg.stopError'), ‪FlashMessage::ERROR);
495  }
496  } else {
497  // The task is not running, nothing to unmark
498  $this->‪addMessage($this->‪getLanguageService()->getLL('msg.maynotStopNonRunningTask'), ‪FlashMessage::WARNING);
499  }
500  } catch (\Exception $e) {
501  // The task was not found, for some reason
502  $this->‪addMessage(sprintf($this->‪getLanguageService()->getLL('msg.taskNotFound'), $this->submittedData['uid']), ‪FlashMessage::ERROR);
503  }
504  }
505 
509  protected function ‪toggleDisableAction(): void
510  {
511  $task = $this->scheduler->fetchTask($this->submittedData['uid']);
512  $task->setDisabled(!$task->isDisabled());
513  // If a disabled single task is enabled again, we register it for a
514  // single execution at next scheduler run.
515  if ($task->getType() === ‪AbstractTask::TYPE_SINGLE) {
516  $task->registerSingleExecution(time());
517  }
518  $task->save();
519  }
520 
524  protected function ‪setNextExecutionTimeAction(): void
525  {
526  $task = $this->scheduler->fetchTask($this->submittedData['uid']);
527  $task->setRunOnNextCronJob(true);
528  $task->save();
529  }
530 
537  protected function ‪editTaskAction(string $requestUri): string
538  {
539  $this->view->setTemplatePathAndFilename($this->backendTemplatePath . 'EditTask.html');
540 
541  $registeredClasses = $this->‪getRegisteredClasses();
542  $registeredTaskGroups = $this->‪getRegisteredTaskGroups();
543 
544  $taskInfo = [];
545  $task = null;
546  $process = 'edit';
547 
548  if (($this->submittedData['uid'] ?? 0) > 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('Running tasks cannot not be edited', 1251232849);
556  }
557 
558  // Get the task object
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 (isset($registeredClasses[get_class($task)]) && $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  $taskInfo['type'] = ‪AbstractTask::TYPE_RECURRING;
581  $taskInfo['frequency'] = $taskInfo['interval'] ?: $taskInfo['croncmd'];
582  } else {
583  // It's not a recurring task
584  // Make sure interval and cron command are both empty
585  $taskInfo['type'] = ‪AbstractTask::TYPE_SINGLE;
586  $taskInfo['frequency'] = '';
587  $taskInfo['end'] = 0;
588  }
589  } else {
590  // The task object is not valid
591  // Issue error message
592  $this->‪addMessage(sprintf($this->‪getLanguageService()->getLL('msg.invalidTaskClassEdit'), get_class($task)), ‪FlashMessage::ERROR);
593  // Initialize empty values
594  $taskInfo['start'] = 0;
595  $taskInfo['end'] = 0;
596  $taskInfo['frequency'] = '';
597  $taskInfo['multiple'] = false;
598  $taskInfo['type'] = ‪AbstractTask::TYPE_SINGLE;
599  }
600  } catch (\OutOfBoundsException $e) {
601  // Add a message and continue throwing the exception
602  $this->‪addMessage(sprintf($this->‪getLanguageService()->getLL('msg.taskNotFound'), $this->submittedData['uid']), ‪FlashMessage::ERROR);
603  throw $e;
604  }
605  } else {
606  // If adding a new object, set some default values
607  $taskInfo['class'] = key($registeredClasses);
608  $taskInfo['type'] = ‪AbstractTask::TYPE_RECURRING;
609  $taskInfo['start'] = ‪$GLOBALS['EXEC_TIME'];
610  $taskInfo['end'] = '';
611  $taskInfo['frequency'] = '';
612  $taskInfo['multiple'] = 0;
613  $process = 'add';
614  }
615 
616  // If some data was already submitted, use it to override
617  // existing data
618  if (!empty($this->submittedData)) {
619  ‪ArrayUtility::mergeRecursiveWithOverrule($taskInfo, $this->submittedData);
620  }
621 
622  // Get the extra fields to display for each task that needs some
623  $allAdditionalFields = [];
624  if ($process === 'add') {
625  foreach ($registeredClasses as $class => $registrationInfo) {
626  if (!empty($registrationInfo['provider'])) {
628  $providerObject = GeneralUtility::makeInstance($registrationInfo['provider']);
629  if ($providerObject instanceof AdditionalFieldProviderInterface) {
630  $additionalFields = $providerObject->getAdditionalFields($taskInfo, null, $this);
631  $allAdditionalFields = array_merge($allAdditionalFields, [$class => $additionalFields]);
632  }
633  }
634  }
635  } elseif ($task !== null && !empty($registeredClasses[$taskInfo['class']]['provider'])) {
636  // only try to fetch additionalFields if the task is valid
637  $providerObject = GeneralUtility::makeInstance($registeredClasses[$taskInfo['class']]['provider']);
638  if ($providerObject instanceof AdditionalFieldProviderInterface) {
639  $allAdditionalFields[$taskInfo['class']] = $providerObject->getAdditionalFields($taskInfo, $task, $this);
640  }
641  }
642 
643  // Load necessary JavaScript
644  $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Scheduler/Scheduler');
645  $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/DateTimePicker');
646 
647  // Start rendering the add/edit form
648  $this->view->assign('uid', htmlspecialchars((string)($this->submittedData['uid'] ?? '')));
649  $this->view->assign('cmd', htmlspecialchars((string)$this->‪getCurrentAction()));
650  $this->view->assign('csh', $this->cshKey);
651  $this->view->assign('lang', 'LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:');
652 
653  $table = [];
654 
655  // Disable checkbox
656  $this->view->assign('task_disable', (($taskInfo['disable'] ?? false) ? ' checked="checked"' : ''));
657  $this->view->assign('task_disable_label', 'LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:disable');
658 
659  // Task class selector
660  // On editing, don't allow changing of the task class, unless it was not valid
661  if (($this->submittedData['uid'] ?? 0) > 0 && !empty($taskInfo['class'])) {
662  $this->view->assign('task_class', $taskInfo['class']);
663  $this->view->assign('task_class_title', $registeredClasses[$taskInfo['class']]['title']);
664  $this->view->assign('task_class_extension', $registeredClasses[$taskInfo['class']]['extension']);
665  } else {
666  // Group registered classes by classname
667  $groupedClasses = [];
668  foreach ($registeredClasses as $class => $classInfo) {
669  $groupedClasses[$classInfo['extension']][$class] = $classInfo;
670  }
671  ksort($groupedClasses);
672  foreach ($groupedClasses as $extension => $class) {
673  foreach ($groupedClasses[$extension] as $class => $classInfo) {
674  $selected = $class == $taskInfo['class'] ? ' selected="selected"' : '';
675  $groupedClasses[$extension][$class]['selected'] = $selected;
676  }
677  }
678  $this->view->assign('groupedClasses', $groupedClasses);
679  }
680 
681  // Task type selector
682  $this->view->assign('task_type_selected_1', ((int)$taskInfo['type'] === ‪AbstractTask::TYPE_SINGLE ? ' selected="selected"' : ''));
683  $this->view->assign('task_type_selected_2', ((int)$taskInfo['type'] === ‪AbstractTask::TYPE_RECURRING ? ' selected="selected"' : ''));
684 
685  // Task group selector
686  foreach ($registeredTaskGroups as $key => $taskGroup) {
687  $selected = $taskGroup['uid'] == ($taskInfo['task_group'] ?? false) ? ' selected="selected"' : '';
688  $registeredTaskGroups[$key]['selected'] = $selected;
689  }
690  $this->view->assign('registeredTaskGroups', $registeredTaskGroups);
691 
692  // Start date/time field
693  $dateFormat = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? '%H:%M %m-%d-%Y' : '%H:%M %d-%m-%Y';
694  // @todo Replace deprecated strftime in php 8.1. Suppress warning in v11.
695  $this->view->assign('start_value_hr', ($taskInfo['start'] > 0 ? @strftime($dateFormat, $taskInfo['start']) : ''));
696  $this->view->assign('start_value', $taskInfo['start']);
697 
698  // End date/time field
699  // NOTE: datetime fields need a special id naming scheme
700  // @todo Replace deprecated strftime in php 8.1. Suppress warning in v11.
701  $this->view->assign('end_value_hr', ($taskInfo['end'] > 0 ? @strftime($dateFormat, $taskInfo['end']) : ''));
702  $this->view->assign('end_value', $taskInfo['end']);
703 
704  // Frequency input field
705  $this->view->assign('frequency', $taskInfo['frequency']);
706 
707  // Multiple execution selector
708  $this->view->assign('multiple', ($taskInfo['multiple'] ? 'checked="checked"' : ''));
709 
710  // Description
711  $this->view->assign('description', $taskInfo['description'] ?? '');
712 
713  // Display additional fields
714  $additionalFieldList = [];
715  foreach ($allAdditionalFields as $class => ‪$fields) {
716  if ($class == $taskInfo['class']) {
717  $additionalFieldsStyle = '';
718  } else {
719  $additionalFieldsStyle = ' style="display: none"';
720  }
721  // Add each field to the display, if there are indeed any
722  if (is_array(‪$fields)) {
723  foreach (‪$fields as $fieldID => $fieldInfo) {
724  $htmlClassName = strtolower(str_replace('\\', '-', (string)$class));
725  $field = [];
726  $field['htmlClassName'] = $htmlClassName;
727  $field['code'] = $fieldInfo['code'];
728  $field['cshKey'] = $fieldInfo['cshKey'] ?? '';
729  $field['cshLabel'] = $fieldInfo['cshLabel'] ?? '';
730  $field['langLabel'] = $fieldInfo['label'] ?? '';
731  $field['fieldID'] = $fieldID;
732  $field['additionalFieldsStyle'] = $additionalFieldsStyle;
733  $field['browseButton'] = $this->‪getBrowseButton($fieldID, $fieldInfo);
734  $additionalFieldList[] = $field;
735  }
736  }
737  }
738  $this->view->assign('additionalFields', $additionalFieldList);
739 
740  $this->view->assign('returnUrl', $requestUri);
741  $this->view->assign('table', implode(LF, $table));
742  $this->view->assign('now', $this->‪getServerTime());
743  $this->view->assign('frequencyOptions', (array)(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['frequencyOptions'] ?? []));
744 
745  return $this->view->render();
746  }
747 
753  protected function ‪getBrowseButton($fieldID, array $fieldInfo): string
754  {
755  if (isset($fieldInfo['browser']) && ($fieldInfo['browser'] === 'page')) {
756  $url = (string)$this->uriBuilder->buildUriFromRoute('wizard_element_browser');
757 
758  $title = htmlspecialchars($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.browse_db'));
759  return '
760  <div><a href="' . htmlspecialchars($url) . '" data-trigger-for="' . htmlspecialchars($fieldID) . '" data-mode="db" data-params="" class="btn btn-default t3js-element-browser" title="' . $title . '">
761  <span class="t3js-icon icon icon-size-small icon-state-default icon-actions-insert-record" data-identifier="actions-insert-record">
762  <span class="icon-markup">' . $this->iconFactory->getIcon(
763  'actions-insert-record',
765  )->render() . '</span>
766  </span>
767  </a><span id="page_' . $fieldID . '">&nbsp;' . htmlspecialchars($fieldInfo['pageTitle']) . '</span></div>';
768  }
769  return '';
770  }
771 
775  protected function ‪executeTasks(): void
776  {
777  // Continue if some elements have been chosen for execution
778  if (isset($this->submittedData['execute']) && !empty($this->submittedData['execute'])) {
779  $taskIds = ‪GeneralUtility::trimExplode(',', $this->submittedData['execute']);
780  // Get list of registered classes
781  $registeredClasses = $this->‪getRegisteredClasses();
782  // Loop on all selected tasks
783  foreach ($taskIds as $uid) {
784  try {
785  // Try fetching the task
786  $task = $this->scheduler->fetchTask($uid);
787  $class = get_class($task);
788  $name = $registeredClasses[$class]['title'] . ' (' . $registeredClasses[$class]['extension'] . ')';
789  if (GeneralUtility::_POST('go_cron') !== null) {
790  $task->setRunOnNextCronJob(true);
791  $task->save();
792  } else {
793  // Now try to execute it and report on outcome
794  try {
795  $result = $this->scheduler->executeTask($task);
796  if ($result) {
797  $this->‪addMessage(sprintf($this->‪getLanguageService()->getLL('msg.executed'), $name, $uid));
798  } else {
799  $this->‪addMessage(sprintf($this->‪getLanguageService()->getLL('msg.notExecuted'), $name, $uid), ‪FlashMessage::ERROR);
800  }
801  } catch (\Exception $e) {
802  // An exception was thrown, display its message as an error
803  $this->‪addMessage(sprintf($this->‪getLanguageService()->getLL('msg.executionFailed'), $name, $e->getMessage()), ‪FlashMessage::ERROR);
804  }
805  }
806  } catch (\OutOfBoundsException $e) {
807  $this->‪addMessage(sprintf($this->‪getLanguageService()->getLL('msg.taskNotFound'), $uid), ‪FlashMessage::ERROR);
808  } catch (\UnexpectedValueException $e) {
809  $this->‪addMessage(sprintf($this->‪getLanguageService()->getLL('msg.executionFailed'), $uid, $e->getMessage()), ‪FlashMessage::ERROR);
810  }
811  }
812  // Record the run in the system registry
813  $this->scheduler->recordLastRun('manual');
814  // Make sure to switch to list view after execution
816  }
817  }
818 
824  protected function ‪listTasksAction(): string
825  {
826  $this->view->setTemplatePathAndFilename($this->backendTemplatePath . 'ListTasks.html');
827 
828  // Define display format for dates
829  $dateFormat = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
830 
831  // Get list of registered task groups
832  $registeredTaskGroups = $this->‪getRegisteredTaskGroups();
833 
834  // add an empty entry for non-grouped tasks
835  // add in front of list
836  array_unshift($registeredTaskGroups, ['uid' => 0, 'groupName' => '']);
837 
838  // Get all registered tasks
839  // Just to get the number of entries
840  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
841  ->getQueryBuilderForTable('tx_scheduler_task');
842  $queryBuilder->getRestrictions()->removeAll();
843 
844  $result = $queryBuilder->select('t.*')
845  ->addSelect(
846  'g.groupName AS taskGroupName',
847  'g.description AS taskGroupDescription',
848  'g.uid AS taskGroupId',
849  'g.deleted AS isTaskGroupDeleted',
850  )
851  ->from('tx_scheduler_task', 't')
852  ->leftJoin(
853  't',
854  'tx_scheduler_task_group',
855  'g',
856  $queryBuilder->expr()->eq('t.task_group', $queryBuilder->quoteIdentifier('g.uid'))
857  )
858  ->where(
859  $queryBuilder->expr()->eq('t.deleted', 0)
860  )
861  ->orderBy('g.sorting')
862  ->executeQuery();
863 
864  $schedulerModuleData = $this->‪getBackendUser()->‪getModuleData('scheduler') ?? [];
865 
866  // Loop on all tasks
867  $temporaryResult = [];
868  while ($row = $result->fetchAssociative()) {
869  if ($row['taskGroupName'] === null || $row['isTaskGroupDeleted'] === '1') {
870  $row['taskGroupName'] = '';
871  $row['taskGroupDescription'] = '';
872  $row['taskGroupId'] = 0;
873  $row['task_group'] = 0;
874  }
875  $temporaryResult[$row['task_group']]['groupName'] = $row['taskGroupName'];
876  $temporaryResult[$row['task_group']]['groupDescription'] = $row['taskGroupDescription'];
877  $temporaryResult[$row['task_group']]['taskGroupId'] = $row['taskGroupId'];
878  $temporaryResult[$row['task_group']]['taskGroupCollapsed'] = (bool)($schedulerModuleData['task-group-' . $row['taskGroupId']] ?? false);
879  $temporaryResult[$row['task_group']]['tasks'][] = $row;
880  }
881 
882  // No tasks defined, display information message
883  if (empty($temporaryResult)) {
884  $this->view->setTemplatePathAndFilename($this->backendTemplatePath . 'ListTasksNoTasks.html');
885  return $this->view->render();
886  }
887 
888  $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Scheduler/Scheduler');
889  $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Tooltip');
890 
891  $tasks = $temporaryResult;
892 
893  $registeredClasses = $this->‪getRegisteredClasses();
894  $missingClasses = [];
895  foreach ($temporaryResult as $taskIndex => $taskGroup) {
896  foreach ($taskGroup['tasks'] as $recordIndex => $schedulerRecord) {
897  if ((int)$schedulerRecord['disable'] === 1) {
898  $translationKey = 'enable';
899  } else {
900  $translationKey = 'disable';
901  }
902  $tasks[$taskIndex]['tasks'][$recordIndex]['translationKey'] = $translationKey;
903 
904  // Define some default values
905  $lastExecution = '-';
906  $class = '';
907  $isRunning = false;
908  $showAsDisabled = false;
909  // Restore the serialized task and pass it a reference to the scheduler object
911  $exceptionWithClass = false;
912  $task = null;
913  try {
914  $task = unserialize($schedulerRecord['serialized_task_object']);
915 
916  $class = get_class($task);
917  if ($class === \__PHP_Incomplete_Class::class && preg_match('/^O:[0-9]+:"(?P<classname>.+?)"/', $schedulerRecord['serialized_task_object'], $matches) === 1) {
918  $class = $matches['classname'];
919  }
920  $tasks[$taskIndex]['tasks'][$recordIndex]['class'] = $class;
921  // Assemble information about last execution
922  if (!empty($schedulerRecord['lastexecution_time'])) {
923  $lastExecution = date($dateFormat, (int)$schedulerRecord['lastexecution_time']);
924  if ($schedulerRecord['lastexecution_context'] === 'CLI') {
925  $context = $this->‪getLanguageService()->‪getLL('label.cron');
926  } else {
927  $context = $this->‪getLanguageService()->‪getLL('label.manual');
928  }
929  $lastExecution .= ' (' . $context . ')';
930  }
931  $tasks[$taskIndex]['tasks'][$recordIndex]['lastExecution'] = $lastExecution;
932  } catch (\BadMethodCallException $e) {
933  $exceptionWithClass = true;
934  }
935  if (!$exceptionWithClass && isset($registeredClasses[get_class($task)]) && $this->scheduler->isValidTaskObject($task)) {
936  $tasks[$taskIndex]['tasks'][$recordIndex]['validClass'] = true;
937  // The task object is valid
938  $labels = [];
939  $additionalInformation = $task->getAdditionalInformation();
940  if ($task instanceof ProgressProviderInterface) {
941  $progress = round((float)$task->getProgress(), 2);
942  $tasks[$taskIndex]['tasks'][$recordIndex]['progress'] = $progress;
943  }
944  $tasks[$taskIndex]['tasks'][$recordIndex]['classTitle'] = $registeredClasses[$class]['title'];
945  $tasks[$taskIndex]['tasks'][$recordIndex]['classExtension'] = $registeredClasses[$class]['extension'];
946  $tasks[$taskIndex]['tasks'][$recordIndex]['additionalInformation'] = $additionalInformation;
947  // Check if task currently has a running execution
948  if (!empty($schedulerRecord['serialized_executions'])) {
949  $labels[] = [
950  'class' => 'success',
951  'text' => $this->‪getLanguageService()->‪getLL('status.running'),
952  ];
953  $isRunning = true;
954  }
955  $tasks[$taskIndex]['tasks'][$recordIndex]['isRunning'] = $isRunning;
956 
957  // Prepare display of next execution date
958  // If task is currently running, date is not displayed (as next hasn't been calculated yet)
959  // Also hide the date if task is disabled (the information doesn't make sense, as it will not run anyway)
960  if ($isRunning || $schedulerRecord['disable']) {
961  $nextDate = '-';
962  } else {
963  $nextDate = date($dateFormat, (int)$schedulerRecord['nextexecution']);
964  if (empty($schedulerRecord['nextexecution'])) {
965  $nextDate = $this->‪getLanguageService()->‪getLL('none');
966  } elseif ($schedulerRecord['nextexecution'] < ‪$GLOBALS['EXEC_TIME']) {
967  $labels[] = [
968  'class' => 'warning',
969  'text' => $this->‪getLanguageService()->‪getLL('status.late'),
970  'description' => $this->‪getLanguageService()->‪getLL('status.legend.scheduled'),
971  ];
972  }
973  }
974  $tasks[$taskIndex]['tasks'][$recordIndex]['nextDate'] = $nextDate;
975  // Get execution type
976  if ($task->getType() === ‪AbstractTask::TYPE_SINGLE) {
977  $execType = $this->‪getLanguageService()->‪getLL('label.type.single');
978  $frequency = '-';
979  } else {
980  $execType = $this->‪getLanguageService()->‪getLL('label.type.recurring');
981  if ($task->getExecution()->getCronCmd() == '') {
982  $frequency = $task->getExecution()->getInterval();
983  } else {
984  $frequency = $task->getExecution()->getCronCmd();
985  }
986  }
987  // Check the disable status
988  // Row is shown dimmed if task is disabled, unless it is still running
989  if ($schedulerRecord['disable'] && !$isRunning) {
990  $labels[] = [
991  'class' => 'default',
992  'text' => $this->‪getLanguageService()->‪getLL('status.disabled'),
993  ];
994  $showAsDisabled = true;
995  }
996  $tasks[$taskIndex]['tasks'][$recordIndex]['execType'] = $execType;
997  $tasks[$taskIndex]['tasks'][$recordIndex]['frequency'] = $frequency;
998  // Get multiple executions setting
999  if ($task->getExecution()->getMultiple()) {
1000  $multiple = $this->‪getLanguageService()->‪sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:yes');
1001  } else {
1002  $multiple = $this->‪getLanguageService()->‪sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:no');
1003  }
1004  $tasks[$taskIndex]['tasks'][$recordIndex]['multiple'] = $multiple;
1005 
1006  // Check if the last run failed
1007  if (!empty($schedulerRecord['lastexecution_failure'])) {
1008  // Try to get the stored exception array
1010  $exceptionArray = @unserialize($schedulerRecord['lastexecution_failure']);
1011  // If the exception could not be unserialized, issue a default error message
1012  if (!is_array($exceptionArray) || empty($exceptionArray)) {
1013  $labelDescription = $this->‪getLanguageService()->‪getLL('msg.executionFailureDefault');
1014  } else {
1015  $labelDescription = sprintf($this->‪getLanguageService()->getLL('msg.executionFailureReport'), $exceptionArray['code'], $exceptionArray['message']);
1016  }
1017  $labels[] = [
1018  'class' => 'danger',
1019  'text' => $this->‪getLanguageService()->‪getLL('status.failure'),
1020  'description' => $labelDescription,
1021  ];
1022  }
1023  $tasks[$taskIndex]['tasks'][$recordIndex]['labels'] = $labels;
1024  if ($showAsDisabled) {
1025  $tasks[$taskIndex]['tasks'][$recordIndex]['showAsDisabled'] = 'disabled';
1026  }
1027  } else {
1028  $missingClasses[] = $tasks[$taskIndex]['tasks'][$recordIndex];
1029  unset($tasks[$taskIndex]['tasks'][$recordIndex]);
1030  }
1031  }
1032  }
1033 
1034  $this->view->assign('tasks', $tasks);
1035  $this->view->assign('missingClasses', $missingClasses);
1036  $this->view->assign('missingClassesCollapsed', (bool)($schedulerModuleData['task-group-missing'] ?? false));
1037  $this->view->assign('moduleUri', $this->moduleUri);
1038  $this->view->assign('now', $this->‪getServerTime());
1040  return $this->view->render();
1041  }
1042 
1049  protected function ‪makeStatusLabel(array $labels): string
1050  {
1051  $htmlLabels = [];
1052  foreach ($labels as $label) {
1053  if (empty($label['text'])) {
1054  continue;
1055  }
1056  $htmlLabels[] = '<span class="label label-' . htmlspecialchars($label['class']) . ' float-end" title="' . htmlspecialchars($label['description']) . '">' . htmlspecialchars($label['text']) . '</span>';
1057  }
1058 
1059  return implode('&nbsp;', $htmlLabels);
1060  }
1061 
1065  protected function ‪saveTask(): void
1066  {
1067  // If a task is being edited fetch old task data
1068  if (!empty($this->submittedData['uid'])) {
1069  try {
1070  $taskRecord = $this->scheduler->fetchTaskRecord($this->submittedData['uid']);
1072  $task = unserialize($taskRecord['serialized_task_object']);
1073  } catch (\OutOfBoundsException $e) {
1074  // If the task could not be fetched, issue an error message
1075  // and exit early
1076  $this->‪addMessage(sprintf($this->‪getLanguageService()->getLL('msg.taskNotFound'), $this->submittedData['uid']), ‪FlashMessage::ERROR);
1077  return;
1078  }
1079  // Register single execution
1080  if ((int)$this->submittedData['type'] === ‪AbstractTask::TYPE_SINGLE) {
1081  $task->registerSingleExecution($this->submittedData['start']);
1082  } else {
1083  if (!empty($this->submittedData['croncmd'])) {
1084  // Definition by cron-like syntax
1085  $interval = 0;
1086  $cronCmd = $this->submittedData['croncmd'];
1087  } else {
1088  // Definition by interval
1089  $interval = $this->submittedData['interval'];
1090  $cronCmd = '';
1091  }
1092  // Register recurring execution
1093  $task->registerRecurringExecution($this->submittedData['start'], $interval, $this->submittedData['end'], $this->submittedData['multiple'], $cronCmd);
1094  }
1095  // Set disable flag
1096  $task->setDisabled($this->submittedData['disable']);
1097  // Set description
1098  $task->setDescription($this->submittedData['description']);
1099  // Set task group
1100  $task->setTaskGroup($this->submittedData['task_group']);
1101  // Save additional input values
1102  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields'])) {
1104  $providerObject = GeneralUtility::makeInstance(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields']);
1105  if ($providerObject instanceof AdditionalFieldProviderInterface) {
1106  $providerObject->saveAdditionalFields($this->submittedData, $task);
1107  }
1108  }
1109  // Save to database
1110  $result = $this->scheduler->saveTask($task);
1111  if ($result) {
1112  $this->‪getBackendUser()->‪writelog(SystemLogType::EXTENSION, SystemLogDatabaseAction::UPDATE, SystemLogErrorClassification::MESSAGE, 0, 'Scheduler task "%s" (UID: %s, Class: "%s") was updated', [$task->getTaskTitle(), $task->getTaskUid(), $task->getTaskClassName()]);
1113  $this->‪addMessage($this->‪getLanguageService()->getLL('msg.updateSuccess'));
1114  } else {
1115  $this->‪addMessage($this->‪getLanguageService()->getLL('msg.updateError'), ‪FlashMessage::ERROR);
1116  }
1117  } else {
1118  // A new task is being created
1119  // Create an instance of chosen class
1121  $task = GeneralUtility::makeInstance($this->submittedData['class']);
1122  if ((int)$this->submittedData['type'] === ‪AbstractTask::TYPE_SINGLE) {
1123  // Set up single execution
1124  $task->registerSingleExecution($this->submittedData['start']);
1125  } else {
1126  // Set up recurring execution
1127  $task->registerRecurringExecution($this->submittedData['start'], $this->submittedData['interval'], $this->submittedData['end'], $this->submittedData['multiple'], $this->submittedData['croncmd']);
1128  }
1129  // Save additional input values
1130  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields'])) {
1132  $providerObject = GeneralUtility::makeInstance(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields']);
1133  if ($providerObject instanceof AdditionalFieldProviderInterface) {
1134  $providerObject->saveAdditionalFields($this->submittedData, $task);
1135  }
1136  }
1137  // Set disable flag
1138  $task->setDisabled($this->submittedData['disable']);
1139  // Set description
1140  $task->setDescription($this->submittedData['description']);
1141  // Set description
1142  $task->setTaskGroup($this->submittedData['task_group']);
1143  // Add to database
1144  $result = $this->scheduler->addTask($task);
1145  if ($result) {
1146  $this->‪getBackendUser()->‪writelog(SystemLogType::EXTENSION, SystemLogDatabaseAction::INSERT, SystemLogErrorClassification::MESSAGE, 0, 'Scheduler task "%s" (UID: %s, Class: "%s") was added', [$task->getTaskTitle(), $task->getTaskUid(), $task->getTaskClassName()]);
1147  $this->‪addMessage($this->‪getLanguageService()->getLL('msg.addSuccess'));
1148 
1149  // set the uid of the just created task so that we
1150  // can continue editing after initial saving
1151  $this->submittedData['uid'] = $task->getTaskUid();
1152  } else {
1153  $this->‪addMessage($this->‪getLanguageService()->getLL('msg.addError'), ‪FlashMessage::ERROR);
1154  }
1155  }
1156  }
1157 
1158  /*************************
1159  *
1160  * INPUT PROCESSING UTILITIES
1161  *
1162  *************************/
1168  protected function ‪preprocessData()
1169  {
1170  $cronErrorCode = 0;
1171  $result = true;
1172  // Validate id
1173  $this->submittedData['uid'] = empty($this->submittedData['uid']) ? 0 : (int)$this->submittedData['uid'];
1174  // Validate selected task class
1175  if (!class_exists($this->submittedData['class'])) {
1176  $this->‪addMessage($this->‪getLanguageService()->getLL('msg.noTaskClassFound'), ‪FlashMessage::ERROR);
1177  }
1178  // Check start date
1179  if (empty($this->submittedData['start'])) {
1180  $this->‪addMessage($this->‪getLanguageService()->getLL('msg.noStartDate'), ‪FlashMessage::ERROR);
1181  $result = false;
1182  } elseif (is_string($this->submittedData['start']) && (!is_numeric($this->submittedData['start']))) {
1183  try {
1184  $this->submittedData['start'] = $this->‪convertToTimestamp($this->submittedData['start']);
1185  } catch (\Exception $e) {
1186  $this->‪addMessage($this->‪getLanguageService()->getLL('msg.invalidStartDate'), ‪FlashMessage::ERROR);
1187  $result = false;
1188  }
1189  } else {
1190  $this->submittedData['start'] = (int)$this->submittedData['start'];
1191  }
1192  // Check end date, if recurring task
1193  if ((int)$this->submittedData['type'] === ‪AbstractTask::TYPE_RECURRING && !empty($this->submittedData['end'])) {
1194  if (is_string($this->submittedData['end']) && (!is_numeric($this->submittedData['end']))) {
1195  try {
1196  $this->submittedData['end'] = $this->‪convertToTimestamp($this->submittedData['end']);
1197  } catch (\Exception $e) {
1198  $this->‪addMessage($this->‪getLanguageService()->getLL('msg.invalidStartDate'), ‪FlashMessage::ERROR);
1199  $result = false;
1200  }
1201  } else {
1202  $this->submittedData['end'] = (int)$this->submittedData['end'];
1203  }
1204  if ($this->submittedData['end'] < $this->submittedData['start']) {
1205  $this->‪addMessage(
1206  $this->‪getLanguageService()->getLL('msg.endDateSmallerThanStartDate'),
1208  );
1209  $result = false;
1210  }
1211  }
1212  // Set default values for interval and cron command
1213  $this->submittedData['interval'] = 0;
1214  $this->submittedData['croncmd'] = '';
1215  // Check type and validity of frequency, if recurring
1216  if ((int)$this->submittedData['type'] === ‪AbstractTask::TYPE_RECURRING) {
1217  $frequency = trim($this->submittedData['frequency']);
1218  if (empty($frequency)) {
1219  // Empty frequency, not valid
1220  $this->‪addMessage($this->‪getLanguageService()->getLL('msg.noFrequency'), ‪FlashMessage::ERROR);
1221  $result = false;
1222  } else {
1223  $cronErrorMessage = '';
1224  // Try interpreting the cron command
1225  try {
1226  ‪NormalizeCommand::normalize($frequency);
1227  $this->submittedData['croncmd'] = $frequency;
1228  } catch (\Exception $e) {
1229  // Store the exception's result
1230  $cronErrorMessage = $e->getMessage();
1231  $cronErrorCode = $e->getCode();
1232  // Check if the frequency is a valid number
1233  // If yes, assume it is a frequency in seconds, and unset cron error code
1234  if (is_numeric($frequency)) {
1235  $this->submittedData['interval'] = (int)$frequency;
1236  $cronErrorCode = 0;
1237  }
1238  }
1239  // If there's a cron error code, issue validation error message
1240  if ($cronErrorCode > 0) {
1241  $this->‪addMessage(sprintf($this->‪getLanguageService()->getLL('msg.frequencyError'), $cronErrorMessage, $cronErrorCode), ‪FlashMessage::ERROR);
1242  $result = false;
1243  }
1244  }
1245  }
1246  // Validate additional input fields
1247  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields'])) {
1249  $providerObject = GeneralUtility::makeInstance(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields']);
1250  if ($providerObject instanceof ‪AdditionalFieldProviderInterface) {
1251  // The validate method will return true if all went well, but that must not
1252  // override previous false values => AND the returned value with the existing one
1253  $result &= $providerObject->validateAdditionalFields($this->submittedData, $this);
1254  }
1255  }
1256  return (bool)$result;
1257  }
1258 
1265  protected function ‪convertToTimestamp(string $input): int
1266  {
1267  // Convert to ISO 8601 dates
1268  $dateTime = new \DateTime($input);
1269  $value = $dateTime->getTimestamp();
1270  if ($value !== 0) {
1271  $value -= (int)date('Z', $value);
1272  }
1273  return $value;
1274  }
1275 
1282  protected function ‪addMessage($message, $severity = ‪FlashMessage::OK)
1283  {
1284  $this->moduleTemplate->addFlashMessage($message, '', $severity);
1285  }
1286 
1300  protected function ‪getRegisteredClasses(): array
1301  {
1302  $list = [];
1303  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'] ?? [] as $class => $registrationInformation) {
1304  $title = isset($registrationInformation['title']) ? $this->‪getLanguageService()->‪sL($registrationInformation['title']) : '';
1305  $description = isset($registrationInformation['description']) ? $this->‪getLanguageService()->‪sL($registrationInformation['description']) : '';
1306  $list[$class] = [
1307  'extension' => $registrationInformation['extension'],
1308  'title' => $title,
1309  'description' => $description,
1310  'provider' => $registrationInformation['additionalFields'] ?? '',
1311  ];
1312  }
1313  return $list;
1314  }
1315 
1321  protected function ‪getRegisteredTaskGroups(): array
1322  {
1323  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1324  ->getQueryBuilderForTable('tx_scheduler_task_group');
1325  $queryBuilder->getRestrictions()->removeByType(HiddenRestriction::class);
1326 
1327  return $queryBuilder
1328  ->select('*')
1329  ->from('tx_scheduler_task_group')
1330  ->orderBy('sorting')
1331  ->executeQuery()
1332  ->fetchAllAssociative();
1333  }
1334 
1340  protected function ‪getButtons(ServerRequestInterface $request): void
1341  {
1342  $queryParams = $request->getQueryParams();
1343  $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
1344  // CSH
1345  $helpButton = $buttonBar->makeHelpButton()
1346  ->setModuleName('_MOD_system_txschedulerM1')
1347  ->setFieldName('');
1348  $buttonBar->addButton($helpButton);
1349 
1350  // Add and Reload
1352  $reloadButton = $buttonBar->makeLinkButton()
1353  ->setTitle($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.reload'))
1354  ->setIcon($this->iconFactory->getIcon('actions-refresh', ‪Icon::SIZE_SMALL))
1355  ->setHref($this->moduleUri);
1356  $buttonBar->addButton($reloadButton, ‪ButtonBar::BUTTON_POSITION_RIGHT, 1);
1357  if ($this->MOD_SETTINGS['function'] === 'scheduler' && !empty($this->‪getRegisteredClasses())) {
1358  $addButton = $buttonBar->makeLinkButton()
1359  ->setTitle($this->‪getLanguageService()->getLL('action.add'))
1360  ->setIcon($this->iconFactory->getIcon('actions-add', ‪Icon::SIZE_SMALL))
1361  ->setHref($this->moduleUri . '&CMD=' . ‪Action::ADD);
1362  $buttonBar->addButton($addButton, ‪ButtonBar::BUTTON_POSITION_LEFT, 2);
1363  }
1364  }
1365 
1366  // Close and Save
1367  if (in_array((string)$this->‪getCurrentAction(), [‪Action::ADD, ‪Action::EDIT], true)) {
1368  // Close
1369  $closeButton = $buttonBar->makeLinkButton()
1370  ->setTitle($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:cancel'))
1371  ->setIcon($this->iconFactory->getIcon('actions-close', ‪Icon::SIZE_SMALL))
1372  ->setHref($this->moduleUri);
1373  $buttonBar->addButton($closeButton, ‪ButtonBar::BUTTON_POSITION_LEFT, 2);
1374  // Save, SaveAndClose, SaveAndNew
1375  $saveButtonDropdown = $buttonBar->makeSplitButton();
1376  $saveButton = $buttonBar->makeInputButton()
1377  ->setName('CMD')
1378  ->setValue(‪Action::SAVE)
1379  ->setForm('tx_scheduler_form')
1380  ->setIcon($this->iconFactory->getIcon('actions-document-save', ‪Icon::SIZE_SMALL))
1381  ->setTitle($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:save'));
1382  $saveButtonDropdown->addItem($saveButton);
1383  $saveAndNewButton = $buttonBar->makeInputButton()
1384  ->setName('CMD')
1385  ->setValue(‪Action::SAVE_NEW)
1386  ->setForm('tx_scheduler_form')
1387  ->setIcon($this->iconFactory->getIcon('actions-document-save-new', ‪Icon::SIZE_SMALL))
1388  ->setTitle($this->‪getLanguageService()->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:label.saveAndCreateNewTask'));
1389  $saveButtonDropdown->addItem($saveAndNewButton);
1390  $saveAndCloseButton = $buttonBar->makeInputButton()
1391  ->setName('CMD')
1392  ->setValue(‪Action::SAVE_CLOSE)
1393  ->setForm('tx_scheduler_form')
1394  ->setIcon($this->iconFactory->getIcon('actions-document-save-close', ‪Icon::SIZE_SMALL))
1395  ->setTitle($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:saveAndClose'));
1396  $saveButtonDropdown->addItem($saveAndCloseButton);
1397  $buttonBar->addButton($saveButtonDropdown, ‪ButtonBar::BUTTON_POSITION_LEFT, 3);
1398  }
1399 
1400  // Delete
1401  if (($queryParams['tx_scheduler']['uid'] ?? false) && $this->‪getCurrentAction()->equals(‪Action::EDIT)) {
1402  $deleteButton = $buttonBar->makeLinkButton()
1403  ->setHref($this->moduleUri . '&CMD=' . ‪Action::DELETE . '&tx_scheduler[uid]=' . $queryParams['tx_scheduler']['uid'])
1404  ->setClasses('t3js-modal-trigger')
1405  ->setDataAttributes([
1406  'severity' => 'warning',
1407  'title' => $this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:delete'),
1408  'button-close-text' => $this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:cancel'),
1409  'bs-content' => $this->‪getLanguageService()->getLL('msg.delete'),
1410  ])
1411  ->setIcon($this->iconFactory->getIcon('actions-edit-delete', ‪Icon::SIZE_SMALL))
1412  ->setTitle($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:delete'));
1413  $buttonBar->addButton($deleteButton, ‪ButtonBar::BUTTON_POSITION_LEFT, 4);
1414  }
1415 
1416  // Shortcut
1417  $shortcutArguments = [
1418  'CMD' => (string)‪Action::cast($queryParams['CMD'] ?? null),
1419  'SET' => [
1420  'function' => $this->MOD_SETTINGS['function'],
1421  ],
1422  ];
1423  if (isset($queryParams['tx_scheduler']['uid'])) {
1424  $shortcutArguments['tx_scheduler']['uid'] = $queryParams['tx_scheduler']['uid'];
1425  }
1426  $shortcutButton = $buttonBar->makeShortcutButton()
1427  ->setRouteIdentifier('system_txschedulerM1')
1428  ->setDisplayName($this->MOD_MENU['function'][$this->MOD_SETTINGS['function']])
1429  ->setArguments($shortcutArguments);
1430  $buttonBar->addButton($shortcutButton);
1431  }
1432 
1438  protected function ‪setCurrentAction(‪Action ‪$action): void
1439  {
1440  $this->action = ‪$action;
1441  }
1446  protected function ‪getServerTime(): string
1447  {
1448  $dateFormat = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'] . ' T (e';
1449  return date($dateFormat) . ', GMT ' . date('P') . ')';
1450  }
1451 
1452  protected function ‪getSectionTitle(): string
1453  {
1454  $currentAction = (string)$this->‪getCurrentAction();
1456  return ($this->MOD_SETTINGS['function'] ?? '') === 'scheduler' && ($currentAction === ‪Action::ADD || $currentAction === ‪Action::EDIT)
1457  ? $this->‪getLanguageService()->‪getLL('action.' . $currentAction)
1458  : '';
1459  }
1460 
1466  {
1467  return ‪$GLOBALS['LANG'];
1468  }
1469 
1475  protected function ‪getBackendUser(): ‪BackendUserAuthentication
1476  {
1477  return ‪$GLOBALS['BE_USER'];
1478  }
1479 }
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\$MOD_SETTINGS
‪array $MOD_SETTINGS
Definition: SchedulerModuleController.php:110
‪TYPO3\CMS\Scheduler\CronCommand\NormalizeCommand\normalize
‪static string normalize($cronCommand)
Definition: NormalizeCommand.php:40
‪TYPO3\CMS\Scheduler\Task\Enumeration\Action\SAVE
‪const SAVE
Definition: Action.php:32
‪TYPO3\CMS\Core\Imaging\Icon\SIZE_SMALL
‪const SIZE_SMALL
Definition: Icon.php:30
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\$scheduler
‪Scheduler $scheduler
Definition: SchedulerModuleController.php:112
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:999
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\$moduleUri
‪string $moduleUri
Definition: SchedulerModuleController.php:86
‪TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction
Definition: HiddenRestriction.php:27
‪TYPO3\CMS\Scheduler\Task\Enumeration\Action\ADD
‪const ADD
Definition: Action.php:28
‪TYPO3\CMS\Backend\Template\Components\ButtonBar\BUTTON_POSITION_LEFT
‪const BUTTON_POSITION_LEFT
Definition: ButtonBar.php:36
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getBrowseButton
‪string getBrowseButton($fieldID, array $fieldInfo)
Definition: SchedulerModuleController.php:743
‪TYPO3\CMS\Backend\Template\Components\ButtonBar
Definition: ButtonBar.php:32
‪TYPO3\CMS\Core\Imaging\Icon
Definition: Icon.php:26
‪TYPO3\CMS\Backend\Template\ModuleTemplateFactory
Definition: ModuleTemplateFactory.php:29
‪TYPO3\CMS\Scheduler\CronCommand\NormalizeCommand
Definition: NormalizeCommand.php:28
‪TYPO3\CMS\Fluid\ViewHelpers\Be\InfoboxViewHelper\STATE_INFO
‪const STATE_INFO
Definition: InfoboxViewHelper.php:62
‪TYPO3\CMS\Scheduler\Controller
Definition: SchedulerModuleController.php:18
‪TYPO3\CMS\Core\Registry
Definition: Registry.php:33
‪TYPO3\CMS\Scheduler\Task\Enumeration\Action\LIST
‪const LIST
Definition: Action.php:31
‪TYPO3\CMS\Scheduler\Task\Enumeration\Action\SET_NEXT_EXECUTION_TIME
‪const SET_NEXT_EXECUTION_TIME
Definition: Action.php:35
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getModuleMenu
‪getModuleMenu()
Definition: SchedulerModuleController.php:200
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\executeTasks
‪executeTasks()
Definition: SchedulerModuleController.php:765
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\deleteTask
‪deleteTask()
Definition: SchedulerModuleController.php:434
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\$moduleTemplate
‪ModuleTemplate $moduleTemplate
Definition: SchedulerModuleController.php:92
‪TYPO3\CMS\Core\Core\Environment\isWindows
‪static bool isWindows()
Definition: Environment.php:318
‪TYPO3\CMS\Fluid\ViewHelpers\Be\InfoboxViewHelper\STATE_ERROR
‪const STATE_ERROR
Definition: InfoboxViewHelper.php:65
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\$pageRenderer
‪PageRenderer $pageRenderer
Definition: SchedulerModuleController.php:114
‪TYPO3\CMS\Core\SysLog\Action\Database
Definition: Database.php:24
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:34
‪TYPO3\CMS\Fluid\ViewHelpers\Be\InfoboxViewHelper\STATE_WARNING
‪const STATE_WARNING
Definition: InfoboxViewHelper.php:64
‪TYPO3\CMS\Core\Utility\ArrayUtility\mergeRecursiveWithOverrule
‪static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
Definition: ArrayUtility.php:654
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getRegisteredTaskGroups
‪array getRegisteredTaskGroups()
Definition: SchedulerModuleController.php:1311
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\preprocessData
‪bool preprocessData()
Definition: SchedulerModuleController.php:1158
‪TYPO3\CMS\Core\Localization\LanguageService\sL
‪string sL($input)
Definition: LanguageService.php:161
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\editTaskAction
‪string editTaskAction(string $requestUri)
Definition: SchedulerModuleController.php:527
‪$fields
‪$fields
Definition: pages.php:5
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\$cshKey
‪string $cshKey
Definition: SchedulerModuleController.php:74
‪TYPO3\CMS\Backend\Template\ModuleTemplate
Definition: ModuleTemplate.php:46
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\$backendTemplatePath
‪string $backendTemplatePath
Definition: SchedulerModuleController.php:78
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getModuleContent
‪string getModuleContent(Action $previousAction, string $requestUri)
Definition: SchedulerModuleController.php:234
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\convertToTimestamp
‪int convertToTimestamp(string $input)
Definition: SchedulerModuleController.php:1255
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:43
‪TYPO3\CMS\Core\Type\Enumeration\cast
‪static static cast($value)
Definition: Enumeration.php:186
‪TYPO3\CMS\Core\Messaging\AbstractMessage\WARNING
‪const WARNING
Definition: AbstractMessage.php:30
‪TYPO3\CMS\Scheduler\Task\Enumeration\Action\TOGGLE_HIDDEN
‪const TOGGLE_HIDDEN
Definition: Action.php:37
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\infoScreenAction
‪string infoScreenAction()
Definition: SchedulerModuleController.php:416
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\$action
‪Action $action
Definition: SchedulerModuleController.php:96
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\$iconFactory
‪IconFactory $iconFactory
Definition: SchedulerModuleController.php:113
‪TYPO3\CMS\Scheduler\Task\AbstractTask
Definition: AbstractTask.php:35
‪TYPO3\CMS\Scheduler\Task\Enumeration\Action\STOP
‪const STOP
Definition: Action.php:36
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\toggleDisableAction
‪toggleDisableAction()
Definition: SchedulerModuleController.php:499
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getLanguageService
‪LanguageService getLanguageService()
Definition: SchedulerModuleController.php:1455
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\determineExecutablePath
‪determineExecutablePath()
Definition: SchedulerModuleController.php:392
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController
Definition: SchedulerModuleController.php:58
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:40
‪TYPO3\CMS\Core\Authentication\AbstractUserAuthentication\getModuleData
‪mixed getModuleData($module, $type='')
Definition: AbstractUserAuthentication.php:1054
‪TYPO3\CMS\Scheduler\ProgressProviderInterface
Definition: ProgressProviderInterface.php:22
‪TYPO3\CMS\Scheduler\Scheduler
Definition: Scheduler.php:33
‪TYPO3\CMS\Scheduler\Task\Enumeration\Action\EDIT
‪const EDIT
Definition: Action.php:30
‪TYPO3\CMS\Core\SysLog\Error
Definition: Error.php:24
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\stopTask
‪stopTask()
Definition: SchedulerModuleController.php:473
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\addMessage
‪addMessage($message, $severity=FlashMessage::OK)
Definition: SchedulerModuleController.php:1272
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getCurrentAction
‪Action getCurrentAction()
Definition: SchedulerModuleController.php:192
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\setCurrentAction
‪setCurrentAction(Action $action)
Definition: SchedulerModuleController.php:1428
‪TYPO3\CMS\Fluid\ViewHelpers\Be\InfoboxViewHelper\STATE_OK
‪const STATE_OK
Definition: InfoboxViewHelper.php:63
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\$moduleTemplateFactory
‪ModuleTemplateFactory $moduleTemplateFactory
Definition: SchedulerModuleController.php:116
‪TYPO3\CMS\Scheduler\Task\Enumeration\Action\DELETE
‪const DELETE
Definition: Action.php:29
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\setNextExecutionTimeAction
‪setNextExecutionTimeAction()
Definition: SchedulerModuleController.php:514
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\$MOD_MENU
‪array $MOD_MENU
Definition: SchedulerModuleController.php:102
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\checkScreenAction
‪string checkScreenAction()
Definition: SchedulerModuleController.php:332
‪TYPO3\CMS\Core\Messaging\AbstractMessage\OK
‪const OK
Definition: AbstractMessage.php:29
‪TYPO3\CMS\Scheduler\Task\AbstractTask\TYPE_RECURRING
‪const TYPE_RECURRING
Definition: AbstractTask.php:39
‪TYPO3\CMS\Core\Core\Environment\isComposerMode
‪static bool isComposerMode()
Definition: Environment.php:152
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\$messages
‪array $messages
Definition: SchedulerModuleController.php:70
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:26
‪TYPO3\CMS\Fluid\View\StandaloneView
Definition: StandaloneView.php:31
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication\writelog
‪int writelog($type, $action, $error, $details_nr, $details, $data, $tablename='', $recuid='', $recpid='', $event_pid=-1, $NEWid='', $userId=0)
Definition: BackendUserAuthentication.php:2020
‪TYPO3\CMS\Scheduler\Task\Enumeration\Action
Definition: Action.php:26
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:24
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getServerTime
‪string getServerTime()
Definition: SchedulerModuleController.php:1436
‪TYPO3\CMS\Scheduler\Task\Enumeration\Action\SAVE_CLOSE
‪const SAVE_CLOSE
Definition: Action.php:33
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Scheduler\Task\AbstractTask\TYPE_SINGLE
‪const TYPE_SINGLE
Definition: AbstractTask.php:38
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:43
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getSectionTitle
‪getSectionTitle()
Definition: SchedulerModuleController.php:1442
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\extPath
‪static string extPath($key, $script='')
Definition: ExtensionManagementUtility.php:142
‪TYPO3\CMS\Fluid\ViewHelpers\Be\InfoboxViewHelper
Definition: InfoboxViewHelper.php:59
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\$view
‪StandaloneView $view
Definition: SchedulerModuleController.php:82
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getButtons
‪getButtons(ServerRequestInterface $request)
Definition: SchedulerModuleController.php:1330
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\saveTask
‪saveTask()
Definition: SchedulerModuleController.php:1055
‪TYPO3\CMS\Core\Localization\LanguageService\includeLLFile
‪array includeLLFile($fileRef)
Definition: LanguageService.php:271
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:42
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getRegisteredClasses
‪array getRegisteredClasses()
Definition: SchedulerModuleController.php:1290
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\$uriBuilder
‪UriBuilder $uriBuilder
Definition: SchedulerModuleController.php:115
‪TYPO3\CMS\Core\Localization\LanguageService\getLL
‪string getLL($index)
Definition: LanguageService.php:121
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getBackendUser
‪BackendUserAuthentication getBackendUser()
Definition: SchedulerModuleController.php:1465
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\mainAction
‪ResponseInterface mainAction(ServerRequestInterface $request)
Definition: SchedulerModuleController.php:144
‪TYPO3\CMS\Backend\Template\Components\ButtonBar\BUTTON_POSITION_RIGHT
‪const BUTTON_POSITION_RIGHT
Definition: ButtonBar.php:41
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\makeStatusLabel
‪string makeStatusLabel(array $labels)
Definition: SchedulerModuleController.php:1039
‪TYPO3\CMS\Core\Messaging\AbstractMessage\ERROR
‪const ERROR
Definition: AbstractMessage.php:31
‪TYPO3\CMS\Scheduler\AdditionalFieldProviderInterface
Definition: AdditionalFieldProviderInterface.php:25
‪TYPO3\CMS\Core\Http\HtmlResponse
Definition: HtmlResponse.php:26
‪TYPO3\CMS\Core\SysLog\Type
Definition: Type.php:28
‪TYPO3\CMS\Scheduler\Task\Enumeration\Action\SAVE_NEW
‪const SAVE_NEW
Definition: Action.php:34
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\$submittedData
‪array $submittedData
Definition: SchedulerModuleController.php:63
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\__construct
‪__construct(Scheduler $scheduler, IconFactory $iconFactory, PageRenderer $pageRenderer, UriBuilder $uriBuilder, ModuleTemplateFactory $moduleTemplateFactory)
Definition: SchedulerModuleController.php:118
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\listTasksAction
‪string listTasksAction()
Definition: SchedulerModuleController.php:814