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