‪TYPO3CMS  ‪main
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;
22 use ‪TYPO3\CMS\Backend\Attribute\Controller as BackendController;
35 use ‪TYPO3\CMS\Core\SysLog\Action\Database as SystemLogDatabaseAction;
36 use ‪TYPO3\CMS\Core\SysLog\Error as SystemLogErrorClassification;
37 use ‪TYPO3\CMS\Core\SysLog\Type as SystemLogType;
51 
57 #[BackendController]
59 {
61 
62  public function ‪__construct(
63  protected readonly ‪Scheduler $scheduler,
64  protected readonly ‪TaskSerializer $taskSerializer,
65  protected readonly ‪SchedulerTaskRepository $taskRepository,
66  protected readonly ‪IconFactory $iconFactory,
67  protected readonly ‪UriBuilder $uriBuilder,
68  protected readonly ‪ModuleTemplateFactory $moduleTemplateFactory,
69  protected readonly ‪Context $context,
70  protected readonly ‪TaskService $taskService,
71  ) {
72  }
73 
83  public function ‪handleRequest(ServerRequestInterface $request): ResponseInterface
84  {
85  $parsedBody = $request->getParsedBody();
86  $queryParams = $request->getQueryParams();
87 
88  $view = $this->moduleTemplateFactory->create($request);
89  $view->assign('dateFormat', [
90  'day' => ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] ?? 'd-m-y',
91  'time' => ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'] ?? 'H:i',
92  ]);
93 
94  $backendUser = $this->‪getBackendUser();
95  $moduleData = $request->getAttribute('moduleData');
96 
97  // Simple actions from list view.
98  if (!empty($parsedBody['action']['toggleHidden'])) {
99  $this->‪toggleDisabledFlag($view, (int)$parsedBody['action']['toggleHidden']);
100  return $this->‪renderListTasksView($view, $moduleData);
101  }
102  if (!empty($queryParams['action']['stop'])) {
103  // @todo: Same as above.
104  $this->‪stopTask($view, (int)$queryParams['action']['stop']);
105  return $this->‪renderListTasksView($view, $moduleData);
106  }
107  if (!empty($parsedBody['execute'])) {
108  $this->‪executeTasks($view, (string)$parsedBody['execute']);
109  return $this->‪renderListTasksView($view, $moduleData);
110  }
111  if (!empty($parsedBody['scheduleCron'])) {
112  $this->‪scheduleCrons($view, (string)$parsedBody['scheduleCron']);
113  return $this->‪renderListTasksView($view, $moduleData);
114  }
115 
116  if (!empty($parsedBody['action']['group']['uid'])) {
117  $this->‪groupDisable((int)$parsedBody['action']['group']['uid'], (int)($parsedBody['action']['group']['hidden'] ?? 0));
118  return $this->‪renderListTasksView($view, $moduleData);
119  }
120 
121  if (!empty($parsedBody['action']['delete'])) {
122  $this->‪deleteTask($view, (int)$parsedBody['action']['delete']);
123  return $this->‪renderListTasksView($view, $moduleData);
124  }
125 
126  if (!empty($parsedBody['action']['groupRemove'])) {
127  $rows = $this->‪groupRemove((int)$parsedBody['action']['groupRemove']);
128  if ($rows > 0) {
129  $view->addFlashMessage($this->‪getLanguageService()->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.group.deleted'));
130  } else {
131  $view->addFlashMessage($this->‪getLanguageService()->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.group.delete.failed'), '', ContextualFeedbackSeverity::WARNING);
132  }
133 
134  return $this->‪renderListTasksView($view, $moduleData);
135  }
136 
137  if (($parsedBody['action'] ?? '') === ‪Action::ADD
138  && in_array($parsedBody['CMD'] ?? '', ['save', 'saveclose', 'close'], true)
139  ) {
140  // Received data for adding a new task - validate, persist, render requested 'next' action.
141  $isTaskDataValid = $this->‪isSubmittedTaskDataValid($view, $request, true);
142  if (!$isTaskDataValid) {
143  return $this->‪renderAddTaskFormView($view, $request);
144  }
145  $newTaskUid = $this->‪createTask($view, $request);
146  if ($parsedBody['CMD'] === 'close') {
147  return $this->‪renderListTasksView($view, $moduleData);
148  }
149  if ($parsedBody['CMD'] === 'saveclose') {
150  return $this->‪renderListTasksView($view, $moduleData);
151  }
152  if ($parsedBody['CMD'] === 'save') {
153  return $this->‪renderEditTaskFormView($view, $request, $newTaskUid);
154  }
155  }
156 
157  if (($parsedBody['action'] ?? '') === ‪Action::EDIT
158  && in_array($parsedBody['CMD'] ?? '', ['save', 'close', 'saveclose', 'new'], true)
159  ) {
160  // Received data for updating existing task - validate, persist, render requested 'next' action.
161  $isTaskDataValid = $this->‪isSubmittedTaskDataValid($view, $request, false);
162  if (!$isTaskDataValid) {
163  return $this->‪renderEditTaskFormView($view, $request);
164  }
165  $this->‪updateTask($view, $request);
166  if ($parsedBody['CMD'] === 'new') {
167  return $this->‪renderAddTaskFormView($view, $request);
168  }
169  if ($parsedBody['CMD'] === 'close') {
170  return $this->‪renderListTasksView($view, $moduleData);
171  }
172  if ($parsedBody['CMD'] === 'saveclose') {
173  return $this->‪renderListTasksView($view, $moduleData);
174  }
175  if ($parsedBody['CMD'] === 'save') {
176  return $this->‪renderEditTaskFormView($view, $request);
177  }
178  }
179 
180  // Add new task form / edit existing task form.
181  if (($queryParams['action'] ?? '') === ‪Action::ADD) {
182  return $this->‪renderAddTaskFormView($view, $request);
183  }
184  if (($queryParams['action'] ?? '') === ‪Action::EDIT) {
185  return $this->‪renderEditTaskFormView($view, $request);
186  }
187 
188  // Render list if no other action kicked in.
189  return $this->‪renderListTasksView($view, $moduleData);
190  }
191 
195  public function ‪getCurrentAction(): ‪Action
196  {
198  }
199 
203  protected function ‪deleteTask(‪ModuleTemplate $view, int $taskUid): void
204  {
205  $languageService = $this->‪getLanguageService();
206  $backendUser = $this->‪getBackendUser();
207  if ($taskUid <= 0) {
208  throw new \RuntimeException('Expecting a valid task uid', 1641670374);
209  }
210  try {
211  // Try to fetch the task and delete it
212  $task = $this->taskRepository->findByUid($taskUid);
213  if ($this->taskRepository->isTaskMarkedAsRunning($task)) {
214  // If the task is currently running, it may not be deleted
215  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.canNotDeleteRunningTask'), ContextualFeedbackSeverity::ERROR);
216  } else {
217  if ($this->taskRepository->remove($task)) {
218  $backendUser->writelog(
219  SystemLogType::EXTENSION,
220  SystemLogDatabaseAction::DELETE,
221  SystemLogErrorClassification::MESSAGE,
222  0,
223  'Scheduler task "%s" (UID: %s, Class: "%s") was deleted',
224  [$task->getTaskTitle(), $task->getTaskUid(), $task->getTaskClassName()]
225  );
226  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.deleteSuccess'));
227  } else {
228  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.deleteError'));
229  }
230  }
231  } catch (\UnexpectedValueException $e) {
232  // The task could not be unserialized, simply update the database record setting it to deleted
233  $result = $this->taskRepository->remove($taskUid);
234  if ($result) {
235  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.deleteSuccess'));
236  } else {
237  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.deleteError'), ContextualFeedbackSeverity::ERROR);
238  }
239  } catch (\OutOfBoundsException $e) {
240  // The task was not found, for some reason
241  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskNotFound'), $taskUid), ContextualFeedbackSeverity::ERROR);
242  }
243  }
244 
250  protected function ‪stopTask(‪ModuleTemplate $view, int $taskUid): void
251  {
252  $languageService = $this->‪getLanguageService();
253  if (!$taskUid > 0) {
254  throw new \RuntimeException('Expecting a valid task uid', 1641670375);
255  }
256  try {
257  // Try to fetch the task and stop it
258  $task = $this->taskRepository->findByUid($taskUid);
259  if ($this->taskRepository->isTaskMarkedAsRunning($task)) {
260  // If the task is indeed currently running, clear marked executions
261  $result = $this->taskRepository->removeAllRegisteredExecutionsForTask($task);
262  if ($result) {
263  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.stopSuccess'));
264  } else {
265  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.stopError'), ContextualFeedbackSeverity::ERROR);
266  }
267  } else {
268  // The task is not running, nothing to unmark
269  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.maynotStopNonRunningTask'), ContextualFeedbackSeverity::WARNING);
270  }
271  } catch (\OutOfBoundsException $e) {
272  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskNotFound'), $taskUid), ContextualFeedbackSeverity::ERROR);
273  } catch (\UnexpectedValueException $e) {
274  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.stopTaskFailed'), $taskUid, $e->getMessage()), ContextualFeedbackSeverity::ERROR);
275  }
276  }
277 
281  protected function ‪toggleDisabledFlag(‪ModuleTemplate $view, int $taskUid): void
282  {
283  $languageService = $this->‪getLanguageService();
284  if (!$taskUid > 0) {
285  throw new \RuntimeException('Expecting a valid task uid to toggle disabled state', 1641670373);
286  }
287  try {
288  $task = $this->taskRepository->findByUid($taskUid);
289  // If a disabled single task is enabled again, register it for a single execution at next scheduler run.
290  $isTaskQueuedForExecution = $task->getType() === ‪AbstractTask::TYPE_SINGLE;
291 
292  // Toggle task state and add a flash message
293  $taskName = $this->‪getHumanReadableTaskName($task);
294  $isTaskDisabled = $task->isDisabled();
295  if ($isTaskDisabled && $isTaskQueuedForExecution) {
296  $task->setDisabled(false);
297  $task->registerSingleExecution($this->context->getAspect('date')->get('timestamp'));
298  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskEnabledAndQueuedForExecution'), $taskName, $taskUid));
299  } elseif ($isTaskDisabled) {
300  $task->setDisabled(false);
301  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskEnabled'), $taskName, $taskUid));
302  } else {
303  $task->setDisabled(true);
304  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskDisabled'), $taskName, $taskUid));
305  }
306  $this->taskRepository->update($task);
307  } catch (\OutOfBoundsException $e) {
308  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskNotFound'), $taskUid), ContextualFeedbackSeverity::ERROR);
309  } catch (\UnexpectedValueException $e) {
310  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.toggleDisableFailed'), $taskUid, $e->getMessage()), ContextualFeedbackSeverity::ERROR);
311  }
312  }
313 
317  protected function ‪renderAddTaskFormView(‪ModuleTemplate $view, ServerRequestInterface $request): ResponseInterface
318  {
319  $languageService = $this->‪getLanguageService();
320  $registeredClasses = $this->taskService->getAvailableTaskTypes();
321  // Class selection can be GET - link and + button in info screen.
322  $queryParams = $request->getQueryParams()['tx_scheduler'] ?? [];
323  $parsedBody = $request->getParsedBody()['tx_scheduler'] ?? [];
324 
325  if ((int)($parsedBody['select_latest_group'] ?? 0) === 1) {
326  $groups = array_column($this->‪getRegisteredTaskGroups(), 'uid');
327  rsort($groups);
328  $selectedTaskGroup = $groups[0] ?? 0;
329  } else {
330  $selectedTaskGroup = 0;
331  }
332 
333  $currentData = [
334  'class' => $parsedBody['class'] ?? $queryParams['class'] ?? key($registeredClasses),
335  'disable' => (bool)($parsedBody['disable'] ?? false),
336  'task_group' => $selectedTaskGroup,
337  'type' => (int)($parsedBody['type'] ?? ‪AbstractTask::TYPE_RECURRING),
338  'start' => $parsedBody['start'] ?? $this->context->getAspect('date')->get('timestamp'),
339  'end' => $parsedBody['end'] ?? 0,
340  'frequency' => $parsedBody['frequency'] ?? '',
341  'multiple' => (bool)($parsedBody['multiple'] ?? false),
342  'description' => $parsedBody['description'] ?? '',
343  ];
344 
345  // Group available tasks by extension name
346  $groupedClasses = [];
347  foreach ($registeredClasses as $class => $classInfo) {
348  $groupedClasses[$classInfo['extension']][$class] = $classInfo;
349  }
350  ksort($groupedClasses);
351 
352  // Additional field provider access $this->getCurrentAction() - Init it for them
353  $this->currentAction = new ‪Action(‪Action::ADD);
354  // Get the extra fields to display for each task that needs some.
355  $additionalFields = [];
356  foreach ($registeredClasses as $class => $registrationInfo) {
357  if (!empty($registrationInfo['provider'])) {
359  $providerObject = GeneralUtility::makeInstance($registrationInfo['provider']);
360  if ($providerObject instanceof ‪AdditionalFieldProviderInterface) {
361  // Additional field provider receive form data by reference. But they shouldn't pollute our array here.
362  $parseBodyForProvider = $request->getParsedBody()['tx_scheduler'] ?? [];
363  ‪$fields = $providerObject->getAdditionalFields($parseBodyForProvider, null, $this);
364  if (is_array(‪$fields)) {
365  $additionalFields = $this->‪addPreparedAdditionalFields($additionalFields, ‪$fields, (string)$class);
366  }
367  }
368  }
369  }
370 
371  $view->‪assignMultiple([
372  'currentData' => $currentData,
373  'groupedClasses' => $groupedClasses,
374  'registeredTaskGroups' => $this->‪getRegisteredTaskGroups(),
375  'preSelectedTaskGroup' => (int)($request->getQueryParams()['groupId'] ?? 0),
376  'frequencyOptions' => (array)(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['frequencyOptions'] ?? []),
377  'additionalFields' => $additionalFields,
378  // Adding a group in edit view switches to formEngine. returnUrl is needed to go back to edit view on group record close.
379  'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri(),
380  ]);
383  $view->‪setTitle(
384  $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang_mod.xlf:mlang_tabs_tab'),
385  $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:function.add')
386  );
387  $this->‪addDocHeaderShortcutButton($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:function.add'), 'add');
388  return $view->‪renderResponse('AddTaskForm');
389  }
390 
394  protected function ‪renderEditTaskFormView(‪ModuleTemplate $view, ServerRequestInterface $request, ?int $taskUid = null): ResponseInterface
395  {
396  $languageService = $this->‪getLanguageService();
397  $registeredClasses = $this->taskService->getAvailableTaskTypes();
398  $parsedBody = $request->getParsedBody()['tx_scheduler'] ?? [];
399  $moduleData = $request->getAttribute('moduleData');
400  $taskUid = (int)($taskUid ?? $request->getQueryParams()['uid'] ?? $parsedBody['uid'] ?? 0);
401  if (empty($taskUid)) {
402  throw new \RuntimeException('No valid task uid given to edit task', 1641720929);
403  }
404 
405  try {
406  $taskRecord = $this->taskRepository->findRecordByUid($taskUid);
407  } catch (\OutOfBoundsException $e) {
408  // Task not found - removed meanwhile?
409  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskNotFound'), $taskUid), ContextualFeedbackSeverity::ERROR);
410  return $this->‪renderListTasksView($view, $moduleData);
411  }
412 
413  if (!empty($taskRecord['serialized_executions'])) {
414  // If there's a registered execution, the task should not be edited. May happen if a cron started the task meanwhile.
415  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.maynotEditRunningTask'), ContextualFeedbackSeverity::ERROR);
416  return $this->‪renderListTasksView($view, $moduleData);
417  }
418 
419  $task = null;
420  $isInvalidTask = false;
421  try {
422  $task = $this->taskSerializer->deserialize($taskRecord['serialized_task_object']);
423  $class = $this->taskSerializer->resolveClassName($task);
424  } catch (‪InvalidTaskException) {
425  $isInvalidTask = true;
426  $class = $this->taskSerializer->extractClassName($taskRecord['serialized_task_object']);
427  }
428 
429  if ($isInvalidTask || !isset($registeredClasses[$class]) || !(new ‪TaskValidator())->isValid($task)) {
430  // The task object is not valid anymore. Add flash message and go back to list view.
431  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.invalidTaskClassEdit'), $class), ContextualFeedbackSeverity::ERROR);
432  return $this->‪renderListTasksView($view, $moduleData);
433  }
434 
435  $taskExecution = $task->getExecution();
436  $taskName = $this->‪getHumanReadableTaskName($task);
437  // If an interval or a cron command is defined, it's a recurring task
438  $taskType = (int)($parsedBody['type'] ?? ((empty($taskExecution->getCronCmd()) && empty($taskExecution->getInterval())) ? ‪AbstractTask::TYPE_SINGLE : ‪AbstractTask::TYPE_RECURRING));
439 
440  $currentData = [
441  'class' => $class,
442  'taskName' => $taskName,
443  'disable' => (bool)($parsedBody['disable'] ?? $task->isDisabled()),
444  'task_group' => (int)($parsedBody['task_group'] ?? $task->getTaskGroup()),
445  'type' => $taskType,
446  'start' => $parsedBody['start'] ?? $taskExecution->getStart(),
447  // End for single execution tasks is always 0
448  'end' => $parsedBody['end'] ?? ($taskType === ‪AbstractTask::TYPE_RECURRING ? $taskExecution->getEnd() : 0),
449  // Find current frequency field value depending on task type and interval vs. cron command
450  'frequency' => $parsedBody['frequency'] ?? ($taskType === ‪AbstractTask::TYPE_RECURRING ? ($taskExecution->getInterval() ?: $taskExecution->getCronCmd()) : ''),
451  'multiple' => (bool)($parsedBody['multiple'] ?? $taskExecution->getMultiple()),
452  'description' => $parsedBody['description'] ?? $task->getDescription(),
453  ];
454 
455  // Additional field provider access $this->getCurrentAction() - Init it for them
456  $this->currentAction = new ‪Action(‪Action::EDIT);
457  $additionalFields = [];
458  if (!empty($registeredClasses[$class]['provider'])) {
459  $providerObject = GeneralUtility::makeInstance($registeredClasses[$class]['provider']);
460  if ($providerObject instanceof ‪AdditionalFieldProviderInterface) {
461  // Additional field provider receive form data by reference. But they shouldn't pollute our array here.
462  $parseBodyForProvider = $request->getParsedBody()['tx_scheduler'] ?? [];
463  ‪$fields = $providerObject->getAdditionalFields($parseBodyForProvider, $task, $this);
464  if (is_array(‪$fields)) {
465  $additionalFields = $this->‪addPreparedAdditionalFields($additionalFields, ‪$fields, (string)$class);
466  }
467  }
468  }
469 
470  $view->‪assignMultiple([
471  'uid' => $taskUid,
472  'action' => 'edit',
473  'currentData' => $currentData,
474  'registeredTaskGroups' => $this->‪getRegisteredTaskGroups(),
475  'frequencyOptions' => (array)(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['frequencyOptions'] ?? []),
476  'additionalFields' => $additionalFields,
477  // Adding a group in edit view switches to formEngine. returnUrl is needed to go back to edit view on group record close.
478  'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri(),
479  ]);
482  $view->‪setTitle(
483  $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang_mod.xlf:mlang_tabs_tab'),
484  sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:function.edit'), $taskName)
485  );
486  $this->‪addDocHeaderNewButton($view);
487  $this->‪addDocHeaderDeleteButton($view, $taskUid);
489  $view,
490  sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:function.edit'), $taskName),
491  'edit',
492  $taskUid
493  );
494  return $view->‪renderResponse('EditTaskForm');
495  }
496 
500  protected function ‪executeTasks(‪ModuleTemplate $view, string $taskUids): void
501  {
502  $taskUids = ‪GeneralUtility::intExplode(',', $taskUids, true);
503  if (empty($taskUids)) {
504  throw new \RuntimeException('Expecting a list of task uids to execute', 1641715832);
505  }
506  // Loop selected tasks and execute.
507  $languageService = $this->‪getLanguageService();
508  foreach ($taskUids as ‪$uid) {
509  try {
510  $task = $this->taskRepository->findByUid(‪$uid);
511  $name = $this->‪getHumanReadableTaskName($task);
512  // Try to execute it and report result
513  $result = $this->scheduler->executeTask($task);
514  if ($result) {
515  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.executed'), $name, ‪$uid));
516  } else {
517  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.notExecuted'), $name, ‪$uid), ContextualFeedbackSeverity::ERROR);
518  }
519  $this->scheduler->recordLastRun('manual');
520  } catch (\OutOfBoundsException $e) {
521  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskNotFound'), ‪$uid), ContextualFeedbackSeverity::ERROR);
522  } catch (\‪Exception $e) {
523  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.executionFailed'), ‪$uid, $e->getMessage()), ContextualFeedbackSeverity::ERROR);
524  }
525  }
526  }
527 
531  protected function ‪scheduleCrons(‪ModuleTemplate $view, string $taskUids): void
532  {
533  $taskUids = ‪GeneralUtility::intExplode(',', $taskUids, true);
534  if (empty($taskUids)) {
535  throw new \RuntimeException('Expecting a list of task uids to schedule', 1641715833);
536  }
537  // Loop selected tasks and register for next cron run.
538  $languageService = $this->‪getLanguageService();
539  foreach ($taskUids as ‪$uid) {
540  try {
541  $task = $this->taskRepository->findByUid(‪$uid);
542  $name = $this->‪getHumanReadableTaskName($task);
543  $task->setRunOnNextCronJob(true);
544  if ($task->isDisabled()) {
545  $task->setDisabled(false);
546  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskEnabledAndQueuedForExecution'), $name, ‪$uid));
547  } else {
548  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskQueuedForExecution'), $name, ‪$uid));
549  }
550  $this->taskRepository->update($task);
551  } catch (\OutOfBoundsException $e) {
552  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskNotFound'), ‪$uid), ContextualFeedbackSeverity::ERROR);
553  } catch (\UnexpectedValueException $e) {
554  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.schedulingFailed'), ‪$uid, $e->getMessage()), ContextualFeedbackSeverity::ERROR);
555  }
556  }
557  }
558 
562  protected function ‪renderListTasksView(‪ModuleTemplate $view, ‪ModuleData $moduleData): ResponseInterface
563  {
564  $languageService = $this->‪getLanguageService();
565  $tasks = $this->taskRepository->getGroupedTasks();
566  $registeredClasses = $this->taskService->getAvailableTaskTypes();
567 
568  foreach ($tasks['taskGroupsWithTasks'] as $key => $group) {
569  $group['taskGroupCollapsed'] = (bool)($moduleData->‪get('task-group-' . ($key ?? 0), false));
570  }
571 
572  $view->‪assignMultiple([
573  'tasks' => $tasks['taskGroupsWithTasks'],
574  'groupsWithoutTasks' => $this->‪getGroupsWithoutTasks($tasks['taskGroupsWithTasks']),
575  'now' => $this->context->getAspect('date')->get('timestamp'),
576  'errorClasses' => $tasks['errorClasses'],
577  'errorClassesCollapsed' => (bool)($moduleData->‪get('task-group-missing', false)),
578  ]);
579  $view->‪setTitle(
580  $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang_mod.xlf:mlang_tabs_tab'),
581  $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:function.scheduler')
582  );
584  $this->‪addDocHeaderReloadButton($view);
585  if (!empty($registeredClasses)) {
586  $this->‪addDocHeaderAddTaskButton($view);
587  $this->‪addDocHeaderAddTaskGroupButton($view);
588  }
589  $this->‪addDocHeaderShortcutButton($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:function.scheduler'));
590  return $view->‪renderResponse('ListTasks');
591  }
592 
593  protected function ‪isSubmittedTaskDataValid(‪ModuleTemplate $view, ServerRequestInterface $request, bool $isNewTask): bool
594  {
595  $languageService = $this->‪getLanguageService();
596  $parsedBody = $request->getParsedBody()['tx_scheduler'] ?? [];
597  $type = (int)($parsedBody['type'] ?? 0);
598  $startTime = $parsedBody['start'] ?? 0;
599  $endTime = $parsedBody['end'] ?? 0;
600  $result = true;
601  $taskClass = '';
602  if ($isNewTask) {
603  $taskClass = $parsedBody['class'] ?? '';
604  if (!class_exists($taskClass)) {
605  $result = false;
606  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.noTaskClassFound'), ContextualFeedbackSeverity::ERROR);
607  }
608  } else {
609  try {
610  $taskUid = (int)($parsedBody['uid'] ?? 0);
611  $task = $this->taskRepository->findByUid($taskUid);
612  $taskClass = get_class($task);
613  } catch (\OutOfBoundsException|\UnexpectedValueException $e) {
614  $result = false;
615  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskNotFound'), $taskUid), ContextualFeedbackSeverity::ERROR);
616  }
617  }
619  $result = false;
620  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.invalidTaskType'), ContextualFeedbackSeverity::ERROR);
621  }
622  if (empty($startTime)) {
623  $result = false;
624  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.noStartDate'), ContextualFeedbackSeverity::ERROR);
625  } else {
626  try {
627  $startTime = $this->‪getTimestampFromDateString($startTime);
628  } catch (‪InvalidDateException $e) {
629  $result = false;
630  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.invalidStartDate'), ContextualFeedbackSeverity::ERROR);
631  }
632  }
633  if ($type === ‪AbstractTask::TYPE_RECURRING && !empty($endTime)) {
634  try {
635  $endTime = $this->‪getTimestampFromDateString($endTime);
636  } catch (‪InvalidDateException $e) {
637  $result = false;
638  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.invalidStartDate'), ContextualFeedbackSeverity::ERROR);
639  }
640  }
641  if ($type === ‪AbstractTask::TYPE_RECURRING && $endTime > 0 && $endTime < $startTime) {
642  $result = false;
643  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.endDateSmallerThanStartDate'), ContextualFeedbackSeverity::ERROR);
644  }
645  if ($type === ‪AbstractTask::TYPE_RECURRING) {
646  if (empty(trim($parsedBody['frequency']))) {
647  $result = false;
648  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.noFrequency'), ContextualFeedbackSeverity::ERROR);
649  } elseif (!is_numeric(trim($parsedBody['frequency']))) {
650  try {
651  ‪NormalizeCommand::normalize(trim($parsedBody['frequency']));
652  } catch (\InvalidArgumentException $e) {
653  $result = false;
654  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.frequencyError'), $e->getMessage(), $e->getCode()), ContextualFeedbackSeverity::ERROR);
655  }
656  }
657  }
658  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$taskClass]['additionalFields'])) {
660  $provider = GeneralUtility::makeInstance(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$taskClass]['additionalFields']);
661  if ($provider instanceof ‪AdditionalFieldProviderInterface) {
662  // Providers should add messages for failed validations on their own.
663  $result = $result && $provider->validateAdditionalFields($parsedBody, $this);
664  }
665  }
666  return $result;
667  }
668 
672  protected function ‪createTask(‪ModuleTemplate $view, ServerRequestInterface $request): int
673  {
675  $task = GeneralUtility::makeInstance($request->getParsedBody()['tx_scheduler']['class']);
676  $task = $this->‪setTaskDataFromRequest($task, $request);
677  if (!$this->taskRepository->add($task)) {
678  throw new \RuntimeException('Unable to add task. Possible database error', 1641720169);
679  }
680  $this->‪getBackendUser()->writelog(
681  SystemLogType::EXTENSION,
682  SystemLogDatabaseAction::INSERT,
683  SystemLogErrorClassification::MESSAGE,
684  0,
685  'Scheduler task "%s" (UID: %s, Class: "%s") was added',
686  [$task->getTaskTitle(), $task->getTaskUid(), $task->getTaskClassName()]
687  );
688  $this->‪addMessage($view, $this->‪getLanguageService()->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.addSuccess'));
689  return $task->getTaskUid();
690  }
691 
695  protected function ‪updateTask(‪ModuleTemplate $view, ServerRequestInterface $request): void
696  {
697  $task = $this->taskRepository->findByUid((int)$request->getParsedBody()['tx_scheduler']['uid']);
698  $task = $this->‪setTaskDataFromRequest($task, $request);
699  $this->taskRepository->update($task);
700  $this->‪getBackendUser()->writelog(
701  SystemLogType::EXTENSION,
702  SystemLogDatabaseAction::UPDATE,
703  SystemLogErrorClassification::MESSAGE,
704  0,
705  'Scheduler task "%s" (UID: %s, Class: "%s") was updated',
706  [$task->getTaskTitle(), $task->getTaskUid(), $task->getTaskClassName()]
707  );
708  $this->‪addMessage($view, $this->‪getLanguageService()->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.updateSuccess'));
709  }
710 
711  protected function ‪setTaskDataFromRequest(‪AbstractTask $task, ServerRequestInterface $request): ‪AbstractTask
712  {
713  $parsedBody = $request->getParsedBody()['tx_scheduler'];
714  if ((int)$parsedBody['type'] === ‪AbstractTask::TYPE_SINGLE) {
715  $task->‪registerSingleExecution($this->‪getTimestampFromDateString($parsedBody['start']));
716  } else {
718  $this->‪getTimestampFromDateString($parsedBody['start']),
719  is_numeric($parsedBody['frequency']) ? (int)$parsedBody['frequency'] : 0,
720  !empty($parsedBody['end'] ?? '') ? $this->‪getTimestampFromDateString($parsedBody['end']) : 0,
721  (bool)($parsedBody['multiple'] ?? false),
722  !is_numeric($parsedBody['frequency']) ? $parsedBody['frequency'] : '',
723  );
724  }
725  $task->‪setDisabled($parsedBody['disable'] ?? false);
726  $task->‪setDescription($parsedBody['description'] ?? '');
727  $task->‪setTaskGroup((int)($parsedBody['task_group'] ?? 0));
728  $taskClass = get_class($task);
729  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$taskClass]['additionalFields'])) {
731  $provider = GeneralUtility::makeInstance(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$taskClass]['additionalFields']);
732  if ($provider instanceof ‪AdditionalFieldProviderInterface) {
733  $provider->saveAdditionalFields($parsedBody, $task);
734  }
735  }
736  return $task;
737  }
738 
744  protected function ‪getTimestampFromDateString(string $input): int
745  {
746  if (is_numeric($input)) {
747  // Already looks like a timestamp
748  return (int)$input;
749  }
750  try {
751  // Convert from ISO 8601 dates
752  $dateTime = new \DateTime($input);
753  $value = $dateTime->getTimestamp();
754  if ($value !== 0) {
755  $value -= (int)date('Z', $value);
756  }
757  } catch (\‪Exception $e) {
758  throw new ‪InvalidDateException($e->getMessage(), 1641717510);
759  }
760  return $value;
761  }
762 
766  protected function ‪getRegisteredTaskGroups(): array
767  {
768  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_scheduler_task_group');
769  $queryBuilder->getRestrictions()->removeByType(HiddenRestriction::class);
770 
771  return $queryBuilder->select('*')
772  ->from('tx_scheduler_task_group')
773  ->orderBy('sorting')
774  ->executeQuery()
775  ->fetchAllAssociative();
776  }
777 
781  protected function ‪addPreparedAdditionalFields(array $currentAdditionalFields, array $newAdditionalFields, string $class): array
782  {
783  foreach ($newAdditionalFields as $fieldID => $fieldInfo) {
784  $currentAdditionalFields[] = [
785  'class' => $class,
786  'fieldID' => $fieldID,
787  'htmlClassName' => strtolower(str_replace('\\', '-', $class)),
788  'code' => $fieldInfo['code'] ?? '',
789  'cshKey' => $fieldInfo['cshKey'] ?? '',
790  'cshLabel' => $fieldInfo['cshLabel'] ?? '',
791  'langLabel' => $this->‪getLanguageService()->sL($fieldInfo['label'] ?? ''),
792  'browser' => $fieldInfo['browser'] ?? '',
793  'pageTitle' => $fieldInfo['pageTitle'] ?? '',
794  ];
795  }
796  return $currentAdditionalFields;
797  }
798 
799  protected function ‪addDocHeaderReloadButton(‪ModuleTemplate $moduleTemplate): void
800  {
801  $languageService = $this->‪getLanguageService();
802  $buttonBar = $moduleTemplate->‪getDocHeaderComponent()->getButtonBar();
803  $reloadButton = $buttonBar->makeLinkButton()
804  ->setTitle($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.reload'))
805  ->setIcon($this->iconFactory->getIcon('actions-refresh', ‪Icon::SIZE_SMALL))
806  ->setHref((string)$this->uriBuilder->buildUriFromRoute('scheduler_manage'));
807  $buttonBar->addButton($reloadButton, ‪ButtonBar::BUTTON_POSITION_RIGHT, 1);
808  }
809 
810  protected function ‪addDocHeaderAddTaskButton(‪ModuleTemplate $moduleTemplate): void
811  {
812  $languageService = $this->‪getLanguageService();
813  $buttonBar = $moduleTemplate->‪getDocHeaderComponent()->getButtonBar();
814  $addButton = $buttonBar->makeLinkButton()
815  ->setTitle($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:function.add'))
816  ->setShowLabelText(true)
817  ->setIcon($this->iconFactory->getIcon('actions-plus', ‪Icon::SIZE_SMALL))
818  ->setHref((string)$this->uriBuilder->buildUriFromRoute('scheduler_manage', ['action' => 'add']));
819  $buttonBar->addButton($addButton, ‪ButtonBar::BUTTON_POSITION_LEFT, 2);
820  }
821 
822  private function ‪addDocHeaderAddTaskGroupButton(‪ModuleTemplate $moduleTemplate): void
823  {
824  $languageService = $this->‪getLanguageService();
825  $buttonBar = $moduleTemplate->‪getDocHeaderComponent()->getButtonBar();
826  $addButton = $buttonBar->makeInputButton()
827  ->setTitle($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:function.group.add'))
828  ->setShowLabelText(true)
829  ->setIcon($this->iconFactory->getIcon('actions-plus', ‪Icon::SIZE_SMALL))
830  ->setName('createSchedulerGroup')
831  ->setValue('1')
832  ->setClasses('t3js-create-group');
833  $buttonBar->addButton($addButton, ‪ButtonBar::BUTTON_POSITION_LEFT, 3);
834  }
835 
836  protected function ‪addDocHeaderCloseAndSaveButtons(‪ModuleTemplate $moduleTemplate): void
837  {
838  $languageService = $this->‪getLanguageService();
839  $buttonBar = $moduleTemplate->‪getDocHeaderComponent()->getButtonBar();
840  $closeButton = $buttonBar->makeLinkButton()
841  ->setTitle($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:close'))
842  ->setIcon($this->iconFactory->getIcon('actions-close', ‪Icon::SIZE_SMALL))
843  ->setShowLabelText(true)
844  ->setHref((string)$this->uriBuilder->buildUriFromRoute('scheduler_manage'));
845  $buttonBar->addButton($closeButton, ‪ButtonBar::BUTTON_POSITION_LEFT, 2);
846  $saveButton = $buttonBar->makeInputButton()
847  ->setName('CMD')
848  ->setValue('save')
849  ->setForm('tx_scheduler_form')
850  ->setIcon($this->iconFactory->getIcon('actions-document-save', ‪Icon::SIZE_SMALL))
851  ->setTitle($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:save'))
852  ->setShowLabelText(true);
853  $buttonBar->addButton($saveButton, ‪ButtonBar::BUTTON_POSITION_LEFT, 4);
854  }
855 
856  protected function ‪addDocHeaderNewButton(‪ModuleTemplate $moduleTemplate): void
857  {
858  $languageService = $this->‪getLanguageService();
859  $buttonBar = $moduleTemplate->‪getDocHeaderComponent()->getButtonBar();
860  $newButton = $buttonBar->makeInputButton()
861  ->setName('CMD')
862  ->setValue('new')
863  ->setForm('tx_scheduler_form')
864  ->setIcon($this->iconFactory->getIcon('actions-document-new', ‪Icon::SIZE_SMALL))
865  ->setTitle($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:new'))
866  ->setShowLabelText(true);
867  $buttonBar->addButton($newButton, ‪ButtonBar::BUTTON_POSITION_LEFT, 5);
868  }
869 
870  protected function ‪addDocHeaderDeleteButton(‪ModuleTemplate $moduleTemplate, int $taskUid): void
871  {
872  $languageService = $this->‪getLanguageService();
873  $buttonBar = $moduleTemplate->‪getDocHeaderComponent()->getButtonBar();
874  $deleteButton = $buttonBar->makeLinkButton()
875  ->setHref((string)$this->uriBuilder->buildUriFromRoute('scheduler_manage', ['action' => ['delete' => $taskUid]]))
876  ->setClasses('t3js-modal-trigger')
877  ->setDataAttributes([
878  'severity' => 'warning',
879  'title' => $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:delete'),
880  'button-close-text' => $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:cancel'),
881  'bs-content' => $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.delete'),
882  ])
883  ->setIcon($this->iconFactory->getIcon('actions-edit-delete', ‪Icon::SIZE_SMALL))
884  ->setTitle($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:delete'))
885  ->setShowLabelText(true);
886  $buttonBar->addButton($deleteButton, ‪ButtonBar::BUTTON_POSITION_LEFT, 6);
887  }
888 
889  protected function ‪addDocHeaderShortcutButton(‪ModuleTemplate $moduleTemplate, string $name, string $action = '', int $taskUid = 0): void
890  {
891  $buttonBar = $moduleTemplate->‪getDocHeaderComponent()->getButtonBar();
892  $shortcutArguments = [];
893  if ($action) {
894  $shortcutArguments['action'] = $action;
895  }
896  if ($taskUid) {
897  $shortcutArguments['uid'] = $taskUid;
898  }
899  $shortcutButton = $buttonBar->makeShortcutButton()
900  ->setRouteIdentifier('scheduler_manage')
901  ->setDisplayName($name)
902  ->setArguments($shortcutArguments);
903  $buttonBar->addButton($shortcutButton);
904  }
905 
906  protected function ‪getHumanReadableTaskName(‪AbstractTask $task): string
907  {
908  $class = get_class($task);
909  $registeredClasses = $this->taskService->getAvailableTaskTypes();
910  if (!array_key_exists($class, $registeredClasses)) {
911  throw new \RuntimeException('Class ' . $class . ' not found in list of registered task classes', 1641658569);
912  }
913  return $registeredClasses[$class]['title'] . ' (' . $registeredClasses[$class]['extension'] . ')';
914  }
915 
919  protected function ‪addMessage(‪ModuleTemplate $moduleTemplate, string $message, ‪ContextualFeedbackSeverity $severity = ContextualFeedbackSeverity::OK): void
920  {
921  $moduleTemplate->‪addFlashMessage($message, '', $severity);
922  }
923 
924  private function ‪getGroupsWithoutTasks(array $taskGroupsWithTasks): array
925  {
926  $uidGroupsWithTasks = array_filter(array_column($taskGroupsWithTasks, 'groupUid'));
927  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_scheduler_task_group');
928  $queryBuilder->getRestrictions()->removeByType(HiddenRestriction::class);
929  $resultEmptyGroups = $queryBuilder->select('*')
930  ->from('tx_scheduler_task_group')
931  ->orderBy('groupName');
932 
933  // Only add where statement if we have taskGroups to consider.
934  if (!empty($uidGroupsWithTasks)) {
935  $resultEmptyGroups->where($queryBuilder->expr()->notIn('uid', $uidGroupsWithTasks));
936  }
937 
938  return $resultEmptyGroups->executeQuery()->fetchAllAssociative();
939  }
940 
941  private function ‪groupRemove(int $groupId): int
942  {
943  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_scheduler_task_group');
944  return $queryBuilder->update('tx_scheduler_task_group')
945  ->where($queryBuilder->expr()->eq('uid', $groupId))
946  ->set('deleted', 1)
947  ->executeStatement();
948  }
949 
950  private function ‪groupDisable(int $groupId, int $hidden): void
951  {
952  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_scheduler_task_group');
953  $queryBuilder->update('tx_scheduler_task_group')
954  ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($groupId)))
955  ->set('hidden', $hidden)
956  ->executeStatement();
957  }
958 
960  {
961  return ‪$GLOBALS['LANG'];
962  }
963 
965  {
966  return ‪$GLOBALS['BE_USER'];
967  }
968 }
‪TYPO3\CMS\Scheduler\CronCommand\NormalizeCommand\normalize
‪static string normalize($cronCommand)
Definition: NormalizeCommand.php:40
‪TYPO3\CMS\Core\Imaging\Icon\SIZE_SMALL
‪const SIZE_SMALL
Definition: Icon.php:35
‪TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction
Definition: HiddenRestriction.php:27
‪TYPO3\CMS\Scheduler\Task\Enumeration\Action\ADD
‪const ADD
Definition: Action.php:29
‪TYPO3\CMS\Backend\Template\Components\ButtonBar\BUTTON_POSITION_LEFT
‪const BUTTON_POSITION_LEFT
Definition: ButtonBar.php:38
‪TYPO3\CMS\Backend\Template\Components\ButtonBar
Definition: ButtonBar.php:34
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\scheduleCrons
‪scheduleCrons(ModuleTemplate $view, string $taskUids)
Definition: SchedulerModuleController.php:531
‪TYPO3\CMS\Backend\Template\ModuleTemplate\assignMultiple
‪assignMultiple(array $values)
Definition: ModuleTemplate.php:110
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getGroupsWithoutTasks
‪getGroupsWithoutTasks(array $taskGroupsWithTasks)
Definition: SchedulerModuleController.php:924
‪TYPO3\CMS\Core\Imaging\Icon
Definition: Icon.php:26
‪TYPO3\CMS\Backend\Template\ModuleTemplateFactory
Definition: ModuleTemplateFactory.php:33
‪TYPO3\CMS\Scheduler\CronCommand\NormalizeCommand
Definition: NormalizeCommand.php:28
‪TYPO3\CMS\Scheduler\Exception\InvalidDateException
Definition: InvalidDateException.php:26
‪TYPO3\CMS\Scheduler\Controller
Definition: AvailableSchedulerTasksController.php:18
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getTimestampFromDateString
‪getTimestampFromDateString(string $input)
Definition: SchedulerModuleController.php:744
‪TYPO3\CMS\Scheduler\Domain\Repository\SchedulerTaskRepository
Definition: SchedulerTaskRepository.php:36
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\addDocHeaderNewButton
‪addDocHeaderNewButton(ModuleTemplate $moduleTemplate)
Definition: SchedulerModuleController.php:856
‪TYPO3\CMS\Backend\Module\ModuleData
Definition: ModuleData.php:30
‪TYPO3\CMS\Backend\Module\ModuleData\get
‪get(string $propertyName, mixed $default=null)
Definition: ModuleData.php:56
‪TYPO3\CMS\Scheduler\Task\TaskSerializer
Definition: TaskSerializer.php:28
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getRegisteredTaskGroups
‪getRegisteredTaskGroups()
Definition: SchedulerModuleController.php:766
‪TYPO3\CMS\Backend\Attribute\Controller
Definition: Controller.php:27
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getLanguageService
‪getLanguageService()
Definition: SchedulerModuleController.php:959
‪TYPO3\CMS\Backend\Template\ModuleTemplate\renderResponse
‪renderResponse(string $templateFileName='')
Definition: ModuleTemplate.php:129
‪TYPO3\CMS\Scheduler\Task\AbstractTask\setDisabled
‪setDisabled($flag)
Definition: AbstractTask.php:184
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\setTaskDataFromRequest
‪setTaskDataFromRequest(AbstractTask $task, ServerRequestInterface $request)
Definition: SchedulerModuleController.php:711
‪TYPO3\CMS\Core\SysLog\Action\Database
Definition: Database.php:24
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:34
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\handleRequest
‪handleRequest(ServerRequestInterface $request)
Definition: SchedulerModuleController.php:83
‪$fields
‪$fields
Definition: pages.php:5
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:55
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\addMessage
‪addMessage(ModuleTemplate $moduleTemplate, string $message, ContextualFeedbackSeverity $severity=ContextualFeedbackSeverity::OK)
Definition: SchedulerModuleController.php:919
‪TYPO3\CMS\Backend\Template\ModuleTemplate
Definition: ModuleTemplate.php:48
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\addPreparedAdditionalFields
‪addPreparedAdditionalFields(array $currentAdditionalFields, array $newAdditionalFields, string $class)
Definition: SchedulerModuleController.php:781
‪TYPO3\CMS\Core\Type\ContextualFeedbackSeverity
‪ContextualFeedbackSeverity
Definition: ContextualFeedbackSeverity.php:25
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\deleteTask
‪deleteTask(ModuleTemplate $view, int $taskUid)
Definition: SchedulerModuleController.php:203
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\$currentAction
‪Action $currentAction
Definition: SchedulerModuleController.php:60
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getCurrentAction
‪getCurrentAction()
Definition: SchedulerModuleController.php:195
‪TYPO3\CMS\Scheduler\Task\AbstractTask
Definition: AbstractTask.php:33
‪TYPO3\CMS\Backend\Template\ModuleTemplate\makeDocHeaderModuleMenu
‪makeDocHeaderModuleMenu(array $additionalQueryParams=[])
Definition: ModuleTemplate.php:373
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\renderEditTaskFormView
‪renderEditTaskFormView(ModuleTemplate $view, ServerRequestInterface $request, ?int $taskUid=null)
Definition: SchedulerModuleController.php:394
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\addDocHeaderCloseAndSaveButtons
‪addDocHeaderCloseAndSaveButtons(ModuleTemplate $moduleTemplate)
Definition: SchedulerModuleController.php:836
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController
Definition: SchedulerModuleController.php:59
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:42
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getBackendUser
‪getBackendUser()
Definition: SchedulerModuleController.php:964
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\renderAddTaskFormView
‪renderAddTaskFormView(ModuleTemplate $view, ServerRequestInterface $request)
Definition: SchedulerModuleController.php:317
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\groupRemove
‪groupRemove(int $groupId)
Definition: SchedulerModuleController.php:941
‪TYPO3\CMS\Scheduler\Task\AbstractTask\registerSingleExecution
‪registerSingleExecution($timestamp)
Definition: AbstractTask.php:303
‪TYPO3\CMS\Scheduler\Scheduler
Definition: Scheduler.php:36
‪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\addDocHeaderAddTaskGroupButton
‪addDocHeaderAddTaskGroupButton(ModuleTemplate $moduleTemplate)
Definition: SchedulerModuleController.php:822
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:66
‪TYPO3\CMS\Scheduler\Task\AbstractTask\setDescription
‪setDescription($description)
Definition: AbstractTask.php:258
‪TYPO3\CMS\Scheduler\Exception
Definition: Exception.php:26
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\createTask
‪createTask(ModuleTemplate $view, ServerRequestInterface $request)
Definition: SchedulerModuleController.php:672
‪TYPO3\CMS\Scheduler\Task\AbstractTask\TYPE_RECURRING
‪const TYPE_RECURRING
Definition: AbstractTask.php:37
‪TYPO3\CMS\Scheduler\Validation\Validator\TaskValidator
Definition: TaskValidator.php:23
‪TYPO3\CMS\Backend\Template\ModuleTemplate\getDocHeaderComponent
‪getDocHeaderComponent()
Definition: ModuleTemplate.php:192
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪TYPO3\CMS\Scheduler\Task\Enumeration\Action
Definition: Action.php:27
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\addDocHeaderShortcutButton
‪addDocHeaderShortcutButton(ModuleTemplate $moduleTemplate, string $name, string $action='', int $taskUid=0)
Definition: SchedulerModuleController.php:889
‪$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:36
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getHumanReadableTaskName
‪getHumanReadableTaskName(AbstractTask $task)
Definition: SchedulerModuleController.php:906
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\stopTask
‪stopTask(ModuleTemplate $view, int $taskUid)
Definition: SchedulerModuleController.php:250
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static int[] intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:842
‪TYPO3\CMS\Backend\Template\ModuleTemplate\addFlashMessage
‪addFlashMessage(string $messageBody, string $messageTitle='', int|ContextualFeedbackSeverity $severity=ContextualFeedbackSeverity::OK, bool $storeInSession=true)
Definition: ModuleTemplate.php:242
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\isSubmittedTaskDataValid
‪isSubmittedTaskDataValid(ModuleTemplate $view, ServerRequestInterface $request, bool $isNewTask)
Definition: SchedulerModuleController.php:593
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\addDocHeaderAddTaskButton
‪addDocHeaderAddTaskButton(ModuleTemplate $moduleTemplate)
Definition: SchedulerModuleController.php:810
‪TYPO3\CMS\Scheduler\Task\AbstractTask\registerRecurringExecution
‪registerRecurringExecution($start, $interval, $end=0, $multiple=false, $cron_cmd='')
Definition: AbstractTask.php:326
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\addDocHeaderDeleteButton
‪addDocHeaderDeleteButton(ModuleTemplate $moduleTemplate, int $taskUid)
Definition: SchedulerModuleController.php:870
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\groupDisable
‪groupDisable(int $groupId, int $hidden)
Definition: SchedulerModuleController.php:950
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\executeTasks
‪executeTasks(ModuleTemplate $view, string $taskUids)
Definition: SchedulerModuleController.php:500
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:51
‪TYPO3\CMS\Backend\Template\ModuleTemplate\setTitle
‪setTitle(string $title, string $context='')
Definition: ModuleTemplate.php:178
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\renderListTasksView
‪renderListTasksView(ModuleTemplate $view, ModuleData $moduleData)
Definition: SchedulerModuleController.php:562
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\toggleDisabledFlag
‪toggleDisabledFlag(ModuleTemplate $view, int $taskUid)
Definition: SchedulerModuleController.php:281
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\updateTask
‪updateTask(ModuleTemplate $view, ServerRequestInterface $request)
Definition: SchedulerModuleController.php:695
‪TYPO3\CMS\Backend\Template\Components\ButtonBar\BUTTON_POSITION_RIGHT
‪const BUTTON_POSITION_RIGHT
Definition: ButtonBar.php:43
‪TYPO3\CMS\Scheduler\Service\TaskService
Definition: TaskService.php:27
‪TYPO3\CMS\Scheduler\AdditionalFieldProviderInterface
Definition: AdditionalFieldProviderInterface.php:25
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\__construct
‪__construct(protected readonly Scheduler $scheduler, protected readonly TaskSerializer $taskSerializer, protected readonly SchedulerTaskRepository $taskRepository, protected readonly IconFactory $iconFactory, protected readonly UriBuilder $uriBuilder, protected readonly ModuleTemplateFactory $moduleTemplateFactory, protected readonly Context $context, protected readonly TaskService $taskService,)
Definition: SchedulerModuleController.php:62
‪TYPO3\CMS\Scheduler\Task\AbstractTask\setTaskGroup
‪setTaskGroup($taskGroup)
Definition: AbstractTask.php:238
‪TYPO3\CMS\Core\SysLog\Type
Definition: Type.php:28
‪TYPO3\CMS\Scheduler\Exception\InvalidTaskException
Definition: InvalidTaskException.php:27
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\addDocHeaderReloadButton
‪addDocHeaderReloadButton(ModuleTemplate $moduleTemplate)
Definition: SchedulerModuleController.php:799