‪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;
52 
58 #[BackendController]
60 {
62 
63  public function ‪__construct(
64  protected readonly ‪Scheduler $scheduler,
65  protected readonly ‪TaskSerializer $taskSerializer,
66  protected readonly ‪SchedulerTaskRepository $taskRepository,
67  protected readonly ‪IconFactory $iconFactory,
68  protected readonly ‪UriBuilder $uriBuilder,
69  protected readonly ‪ModuleTemplateFactory $moduleTemplateFactory,
70  protected readonly ‪Context $context,
71  protected readonly ‪TaskService $taskService,
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  $moduleData = $request->getAttribute('moduleData');
95 
96  // Simple actions from list view.
97  if (!empty($parsedBody['action']['toggleHidden'])) {
98  $this->‪toggleDisabledFlag($view, (int)$parsedBody['action']['toggleHidden']);
99  return $this->‪renderListTasksView($view, $moduleData);
100  }
101  if (!empty($queryParams['action']['stop'])) {
102  $this->‪stopTask($view, (int)$queryParams['action']['stop']);
103  return $this->‪renderListTasksView($view, $moduleData);
104  }
105  if (!empty($parsedBody['execute'])) {
106  $this->‪executeTasks($view, (string)$parsedBody['execute']);
107  return $this->‪renderListTasksView($view, $moduleData);
108  }
109  if (!empty($parsedBody['scheduleCron'])) {
110  $this->‪scheduleCrons($view, (string)$parsedBody['scheduleCron']);
111  return $this->‪renderListTasksView($view, $moduleData);
112  }
113 
114  if (!empty($parsedBody['action']['group']['uid'])) {
115  $this->‪groupDisable((int)$parsedBody['action']['group']['uid'], (int)($parsedBody['action']['group']['hidden'] ?? 0));
116  return $this->‪renderListTasksView($view, $moduleData);
117  }
118 
119  if (!empty($parsedBody['action']['delete'])) {
120  $this->‪deleteTask($view, (int)$parsedBody['action']['delete']);
121  return $this->‪renderListTasksView($view, $moduleData);
122  }
123 
124  if (!empty($parsedBody['action']['groupRemove'])) {
125  $rows = $this->‪groupRemove((int)$parsedBody['action']['groupRemove']);
126  if ($rows > 0) {
127  $view->addFlashMessage($this->‪getLanguageService()->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.group.deleted'));
128  } else {
129  $view->addFlashMessage($this->‪getLanguageService()->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.group.delete.failed'), '', ContextualFeedbackSeverity::WARNING);
130  }
131 
132  return $this->‪renderListTasksView($view, $moduleData);
133  }
134 
135  $parsedAction = SchedulerManagementAction::tryFrom($parsedBody['action'] ?? '') ?? ‪SchedulerManagementAction::LIST;
136 
137  if ($parsedAction === SchedulerManagementAction::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 ($parsedAction === SchedulerManagementAction::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  $queryAction = SchedulerManagementAction::tryFrom($queryParams['action'] ?? '') ?? ‪SchedulerManagementAction::LIST;
181  // Add new task form / edit existing task form.
182  if ($queryAction === SchedulerManagementAction::ADD) {
183  return $this->‪renderAddTaskFormView($view, $request);
184  }
185  if ($queryAction === SchedulerManagementAction::EDIT) {
186  return $this->‪renderEditTaskFormView($view, $request);
187  }
188 
189  // Render list if no other action kicked in.
190  return $this->‪renderListTasksView($view, $moduleData);
191  }
192 
197  {
199  }
200 
204  protected function ‪deleteTask(‪ModuleTemplate $view, int $taskUid): void
205  {
206  $languageService = $this->‪getLanguageService();
207  $backendUser = $this->‪getBackendUser();
208  if ($taskUid <= 0) {
209  throw new \RuntimeException('Expecting a valid task uid', 1641670374);
210  }
211  try {
212  // Try to fetch the task and delete it
213  $task = $this->taskRepository->findByUid($taskUid);
214  if ($this->taskRepository->isTaskMarkedAsRunning($task)) {
215  // If the task is currently running, it may not be deleted
216  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.canNotDeleteRunningTask'), ContextualFeedbackSeverity::ERROR);
217  } else {
218  if ($this->taskRepository->remove($task)) {
219  $backendUser->writelog(
220  SystemLogType::EXTENSION,
221  SystemLogDatabaseAction::DELETE,
222  SystemLogErrorClassification::MESSAGE,
223  0,
224  'Scheduler task "%s" (UID: %s, Class: "%s") was deleted',
225  [$task->getTaskTitle(), $task->getTaskUid(), $task->getTaskClassName()]
226  );
227  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.deleteSuccess'));
228  } else {
229  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.deleteError'));
230  }
231  }
232  } catch (\UnexpectedValueException $e) {
233  // The task could not be unserialized, simply update the database record setting it to deleted
234  $result = $this->taskRepository->remove($taskUid);
235  if ($result) {
236  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.deleteSuccess'));
237  } else {
238  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.deleteError'), ContextualFeedbackSeverity::ERROR);
239  }
240  } catch (\OutOfBoundsException $e) {
241  // The task was not found, for some reason
242  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskNotFound'), $taskUid), ContextualFeedbackSeverity::ERROR);
243  }
244  }
245 
251  protected function ‪stopTask(‪ModuleTemplate $view, int $taskUid): void
252  {
253  $languageService = $this->‪getLanguageService();
254  if (!$taskUid > 0) {
255  throw new \RuntimeException('Expecting a valid task uid', 1641670375);
256  }
257  try {
258  // Try to fetch the task and stop it
259  $task = $this->taskRepository->findByUid($taskUid);
260  if ($this->taskRepository->isTaskMarkedAsRunning($task)) {
261  // If the task is indeed currently running, clear marked executions
262  $result = $this->taskRepository->removeAllRegisteredExecutionsForTask($task);
263  if ($result) {
264  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.stopSuccess'));
265  } else {
266  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.stopError'), ContextualFeedbackSeverity::ERROR);
267  }
268  } else {
269  // The task is not running, nothing to unmark
270  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.maynotStopNonRunningTask'), ContextualFeedbackSeverity::WARNING);
271  }
272  } catch (\OutOfBoundsException $e) {
273  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskNotFound'), $taskUid), ContextualFeedbackSeverity::ERROR);
274  } catch (\UnexpectedValueException $e) {
275  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.stopTaskFailed'), $taskUid, $e->getMessage()), ContextualFeedbackSeverity::ERROR);
276  }
277  }
278 
282  protected function ‪toggleDisabledFlag(‪ModuleTemplate $view, int $taskUid): void
283  {
284  $languageService = $this->‪getLanguageService();
285  if (!$taskUid > 0) {
286  throw new \RuntimeException('Expecting a valid task uid to toggle disabled state', 1641670373);
287  }
288  try {
289  $task = $this->taskRepository->findByUid($taskUid);
290  // If a disabled single task is enabled again, register it for a single execution at next scheduler run.
291  $isTaskQueuedForExecution = $task->getType() === ‪AbstractTask::TYPE_SINGLE;
292 
293  // Toggle task state and add a flash message
294  $taskName = $this->‪getHumanReadableTaskName($task);
295  $isTaskDisabled = $task->isDisabled();
296  if ($isTaskDisabled && $isTaskQueuedForExecution) {
297  $task->setDisabled(false);
298  $task->registerSingleExecution($this->context->getAspect('date')->get('timestamp'));
299  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskEnabledAndQueuedForExecution'), $taskName, $taskUid));
300  } elseif ($isTaskDisabled) {
301  $task->setDisabled(false);
302  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskEnabled'), $taskName, $taskUid));
303  } else {
304  $task->setDisabled(true);
305  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskDisabled'), $taskName, $taskUid));
306  }
307  $this->taskRepository->update($task);
308  } catch (\OutOfBoundsException $e) {
309  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskNotFound'), $taskUid), ContextualFeedbackSeverity::ERROR);
310  } catch (\UnexpectedValueException $e) {
311  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.toggleDisableFailed'), $taskUid, $e->getMessage()), ContextualFeedbackSeverity::ERROR);
312  }
313  }
314 
318  protected function ‪renderAddTaskFormView(‪ModuleTemplate $view, ServerRequestInterface $request): ResponseInterface
319  {
320  $languageService = $this->‪getLanguageService();
321  $registeredClasses = $this->taskService->getAvailableTaskTypes();
322  // Class selection can be GET - link and + button in info screen.
323  $queryParams = $request->getQueryParams()['tx_scheduler'] ?? [];
324  $parsedBody = $request->getParsedBody()['tx_scheduler'] ?? [];
325 
326  if ((int)($parsedBody['select_latest_group'] ?? 0) === 1) {
327  $groups = array_column($this->‪getRegisteredTaskGroups(), 'uid');
328  rsort($groups);
329  $selectedTaskGroup = $groups[0] ?? 0;
330  } else {
331  $selectedTaskGroup = 0;
332  }
333 
334  $currentData = [
335  'class' => $parsedBody['class'] ?? $queryParams['class'] ?? key($registeredClasses),
336  'disable' => (bool)($parsedBody['disable'] ?? false),
337  'task_group' => $selectedTaskGroup,
338  'type' => (int)($parsedBody['type'] ?? ‪AbstractTask::TYPE_RECURRING),
339  'start' => $parsedBody['start'] ?? $this->context->getAspect('date')->get('timestamp'),
340  'end' => $parsedBody['end'] ?? 0,
341  'frequency' => $parsedBody['frequency'] ?? '',
342  'multiple' => (bool)($parsedBody['multiple'] ?? false),
343  'description' => $parsedBody['description'] ?? '',
344  ];
345 
346  // Group available tasks by extension name
347  $groupedClasses = [];
348  foreach ($registeredClasses as $class => $classInfo) {
349  $groupedClasses[$classInfo['extension']][$class] = $classInfo;
350  }
351  ksort($groupedClasses);
352 
353  // Additional field provider access $this->getCurrentAction() - Init it for them
354  $this->currentAction = SchedulerManagementAction::ADD;
355  // Get the extra fields to display for each task that needs some.
356  $additionalFields = [];
357  foreach ($registeredClasses as $class => $registrationInfo) {
358  if (!empty($registrationInfo['provider'])) {
360  $providerObject = GeneralUtility::makeInstance($registrationInfo['provider']);
361  if ($providerObject instanceof ‪AdditionalFieldProviderInterface) {
362  // Additional field provider receive form data by reference. But they shouldn't pollute our array here.
363  $parseBodyForProvider = $request->getParsedBody()['tx_scheduler'] ?? [];
364  ‪$fields = $providerObject->getAdditionalFields($parseBodyForProvider, null, $this);
365  if (is_array(‪$fields)) {
366  $additionalFields = $this->‪addPreparedAdditionalFields($additionalFields, ‪$fields, (string)$class);
367  }
368  }
369  }
370  }
371 
372  $view->‪assignMultiple([
373  'currentData' => $currentData,
374  'groupedClasses' => $groupedClasses,
375  'registeredTaskGroups' => $this->‪getRegisteredTaskGroups(),
376  'preSelectedTaskGroup' => (int)($request->getQueryParams()['groupId'] ?? 0),
377  'frequencyOptions' => (array)(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['frequencyOptions'] ?? []),
378  'additionalFields' => $additionalFields,
379  // Adding a group in edit view switches to formEngine. returnUrl is needed to go back to edit view on group record close.
380  'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri(),
381  ]);
384  $view->‪setTitle(
385  $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang_mod.xlf:mlang_tabs_tab'),
386  $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:function.add')
387  );
388  $this->‪addDocHeaderShortcutButton($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:function.add'), 'add');
389  return $view->‪renderResponse('AddTaskForm');
390  }
391 
395  protected function ‪renderEditTaskFormView(‪ModuleTemplate $view, ServerRequestInterface $request, ?int $taskUid = null): ResponseInterface
396  {
397  $languageService = $this->‪getLanguageService();
398  $registeredClasses = $this->taskService->getAvailableTaskTypes();
399  $parsedBody = $request->getParsedBody()['tx_scheduler'] ?? [];
400  $moduleData = $request->getAttribute('moduleData');
401  $taskUid = (int)($taskUid ?? $request->getQueryParams()['uid'] ?? $parsedBody['uid'] ?? 0);
402  if (empty($taskUid)) {
403  throw new \RuntimeException('No valid task uid given to edit task', 1641720929);
404  }
405 
406  try {
407  $taskRecord = $this->taskRepository->findRecordByUid($taskUid);
408  } catch (\OutOfBoundsException $e) {
409  // Task not found - removed meanwhile?
410  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskNotFound'), $taskUid), ContextualFeedbackSeverity::ERROR);
411  return $this->‪renderListTasksView($view, $moduleData);
412  }
413 
414  if (!empty($taskRecord['serialized_executions'])) {
415  // If there's a registered execution, the task should not be edited. May happen if a cron started the task meanwhile.
416  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.maynotEditRunningTask'), ContextualFeedbackSeverity::ERROR);
417  return $this->‪renderListTasksView($view, $moduleData);
418  }
419 
420  $task = null;
421  $isInvalidTask = false;
422  try {
423  $task = $this->taskSerializer->deserialize($taskRecord['serialized_task_object']);
424  $class = $this->taskSerializer->resolveClassName($task);
425  } catch (‪InvalidTaskException) {
426  $isInvalidTask = true;
427  $class = $this->taskSerializer->extractClassName($taskRecord['serialized_task_object']);
428  }
429 
430  if ($isInvalidTask || !isset($registeredClasses[$class]) || !(new ‪TaskValidator())->isValid($task)) {
431  // The task object is not valid anymore. Add flash message and go back to list view.
432  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.invalidTaskClassEdit'), $class), ContextualFeedbackSeverity::ERROR);
433  return $this->‪renderListTasksView($view, $moduleData);
434  }
435 
436  $taskExecution = $task->getExecution();
437  $taskName = $this->‪getHumanReadableTaskName($task);
438  // If an interval or a cron command is defined, it's a recurring task
439  $taskType = (int)($parsedBody['type'] ?? ((empty($taskExecution->getCronCmd()) && empty($taskExecution->getInterval())) ? ‪AbstractTask::TYPE_SINGLE : ‪AbstractTask::TYPE_RECURRING));
440 
441  $currentData = [
442  'class' => $class,
443  'taskName' => $taskName,
444  'disable' => (bool)($parsedBody['disable'] ?? $task->isDisabled()),
445  'task_group' => (int)($parsedBody['task_group'] ?? $task->getTaskGroup()),
446  'type' => $taskType,
447  'start' => $parsedBody['start'] ?? $taskExecution->getStart(),
448  // End for single execution tasks is always 0
449  'end' => $parsedBody['end'] ?? ($taskType === ‪AbstractTask::TYPE_RECURRING ? $taskExecution->getEnd() : 0),
450  // Find current frequency field value depending on task type and interval vs. cron command
451  'frequency' => $parsedBody['frequency'] ?? ($taskType === ‪AbstractTask::TYPE_RECURRING ? ($taskExecution->getInterval() ?: $taskExecution->getCronCmd()) : ''),
452  'multiple' => !($taskType === ‪AbstractTask::TYPE_SINGLE) && (bool)($parsedBody['multiple'] ?? $taskExecution->getMultiple()),
453  'description' => $parsedBody['description'] ?? $task->getDescription(),
454  ];
455 
456  // Additional field provider access $this->getCurrentAction() - Init it for them
457  $this->currentAction = SchedulerManagementAction::EDIT;
458  $additionalFields = [];
459  if (!empty($registeredClasses[$class]['provider'])) {
460  $providerObject = GeneralUtility::makeInstance($registeredClasses[$class]['provider']);
461  if ($providerObject instanceof ‪AdditionalFieldProviderInterface) {
462  // Additional field provider receive form data by reference. But they shouldn't pollute our array here.
463  $parseBodyForProvider = $request->getParsedBody()['tx_scheduler'] ?? [];
464  ‪$fields = $providerObject->getAdditionalFields($parseBodyForProvider, $task, $this);
465  if (is_array(‪$fields)) {
466  $additionalFields = $this->‪addPreparedAdditionalFields($additionalFields, ‪$fields, (string)$class);
467  }
468  }
469  }
470 
471  $view->‪assignMultiple([
472  'uid' => $taskUid,
473  'action' => 'edit',
474  'currentData' => $currentData,
475  'registeredTaskGroups' => $this->‪getRegisteredTaskGroups(),
476  'frequencyOptions' => (array)(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['frequencyOptions'] ?? []),
477  'additionalFields' => $additionalFields,
478  // Adding a group in edit view switches to formEngine. returnUrl is needed to go back to edit view on group record close.
479  'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri(),
480  ]);
483  $view->‪setTitle(
484  $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang_mod.xlf:mlang_tabs_tab'),
485  sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:function.edit'), $taskName)
486  );
487  $this->‪addDocHeaderNewButton($view);
488  $this->‪addDocHeaderDeleteButton($view, $taskUid);
490  $view,
491  sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:function.edit'), $taskName),
492  'edit',
493  $taskUid
494  );
495  return $view->‪renderResponse('EditTaskForm');
496  }
497 
501  protected function ‪executeTasks(‪ModuleTemplate $view, string $taskUids): void
502  {
503  $taskUids = ‪GeneralUtility::intExplode(',', $taskUids, true);
504  if (empty($taskUids)) {
505  throw new \RuntimeException('Expecting a list of task uids to execute', 1641715832);
506  }
507  // Loop selected tasks and execute.
508  $languageService = $this->‪getLanguageService();
509  foreach ($taskUids as ‪$uid) {
510  try {
511  $task = $this->taskRepository->findByUid(‪$uid);
512  $name = $this->‪getHumanReadableTaskName($task);
513  // Try to execute it and report result
514  $result = $this->scheduler->executeTask($task);
515  if ($result) {
516  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.executed'), $name, ‪$uid));
517  } else {
518  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.notExecuted'), $name, ‪$uid), ContextualFeedbackSeverity::ERROR);
519  }
520  $this->scheduler->recordLastRun('manual');
521  } catch (\OutOfBoundsException $e) {
522  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskNotFound'), ‪$uid), ContextualFeedbackSeverity::ERROR);
523  } catch (\‪Exception $e) {
524  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.executionFailed'), ‪$uid, $e->getMessage()), ContextualFeedbackSeverity::ERROR);
525  }
526  }
527  }
528 
532  protected function ‪scheduleCrons(‪ModuleTemplate $view, string $taskUids): void
533  {
534  $taskUids = ‪GeneralUtility::intExplode(',', $taskUids, true);
535  if (empty($taskUids)) {
536  throw new \RuntimeException('Expecting a list of task uids to schedule', 1641715833);
537  }
538  // Loop selected tasks and register for next cron run.
539  $languageService = $this->‪getLanguageService();
540  foreach ($taskUids as ‪$uid) {
541  try {
542  $task = $this->taskRepository->findByUid(‪$uid);
543  $name = $this->‪getHumanReadableTaskName($task);
544  $task->setRunOnNextCronJob(true);
545  if ($task->isDisabled()) {
546  $task->setDisabled(false);
547  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskEnabledAndQueuedForExecution'), $name, ‪$uid));
548  } else {
549  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskQueuedForExecution'), $name, ‪$uid));
550  }
551  $this->taskRepository->update($task);
552  } catch (\OutOfBoundsException $e) {
553  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskNotFound'), ‪$uid), ContextualFeedbackSeverity::ERROR);
554  } catch (\UnexpectedValueException $e) {
555  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.schedulingFailed'), ‪$uid, $e->getMessage()), ContextualFeedbackSeverity::ERROR);
556  }
557  }
558  }
559 
563  protected function ‪renderListTasksView(‪ModuleTemplate $view, ‪ModuleData $moduleData): ResponseInterface
564  {
565  $languageService = $this->‪getLanguageService();
566  $data = $this->taskRepository->getGroupedTasks();
567  $registeredClasses = $this->taskService->getAvailableTaskTypes();
568 
569  $groups = $data['taskGroupsWithTasks'] ?? [];
570  $groups = array_map(
571  static fn(int $key, array $group): array => array_merge($group, ['taskGroupCollapsed' => (bool)($moduleData->‪get('task-group-' . $key, false))]),
572  array_keys($groups),
573  $groups
574  );
575 
576  $view->‪assignMultiple([
577  'groups' => $groups,
578  'groupsWithoutTasks' => $this->‪getGroupsWithoutTasks($groups),
579  'now' => $this->context->getAspect('date')->get('timestamp'),
580  'errorClasses' => $data['errorClasses'],
581  'errorClassesCollapsed' => (bool)($moduleData->‪get('task-group-missing', false)),
582  ]);
583  $view->‪setTitle(
584  $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang_mod.xlf:mlang_tabs_tab'),
585  $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:function.scheduler')
586  );
588  $this->‪addDocHeaderReloadButton($view);
589  if (!empty($registeredClasses)) {
590  $this->‪addDocHeaderAddTaskButton($view);
591  $this->‪addDocHeaderAddTaskGroupButton($view);
592  }
593  $this->‪addDocHeaderShortcutButton($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:function.scheduler'));
594  return $view->‪renderResponse('ListTasks');
595  }
596 
597  protected function ‪isSubmittedTaskDataValid(‪ModuleTemplate $view, ServerRequestInterface $request, bool $isNewTask): bool
598  {
599  $languageService = $this->‪getLanguageService();
600  $parsedBody = $request->getParsedBody()['tx_scheduler'] ?? [];
601  $type = (int)($parsedBody['type'] ?? 0);
602  $startTime = $parsedBody['start'] ?? 0;
603  $endTime = $parsedBody['end'] ?? 0;
604  $result = true;
605  $taskClass = '';
606  if ($isNewTask) {
607  $taskClass = $parsedBody['class'] ?? '';
608  if (!class_exists($taskClass)) {
609  $result = false;
610  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.noTaskClassFound'), ContextualFeedbackSeverity::ERROR);
611  }
612  } else {
613  try {
614  $taskUid = (int)($parsedBody['uid'] ?? 0);
615  $task = $this->taskRepository->findByUid($taskUid);
616  $taskClass = get_class($task);
617  } catch (\OutOfBoundsException|\UnexpectedValueException $e) {
618  $result = false;
619  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.taskNotFound'), $taskUid), ContextualFeedbackSeverity::ERROR);
620  }
621  }
623  $result = false;
624  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.invalidTaskType'), ContextualFeedbackSeverity::ERROR);
625  }
626  if (empty($startTime)) {
627  $result = false;
628  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.noStartDate'), ContextualFeedbackSeverity::ERROR);
629  } else {
630  try {
631  $startTime = $this->‪getTimestampFromDateString($startTime);
632  } catch (‪InvalidDateException $e) {
633  $result = false;
634  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.invalidStartDate'), ContextualFeedbackSeverity::ERROR);
635  }
636  }
637  if ($type === ‪AbstractTask::TYPE_RECURRING && !empty($endTime)) {
638  try {
639  $endTime = $this->‪getTimestampFromDateString($endTime);
640  } catch (‪InvalidDateException $e) {
641  $result = false;
642  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.invalidStartDate'), ContextualFeedbackSeverity::ERROR);
643  }
644  }
645  if ($type === ‪AbstractTask::TYPE_RECURRING && $endTime > 0 && $endTime < $startTime) {
646  $result = false;
647  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.endDateSmallerThanStartDate'), ContextualFeedbackSeverity::ERROR);
648  }
649  if ($type === ‪AbstractTask::TYPE_RECURRING) {
650  if (empty(trim($parsedBody['frequency']))) {
651  $result = false;
652  $this->‪addMessage($view, $languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.noFrequency'), ContextualFeedbackSeverity::ERROR);
653  } elseif (!is_numeric(trim($parsedBody['frequency']))) {
654  try {
655  ‪NormalizeCommand::normalize(trim($parsedBody['frequency']));
656  } catch (\InvalidArgumentException $e) {
657  $result = false;
658  $this->‪addMessage($view, sprintf($languageService->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.frequencyError'), $e->getMessage(), $e->getCode()), ContextualFeedbackSeverity::ERROR);
659  }
660  }
661  }
662  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$taskClass]['additionalFields'])) {
664  $provider = GeneralUtility::makeInstance(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$taskClass]['additionalFields']);
665  if ($provider instanceof ‪AdditionalFieldProviderInterface) {
666  // Providers should add messages for failed validations on their own.
667  $result = $result && $provider->validateAdditionalFields($parsedBody, $this);
668  }
669  }
670  return $result;
671  }
672 
676  protected function ‪createTask(‪ModuleTemplate $view, ServerRequestInterface $request): int
677  {
679  $task = GeneralUtility::makeInstance($request->getParsedBody()['tx_scheduler']['class']);
680  $task = $this->‪setTaskDataFromRequest($task, $request);
681  if (!$this->taskRepository->add($task)) {
682  throw new \RuntimeException('Unable to add task. Possible database error', 1641720169);
683  }
684  $this->‪getBackendUser()->writelog(
685  SystemLogType::EXTENSION,
686  SystemLogDatabaseAction::INSERT,
687  SystemLogErrorClassification::MESSAGE,
688  0,
689  'Scheduler task "%s" (UID: %s, Class: "%s") was added',
690  [$task->getTaskTitle(), $task->getTaskUid(), $task->getTaskClassName()]
691  );
692  $this->‪addMessage($view, $this->‪getLanguageService()->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.addSuccess'));
693  return $task->getTaskUid();
694  }
695 
699  protected function ‪updateTask(‪ModuleTemplate $view, ServerRequestInterface $request): void
700  {
701  $task = $this->taskRepository->findByUid((int)$request->getParsedBody()['tx_scheduler']['uid']);
702  $task = $this->‪setTaskDataFromRequest($task, $request);
703  $this->taskRepository->update($task);
704  $this->‪getBackendUser()->writelog(
705  SystemLogType::EXTENSION,
706  SystemLogDatabaseAction::UPDATE,
707  SystemLogErrorClassification::MESSAGE,
708  0,
709  'Scheduler task "%s" (UID: %s, Class: "%s") was updated',
710  [$task->getTaskTitle(), $task->getTaskUid(), $task->getTaskClassName()]
711  );
712  $this->‪addMessage($view, $this->‪getLanguageService()->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.updateSuccess'));
713  }
714 
715  protected function ‪setTaskDataFromRequest(‪AbstractTask $task, ServerRequestInterface $request): ‪AbstractTask
716  {
717  $parsedBody = $request->getParsedBody()['tx_scheduler'];
718  if ((int)$parsedBody['type'] === ‪AbstractTask::TYPE_SINGLE) {
719  $task->‪registerSingleExecution($this->‪getTimestampFromDateString($parsedBody['start']));
720  } else {
722  $this->‪getTimestampFromDateString($parsedBody['start']),
723  is_numeric($parsedBody['frequency']) ? (int)$parsedBody['frequency'] : 0,
724  !empty($parsedBody['end'] ?? '') ? $this->‪getTimestampFromDateString($parsedBody['end']) : 0,
725  (bool)($parsedBody['multiple'] ?? false),
726  !is_numeric($parsedBody['frequency']) ? $parsedBody['frequency'] : '',
727  );
728  }
729  $task->‪setDisabled($parsedBody['disable'] ?? false);
730  $task->‪setDescription($parsedBody['description'] ?? '');
731  $task->‪setTaskGroup((int)($parsedBody['task_group'] ?? 0));
732  $taskClass = get_class($task);
733  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$taskClass]['additionalFields'])) {
735  $provider = GeneralUtility::makeInstance(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$taskClass]['additionalFields']);
736  if ($provider instanceof ‪AdditionalFieldProviderInterface) {
737  $provider->saveAdditionalFields($parsedBody, $task);
738  }
739  }
740  return $task;
741  }
742 
748  protected function ‪getTimestampFromDateString(string $input): int
749  {
750  if (is_numeric($input)) {
751  // Already looks like a timestamp
752  return (int)$input;
753  }
754  try {
755  // Convert from ISO 8601 dates
756  $dateTime = new \DateTime($input);
757  $value = $dateTime->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:532
‪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:748
‪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:715
‪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: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\Scheduler\Controller\SchedulerModuleController\deleteTask
‪deleteTask(ModuleTemplate $view, int $taskUid)
Definition: SchedulerModuleController.php:204
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\getCurrentAction
‪getCurrentAction()
Definition: SchedulerModuleController.php:196
‪TYPO3\CMS\Scheduler\Task\AbstractTask
Definition: AbstractTask.php:33
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\$currentAction
‪SchedulerManagementAction $currentAction
Definition: SchedulerModuleController.php:61
‪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:395
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\addDocHeaderCloseAndSaveButtons
‪addDocHeaderCloseAndSaveButtons(ModuleTemplate $moduleTemplate)
Definition: SchedulerModuleController.php:843
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController
Definition: SchedulerModuleController.php:60
‪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:318
‪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:61
‪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:676
‪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:251
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\isSubmittedTaskDataValid
‪isSubmittedTaskDataValid(ModuleTemplate $view, ServerRequestInterface $request, bool $isNewTask)
Definition: SchedulerModuleController.php:597
‪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\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:501
‪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:563
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\toggleDisabledFlag
‪toggleDisabledFlag(ModuleTemplate $view, int $taskUid)
Definition: SchedulerModuleController.php:282
‪TYPO3\CMS\Scheduler\Controller\SchedulerModuleController\updateTask
‪updateTask(ModuleTemplate $view, ServerRequestInterface $request)
Definition: SchedulerModuleController.php:699
‪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:751
‪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:63
‪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