ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
JobTable.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
27
29{
30 private readonly \ILIAS\UI\URLBuilder $url_builder;
31 private readonly \ILIAS\UI\URLBuilderToken $action_parameter_token;
32 private readonly \ILIAS\UI\URLBuilderToken $row_id_token;
33
37 public function __construct(
38 \ilCronManagerGUI $a_parent_obj,
39 string $a_parent_cmd,
40 array $table_action_namespace,
41 string $table_action_param_name,
42 string $table_row_identifier_name,
43 private readonly \ILIAS\UI\Factory $ui_factory,
44 private readonly \Psr\Http\Message\ServerRequestInterface $request,
45 \ilCtrlInterface $ctrl,
46 private readonly \ilLanguage $lng,
47 private readonly \ILIAS\Cron\Job\JobCollection $job_collection,
48 private readonly JobRepository $job_repository,
49 private readonly bool $mayWrite = false
50 ) {
51 $form_action = (new \ILIAS\Data\Factory())->uri(
52 \ilUtil::_getHttpPath() . '/' .
53 $ctrl->getLinkTarget($a_parent_obj, $a_parent_cmd)
54 );
55
56 [
60 ] = (new \ILIAS\UI\URLBuilder($form_action))->acquireParameters(
61 $table_action_namespace,
62 $table_action_param_name,
63 $table_row_identifier_name
64 );
65 }
66
67 public function getRows(
68 \ILIAS\UI\Component\Table\DataRowBuilder $row_builder,
69 array $visible_column_ids,
71 \ILIAS\Data\Order $order,
72 ?array $filter_data,
73 ?array $additional_parameters
74 ): \Generator {
75 foreach ($this->getRecords($range, $order) as $item) {
76 $record = [
77 'title' => $this->formatTitle($item),
78 'component' => $this->formatComponent($item),
79 'schedule' => $this->formatSchedule($item),
80 'status' => (bool) $item->getJobStatus(),
81 'status_info' => $this->formatStatusInfo($item),
82 'result' => $this->formatResult($item),
83 'result_info' => $this->formatResultInfo($item),
84 'last_run' => $this->formatLastRun($item)
85 ];
86
87 if ($item->getJob()->hasFlexibleSchedule()) {
88 if ($item->getScheduleType() === null) {
89 $this->job_repository->updateJobSchedule(
90 $item->getJob(),
91 $item->getEffectiveScheduleType(),
92 $item->getEffectiveScheduleValue()
93 );
94 }
95 } elseif ($item->getScheduleType() !== null) {
96 $this->job_repository->updateJobSchedule($item->getJob(), null, null);
97 }
98
99 $actions_executable = $this->mayWrite && !$item->getRunningTimestamp();
100 $is_crashed = \ILIAS\Cron\Job\JobResult::STATUS_CRASHED === $item->getJobResultStatus();
101 $is_acivated = (bool) $item->getJobStatus();
102
103 $may_reset = $actions_executable && $is_crashed;
104 $may_activate = $actions_executable && !$is_crashed && !$is_acivated;
105 $may_deactivate = $actions_executable && !$is_crashed && $is_acivated;
106 $may_run = $actions_executable && !$is_crashed && $is_acivated && $item->getJob()->isManuallyExecutable();
107 $may_edit = $actions_executable && (
108 $item->getJob()->hasFlexibleSchedule() || $item->getJob()->hasCustomSettings()
109 );
110
111 yield $row_builder
112 ->buildDataRow($item->getEffectiveJobId(), $record)
113 ->withDisabledAction('run', !$may_run)
114 ->withDisabledAction('activate', !$may_activate)
115 ->withDisabledAction('deactivate', !$may_deactivate)
116 ->withDisabledAction('reset', !$may_reset)
117 ->withDisabledAction('edit', !$may_edit);
118 }
119 }
120
121 public function getTotalRowCount(?array $filter_data, ?array $additional_parameters): ?int
122 {
123 return \count($this->job_collection);
124 }
125
129 private function getRecords(\ILIAS\Data\Range $range, \ILIAS\Data\Order $order): array
130 {
131 [$order_field, $order_direction] = $order->join([], static function ($ret, $key, $value) {
132 return [$key, $value];
133 });
134
135 $collection = new OrderedJobEntities(
136 $this->job_collection,
137 match ($order_field) {
140 default => function (JobEntity $left, JobEntity $right) use ($order_field): int {
141 if ($order_field === 'component') {
142 return \ilStr::strCmp($this->formatComponent($left), $this->formatComponent($right));
143 }
144
145 if ($order_field === 'schedule') {
146 return \ilStr::strCmp($this->formatSchedule($left), $this->formatSchedule($right));
147 }
148
149 if ($order_field === 'result') {
150 return \ilStr::strCmp($this->formatResult($left), $this->formatResult($right));
151 }
152
153 if ($order_field === 'last_run') {
154 $left_last_run = 0;
155 if ($left->getRunningTimestamp()) {
156 $left_last_run = strtotime('+1year', $left->getRunningTimestamp());
157 } elseif ($left->getJobResultTimestamp()) {
158 $left_last_run = $left->getJobResultTimestamp();
159 }
160
161 $right_last_run = 0;
162 if ($right->getRunningTimestamp()) {
163 $right_last_run = strtotime('+1year', $right->getRunningTimestamp());
164 } elseif ($right->getJobResultTimestamp()) {
165 $right_last_run = $right->getJobResultTimestamp();
166 }
167
168 return $left_last_run <=> $right_last_run;
169 }
170
171 return 0;
172 }
173 },
174 $order_direction === \ILIAS\Data\Order::DESC
175 );
176
177 $records = \array_slice($collection->toArray(), $range->getStart(), $range->getLength());
178
179 return $records;
180 }
181
182 private function formatTitle(JobEntity $entity): string
183 {
184 $title = implode('', [
185 '<span class="cron-title" id="job-' . $entity->getEffectiveJobId() . '">',
186 $entity->getEffectiveTitle(),
187 '</span>'
188 ]);
189 if ($entity->getJob()->getDescription()) {
190 $title .= implode('', [
191 '<div class="il_Description_no_margin">',
192 $entity->getJob()->getDescription(),
193 '</div>'
194 ]);
195 }
196
197 return $title;
198 }
199
200 private function formatSchedule(JobEntity $entity): string
201 {
202 $schedule = match ($entity->getEffectiveScheduleType()) {
203 JobScheduleType::DAILY => $this->lng->txt('cron_schedule_daily'),
204 JobScheduleType::WEEKLY => $this->lng->txt('cron_schedule_weekly'),
205 JobScheduleType::MONTHLY => $this->lng->txt('cron_schedule_monthly'),
206 JobScheduleType::QUARTERLY => $this->lng->txt('cron_schedule_quarterly'),
207 JobScheduleType::YEARLY => $this->lng->txt('cron_schedule_yearly'),
208 JobScheduleType::IN_MINUTES => \sprintf(
209 $this->lng->txt('cron_schedule_in_minutes'),
211 ),
212 JobScheduleType::IN_HOURS => \sprintf(
213 $this->lng->txt('cron_schedule_in_hours'),
215 ),
216 JobScheduleType::IN_DAYS => \sprintf(
217 $this->lng->txt('cron_schedule_in_days'),
219 )
220 };
221
222 return $schedule;
223 }
224
225 public function formatComponent(JobEntity $entity): string
226 {
227 $component = $entity->getComponent();
228 if ($entity->isPlugin()) {
229 $component = $this->lng->txt('cmps_plugin') . '/' . $component;
230 }
231
232 return $component;
233 }
234
235 private function formatStatusInfo(JobEntity $entity): string
236 {
237 $status_info = [];
238 if ($entity->getJobStatusTimestamp()) {
239 $status_info[] = \ilDatePresentation::formatDate(
241 );
242 }
243
244 if ($entity->getJobStatusType()) {
245 $status_info[] = \ilUserUtil::getNamePresentation($entity->getJobStatusUsrId());
246 } else {
247 $status_info[] = $this->lng->txt('cron_changed_by_crontab');
248 }
249
250 return implode('<br />', $status_info);
251 }
252
253 private function formatResult(JobEntity $entity): string
254 {
255 $result = '-';
256 if ($entity->getJobResultStatus()) {
257 switch ($entity->getJobResultStatus()) {
258 case \ILIAS\Cron\Job\JobResult::STATUS_INVALID_CONFIGURATION:
259 $result = $this->lng->txt('cron_result_status_invalid_configuration');
260 break;
261
262 case \ILIAS\Cron\Job\JobResult::STATUS_NO_ACTION:
263 $result = $this->lng->txt('cron_result_status_no_action');
264 break;
265
266 case \ILIAS\Cron\Job\JobResult::STATUS_OK:
267 $result = $this->lng->txt('cron_result_status_ok');
268 break;
269
270 case \ILIAS\Cron\Job\JobResult::STATUS_CRASHED:
271 $result = $this->lng->txt('cron_result_status_crashed');
272 break;
273
274 case \ILIAS\Cron\Job\JobResult::STATUS_RESET:
275 $result = $this->lng->txt('cron_result_status_reset');
276 break;
277
278 case \ILIAS\Cron\Job\JobResult::STATUS_FAIL:
279 $result = $this->lng->txt('cron_result_status_fail');
280 break;
281 }
282 }
283
284 return $result;
285 }
286
287 private function formatResultInfo(JobEntity $entity): string
288 {
289 $result_info = [];
290 if ($entity->getJobResultDuration()) {
291 $result_info[] = ($entity->getJobResultDuration() / 1000) . ' sec';
292 }
293
294 // #23391 / #11866
295 $resultCode = $entity->getJobResultCode();
296 if (\in_array($resultCode, \ILIAS\Cron\Job\JobResult::getCoreCodes(), true)) {
297 $result_info[] = $this->lng->txt('cro_job_rc_' . $resultCode);
298 } elseif ($entity->getJobResultMessage()) {
299 $result_info[] = $entity->getJobResultMessage();
300 }
301
302 if (\defined('DEVMODE') && DEVMODE && $resultCode) {
303 $result_info[] = $resultCode;
304 }
305
306 if ($entity->getJobResultType()) {
307 $result_info[] = \ilUserUtil::getNamePresentation($entity->getJobResultUsrId());
308 } else {
309 $result_info[] = $this->lng->txt('cron_changed_by_crontab');
310 }
311
312 return implode('<br />', $result_info);
313 }
314
315 private function formatLastRun(JobEntity $entity): string
316 {
317 $last_run = null;
318 if ($entity->getRunningTimestamp()) {
319 $last_run = strtotime('+1year', $entity->getRunningTimestamp());
320 } elseif ($entity->getJobResultTimestamp()) {
321 $last_run = $entity->getJobResultTimestamp();
322 }
323
324 if ($last_run > time()) {
325 $last_run = $this->lng->txt('cron_running_since') . ' ' .
327
328 // job has pinged
329 if ($entity->getAliveTimestamp() !== $entity->getRunningTimestamp()) {
330 $last_run .= '<br />(Ping: ' .
332 }
333 } elseif ($last_run) {
334 $last_run = \ilDatePresentation::formatDate(new \ilDateTime($last_run, IL_CAL_UNIX));
335 }
336
337 return $last_run ?: '-';
338 }
339
343 public function getColumns(): array
344 {
345 return [
346 'title' => $this->ui_factory
347 ->table()
348 ->column()
349 ->text($this->lng->txt('title') . ' / ' . $this->lng->txt('description'))
350 ->withIsSortable(true),
351 'component' => $this->ui_factory
352 ->table()
353 ->column()
354 ->text($this->lng->txt('cron_component'))
355 ->withIsSortable(true)
356 ->withIsOptional(true, false),
357 'schedule' => $this->ui_factory
358 ->table()
359 ->column()
360 ->text($this->lng->txt('cron_schedule'))
361 ->withIsSortable(true),
362 'status' => $this->ui_factory
363 ->table()
364 ->column()
365 ->boolean(
366 $this->lng->txt('cron_status'),
367 $this->ui_factory->symbol()->icon()->custom(
368 'assets/images/standard/icon_ok.svg',
369 $this->lng->txt('cron_status_active'),
370 \ILIAS\UI\Component\Symbol\Icon\Icon::SMALL
371 ),
372 $this->ui_factory->symbol()->icon()->custom(
373 'assets/images/standard/icon_not_ok.svg',
374 $this->lng->txt('cron_status_inactive'),
375 \ILIAS\UI\Component\Symbol\Icon\Icon::SMALL
376 )
377 )
378 ->withIsSortable(true),
379 'status_info' => $this->ui_factory
380 ->table()
381 ->column()
382 ->text($this->lng->txt('cron_status_info'))
383 ->withIsSortable(false),
384 'result' => $this->ui_factory
385 ->table()
386 ->column()
387 ->status($this->lng->txt('cron_result'))
388 ->withIsSortable(true),
389 'result_info' => $this->ui_factory
390 ->table()
391 ->column()
392 ->text($this->lng->txt('cron_result_info'))
393 ->withIsSortable(false),
394 'last_run' => $this->ui_factory
395 ->table()
396 ->column()
397 ->text($this->lng->txt('cron_last_run'))
398 ->withIsSortable(true)
399 ];
400 }
401
405 public function getActions(): array
406 {
407 if (!$this->mayWrite) {
408 return [];
409 }
410
411 return [
412 'run' => $this->ui_factory->table()->action()->single(
413 $this->lng->txt('cron_action_run'),
414 $this->url_builder->withParameter($this->action_parameter_token, 'run'),
415 $this->row_id_token
416 ),
417 'activate' => $this->ui_factory->table()->action()->standard(
418 $this->lng->txt('cron_action_activate'),
419 $this->url_builder->withParameter($this->action_parameter_token, 'activate'),
420 $this->row_id_token
421 ),
422 'deactivate' => $this->ui_factory->table()->action()->standard(
423 $this->lng->txt('cron_action_deactivate'),
424 $this->url_builder->withParameter($this->action_parameter_token, 'deactivate'),
425 $this->row_id_token
426 ),
427 'reset' => $this->ui_factory->table()->action()->standard(
428 $this->lng->txt('cron_action_reset'),
429 $this->url_builder->withParameter($this->action_parameter_token, 'reset'),
430 $this->row_id_token
431 ),
432 'edit' => $this->ui_factory->table()->action()->single(
433 $this->lng->txt('cron_action_edit'),
434 $this->url_builder->withParameter($this->action_parameter_token, 'edit'),
435 $this->row_id_token
436 )
437 ];
438 }
439
441 {
442 return $this->ui_factory
443 ->table()
444 ->data($this, $this->lng->txt('cron_jobs'), $this->getColumns())
445 ->withActions($this->getActions())
446 ->withId(self::class)
447 ->withRequest($this->request)
448 ->withOrder(new \ILIAS\Data\Order('title', \ILIAS\Data\Order::ASC));
449 }
450}
final const int STATUS_CRASHED
Definition: JobResult.php:28
readonly ILIAS UI URLBuilderToken $action_parameter_token
Definition: JobTable.php:31
getRows(\ILIAS\UI\Component\Table\DataRowBuilder $row_builder, array $visible_column_ids, \ILIAS\Data\Range $range, \ILIAS\Data\Order $order, ?array $filter_data, ?array $additional_parameters)
Definition: JobTable.php:67
getRecords(\ILIAS\Data\Range $range, \ILIAS\Data\Order $order)
Definition: JobTable.php:129
readonly ILIAS UI URLBuilder $url_builder
Definition: JobTable.php:30
readonly ILIAS UI URLBuilderToken $row_id_token
Definition: JobTable.php:32
__construct(\ilCronManagerGUI $a_parent_obj, string $a_parent_cmd, array $table_action_namespace, string $table_action_param_name, string $table_row_identifier_name, private readonly \ILIAS\UI\Factory $ui_factory, private readonly \Psr\Http\Message\ServerRequestInterface $request, \ilCtrlInterface $ctrl, private readonly \ilLanguage $lng, private readonly \ILIAS\Cron\Job\JobCollection $job_collection, private readonly JobRepository $job_repository, private readonly bool $mayWrite=false)
Definition: JobTable.php:37
formatTitle(JobEntity $entity)
Definition: JobTable.php:182
formatResult(JobEntity $entity)
Definition: JobTable.php:253
formatResultInfo(JobEntity $entity)
Definition: JobTable.php:287
formatComponent(JobEntity $entity)
Definition: JobTable.php:225
formatLastRun(JobEntity $entity)
Definition: JobTable.php:315
formatStatusInfo(JobEntity $entity)
Definition: JobTable.php:235
formatSchedule(JobEntity $entity)
Definition: JobTable.php:200
getTotalRowCount(?array $filter_data, ?array $additional_parameters)
Mainly for the purpose of pagination-support, it is important to know about the total number of recor...
Definition: JobTable.php:121
Both the subject and the direction need to be specified when expressing an order.
Definition: Order.php:29
const DESC
Definition: Order.php:31
A simple class to express a naive range of whole positive numbers.
Definition: Range.php:29
Definition: UI.php:24
const IL_CAL_UNIX
@ilCtrl_Calls ilCronManagerGUI: ilPropertyFormGUI @ilCtrl_isCalledBy ilCronManagerGUI: ilAdministrati...
static formatDate(ilDateTime $date, bool $a_skip_day=false, bool $a_include_wd=false, bool $include_seconds=false, ?ilObjUser $user=null,)
@classDescription Date and time handling
language handling
static getNamePresentation( $a_user_id, bool $a_user_image=false, bool $a_profile_link=false, string $a_profile_back_link='', bool $a_force_first_lastname=false, bool $a_omit_login=false, bool $a_sortable=true, bool $a_return_data_array=false, $a_ctrl_path=null)
Default behaviour is:
static _getHttpPath()
@template-extends \IteratorAggregate<\ILIAS\Cron\Job\JobEntity>
This describes a symbol.
Definition: Symbol.php:30
This is how the factory for UI elements looks.
Definition: Factory.php:38
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getLinkTarget(object $a_gui_obj, ?string $a_cmd=null, ?string $a_anchor=null, bool $is_async=false, bool $has_xml_style=false)
Returns a link target for the given information.
Interface Observer \BackgroundTasks Contains several chained tasks and infos about them.
global $lng
Definition: privfeed.php:31