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