ILIAS  trunk Revision v12.0_alpha-377-g3641b37b9db
Administration.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
34use ilDateTime;
35use InvalidArgumentException;
37use Closure;
44use Exception;
45use DateTimeImmutable;
53
55{
57 private readonly Closure $confirmation;
60
64 public function __construct(
65 private readonly Config $config,
66 private readonly Container $container,
67 private readonly UI $ui,
68 ?Closure $confirmation = null,
70 ?Factory $refinery = null
71 ) {
72 $this->confirmation = $confirmation ?? fn() => new Confirmation($this->container->language());
73 $this->http_wrapper = $http_wrapper ?? $this->container->http()->wrapper();
74 $this->refinery = $refinery ?? $this->container->refinery();
75 }
76
80 public function deleteDocumentsConfirmation(string $form_link, string $submit_command, string $cancel_command, array $documents): string
81 {
82 $items = array_column(array_map(fn($x) => [$x->id(), $x->content()->title()], $documents), 1, 0);
83
84 return (($this->confirmation)())->render(
85 $form_link,
86 $submit_command,
87 $cancel_command,
88 $this->ui->txt('sure_delete_documents_p'),
89 $items
90 );
91 }
92
96 public function deleteDocuments(array $documents): void
97 {
98 array_map(
99 $this->config->legalDocuments()->document()->repository()->deleteDocument(...),
100 $documents
101 );
102 }
103
107 public function withDocumentAndCriterion(Closure $proc): void
108 {
109 $document = $this->currentDocument()->value();
110 $criterion_id = ($this->container->http()->request()->getQueryParams()['criterion_id'] ?? null);
111 if (null === $criterion_id) {
112 throw new InvalidArgumentException('Missing query parameter criterion_id.');
113 }
114 $criterion_id = (int) $criterion_id;
115
116 $criterion = $this->find(
117 fn($criterion) => $criterion->id() === $criterion_id,
118 $document->criteria()
119 );
120 if (!$criterion) {
121 throw new InvalidArgumentException('Invalid criterion_id given.');
122 }
123
124 $proc($document, $criterion);
125 }
126
130 public function retrieveDocuments(): array
131 {
132 $ids = $this->retrieveIds();
133 $documents = $this->config->legalDocuments()->document()->repository()->select($ids);
134 if (count($documents) !== count($ids)) {
135 throw new InvalidArgumentException('List contains invalid documents.');
136 }
137
138 return $documents;
139 }
140
144 public function retrieveIds(): array
145 {
146 $ids = $this->retrieveValueOrDefaultFromPost(
147 'ids',
148 $this->container->refinery()->kindlyTo()->listOf(
149 $this->container->refinery()->kindlyTo()->int()
150 )
151 );
152
153 if (!$ids) {
154 //Try reading from UI-Table action
156 'legal_document_id',
157 $this->refinery->kindlyTo()->listOf($this->refinery->kindlyTo()->int()),
158 []
159 );
160 }
161
162 if (!$ids) {
163 //Try reading from UI-Table "apply to all objects"
165 'legal_document_id',
166 $this->refinery->kindlyTo()->listOf($this->refinery->kindlyTo()->string()),
167 []
168 );
169
170 if (current($ids) === 'ALL_OBJECTS') {
171 $ids = [];
172 foreach ($this->config->legalDocuments()->document()->repository()->all() as $document) {
173 $ids[] = $document->id();
174 }
175 }
176 }
177
178 return $ids ?: [];
179 }
180
181 private function retrieveValueOrDefaultFromPost(string $key, Transformation $transformation, mixed $default = null): mixed
182 {
183 return $this->container->http()->wrapper()->post()->retrieve(
184 $key,
185 $this->container->refinery()->byTrying([
186 $transformation,
187 $this->container->refinery()->always($default)
188 ])
189 );
190 }
191
192 private function retrieveValueOrDefaultFromQuery(string $key, Transformation $transformation, mixed $default = null): mixed
193 {
194 return $this->container->http()->wrapper()->query()->retrieve(
195 $key,
196 $this->container->refinery()->byTrying([
197 $transformation,
198 $this->container->refinery()->always($default)
199 ])
200 );
201 }
202
206 public function idOrHash(object $gui, Closure $then): void
207 {
208 $with_doc_id = fn($document) => $then($this->willLinkWith($gui, ['doc_id' => $document->id()]), $document->content()->title(), new NumberId($document), false);
209 $with_hash = fn(string $hash) => $then($this->willLinkWith($gui, ['hash' => $hash]), '', new HashId($hash), true);
210 $try_hash = fn() => new Ok($with_hash($this->requireDocumentHash()));
211
212 $this->currentDocument()
213 ->map($with_doc_id)
214 ->except($try_hash)
215 ->value();
216
217 }
218
219 public function targetWithDoc(object $gui, Document $document, string $cmd, string $method = 'getLinkTarget'): string
220 {
221 $link = $this->willLinkWith($gui, ['doc_id' => (string) $document->id()]);
222 return $link($cmd, $method);
223 }
224
225 public function targetWithDocAndCriterion(object $gui, Document $document, Criterion $criterion, string $cmd, string $method = 'getLinkTarget'): string
226 {
227 $link = $this->willLinkWith($gui, [
228 'doc_id' => (string) $document->id(),
229 'criterion_id' => (string) $criterion->id(),
230 ]);
231
232 return $link($cmd, $method);
233 }
234
240 public function willLinkWith($gui, array $parameters = []): Closure
241 {
242 $class = is_string($gui) ? $gui : $gui::class;
243 return function (string $cmd, ?string $method = null) use ($gui, $class, $parameters): string {
244 $method ??= $class === $gui ? 'getLinkTargetByClass' : 'getLinkTarget';
245 $array = $this->container->ctrl()->getParameterArrayByClass($class);
246 foreach ($parameters as $key => $value) {
247 $this->container->ctrl()->setParameterByClass($class, (string) $key, $value);
248 }
249 $link = $this->container->ctrl()->$method($gui, $cmd);
250 foreach ($parameters as $key => $_) {
251 $this->container->ctrl()->setParameterByClass($class, (string) $key, $array[$key] ?? '');
252 }
253
254 return $link;
255 };
256 }
257
261 public function withFormData(Form $form, Closure $then): Form
262 {
263 $request = $this->container->http()->request();
264 if ($request->getMethod() !== 'POST') {
265 return $form;
266 }
267 $form = $form->withRequest($request);
268 $data = $form->getData();
269
270 if ($data !== null) {
271 $then($data);
272 }
273
274 return $form;
275 }
276
283 public function find(Closure $predicate, array $array)
284 {
285 foreach ($array as $value) {
286 if ($predicate($value)) {
287 return $value;
288 }
289 }
290
291 return null;
292 }
293
297 public function currentDocument(): Result
298 {
299 $doc_id = $this->retrieveValueOrDefaultFromQuery(
300 'doc_id',
301 $this->refinery->kindlyTo()->int(),
302 );
303
304 if (!$doc_id) {
305 //Try reading from UI-Table action
306 $doc_id = $this->retrieveValueOrDefaultFromQuery(
307 'legal_document_id',
308 $this->refinery->kindlyTo()->listOf($this->refinery->kindlyTo()->int()),
309 []
310 );
311 $doc_id = current($doc_id) ?: null;
312 }
313
314 $repo = $this->config->legalDocuments()->document()->repository();
315
316 return $this->refinery->kindlyTo()->int()->applyTo(new Ok($doc_id))->then($repo->find(...));
317 }
318
319 public function criterionForm(string $url, Document $document, ?CriterionContent $criterion = null): Form
320 {
321 $groups = $this->config->legalDocuments()->document()->conditionGroups($criterion);
322 $group = $this->ui->create()->input()->field()->switchableGroup($groups->choices(), $this->ui->txt('form_criterion'));
323 $value = $criterion ? $criterion->type() : $groups->defaultSelection();
324 if ($value) {
325 $group = $group->withValue($value);
326 }
327
328 $this->ui->mainTemplate()->setOnScreenMessage(
329 $this->ui->mainTemplate()::MESSAGE_TYPE_INFO,
330 $this->ui->txt('form_criterion_standard_fields_info_text')
331 );
332
333 $title = $this->ui->create()->input()->field()->text($this->ui->txt('form_document'))->withValue($document->content()->title())->withDisabled(true);
334
335 $section = $this->ui->create()->input()->field()->section([
336 $title,
337 'content' => $group,
338 ], $this->ui->txt($criterion ? 'form_edit_criterion_head' : 'form_attach_criterion_head'));
339
340 return $this->ui->create()->input()->container()->form()->standard($url, [$section]);
341 }
342
343 public function requireDocumentHash(): string
344 {
345 return $this->container->http()->wrapper()->query()->retrieve('hash', $this->container->refinery()->to()->string());
346 }
347
352 public function tabs(array $tabs, array $run_after = []): void
353 {
354 foreach ($tabs as $tab) {
355 $this->addTab(...$tab);
356 if (isset($run_after[$tab[0]])) {
357 $run_after[$tab[0]]();
358 }
359 }
360 }
361
362 public function uploadContent(): string
363 {
364 $value = null;
365 $upload = $this->container->upload();
366 $upload->register(new PreProcessor(function (string $content) use (&$value): void {
367 $value = $content;
368 }));
369 $upload->process();
370 $result_array = $upload->getResults();
371 if (count($result_array) !== 1 || !current($result_array)->isOk()) {
372 throw new Exception('Unexpected upload result.');
373 }
374
375 return $value;
376 }
377
381 public function setContent($component): void
382 {
383 $this->ui->mainTemplate()->setContent($this->render($component));
384 }
385
386 public function addDocumentButton(string $add_document_link): Component
387 {
388 return $this->ui->create()->button()->primary(
389 $this->ui->txt('add_document_btn_label'),
390 $add_document_link
391 );
392 }
393
397 public function setVariable(string $variable, $component): void
398 {
399 $this->ui->mainTemplate()->setVariable($variable, $this->render($component));
400 }
401
405 public function render($component): string
406 {
407 if (is_string($component)) {
408 return $component;
409 }
410 return $this->container->ui()->renderer()->render($component);
411 }
412
416 public function resetBox(DateTimeImmutable $reset_date, array $buttons = []): Component
417 {
418 $reset_date = new ilDateTime($reset_date->getTimeStamp(), IL_CAL_UNIX);
419 return $this->ui->create()
420 ->messageBox()
421 ->info(sprintf($this->ui->txt('last_reset_date'), ilDatePresentation::formatDate($reset_date)))
422 ->withButtons($buttons);
423 }
424
425 public function resetButton(string $confirm_reset_link): Button
426 {
427 return $this->ui->create()->button()->standard(
428 $this->ui->txt('reset_for_all_users'),
429 $confirm_reset_link
430 );
431 }
432
437 public function documentForm(Closure $link, string $title, Closure $document_content, bool $may_be_new): Form
438 {
439 $field = $this->ui->create()->input()->field();
440 $edit_link = $link('editDocument');
441 $content_title = $may_be_new ? 'form_document' : 'form_document_new';
442
443 $require = $this->container->refinery()->custom()->constraint(fn($x) => (bool) $x, $this->ui->txt('title_required'));
444
445 $section = $field->section([
446 'title' => $field->text($this->ui->txt('title'))->withRequired(true, $require)->withValue($title),
447 'content' => $field->file(new UploadHandler($link, $document_content, $this->ui->txt(...)), $this->ui->txt($content_title))->withAcceptedMimeTypes([
448 'text/html',
449 'text/plain',
450 ])->withRequired($may_be_new),
451 ], $this->ui->txt($may_be_new ? 'form_new_doc_head' : 'form_edit_doc_head'));
452
453 return $this->ui->create()->input()->container()->form()->standard($edit_link, [$section]);
454 }
455
460 public function saveDocumentOrder(array $documents, array $order_by_document): void
461 {
462 $update = $this->config->legalDocuments()->document()->repository()->updateDocumentOrder(...);
463
464 usort($documents, fn($document, $other) => $order_by_document[$document->id()] - $order_by_document[$other->id()]);
465
466 array_map(
467 fn($document, int $order) => $update(new NumberId($document), $order),
468 $documents,
469 range(10, 10 * count($documents), 10)
470 );
471 }
472
479 public function withDocumentsAndOrder(Closure $proc)
480 {
481 // kindlyTo->int() does not accept numbers of the form "01".
482 $to_int = $this->container->refinery()->byTrying([
483 $this->container->refinery()->kindlyTo()->int(),
484 $this->container->refinery()->in()->series([
485 $this->container->refinery()->to()->string(),
486 $this->container->refinery()->custom()->transformation(fn($s) => ltrim($s, '0') ?: '0'),
487 $this->container->refinery()->kindlyTo()->int(),
488 ]),
489 ]);
490
491 $order = $this->container->http()->request()->getParsedBody();
492 if (!is_array($order)) {
493 throw new InvalidArgumentException('Invalid order given. List of numbers expected.');
494 }
495
496 $order = array_map($to_int, $order);
497 $document_ids = array_map($to_int, array_keys($order));
498 $order = array_combine($document_ids, array_values($order));
499
500 $documents = $this->config->legalDocuments()->document()->repository()->all();
501
502 foreach ($documents as $document) {
503 if (!isset($order[$document->id()])) {
504 $order[$document->id()] = $document->meta()->sorting();
505 }
506 }
507
508 return $proc($documents, $order);
509 }
510
511 public function exitWithJsonResponse($value): void
512 {
513 // ... The content type cannot be set to application/json, because the components/ILIAS/UI/src/templates/js/Input/Field/file.js:392
514 // does not expect that the content type is correct and parses it again ...
515 $this->container->http()->saveResponse($this->container->http()->response()/* ->withHeader('Content-Type', 'application/json') */->withBody(
516 Streams::ofString(json_encode($value))
517 ));
518
519 $this->container->http()->sendResponse();
520 $this->container->http()->close();
521 }
522
523 public function requireEditable(): void
524 {
525 if (!$this->config->editable()) {
526 $this->container['ilErr']->raiseError($this->container->language()->txt('permission_denied'), $this->container['ilErr']->WARNING);
527 }
528 }
529
530 public function externalSettingsMessage(bool $enabled): Component
531 {
532 $message_box = $this->ui->create()->messageBox()->info(
533 $this->ui->txt('withdrawal_usr_deletion') . ': ' . $this->ui->txt($enabled ? 'enabled' : 'disabled')
534 );
535
536 if (!$this->canWriteUserAdministration()) {
537 return $message_box;
538 }
539
540 $this->container->language()->loadLanguageModule('administration');
541
542 $this->container->ctrl()->setParameterByClass(SettingsGUI::class, 'ref_id', (string) USER_FOLDER_ID);
543 return $message_box->withLinks([
544 $this->ui->create()->link()->standard(
545 $this->ui->txt('adm_external_setting_edit'),
546 $this->container->ctrl()->getLinkTargetByClass([ilAdministrationGUI::class, ilObjUserFolderGUI::class, SettingsGUI::class], 'show')
547 )
548 ]);
549 }
550
551 public function isInvalidHTML(string $string): bool
552 {
553 return !$this->isValidHTML($string);
554 }
555
556 public function isValidHTML(string $string): bool
557 {
558 return (new ValidHTML())->isTrue($string);
559 }
560
561 public function canReadUserAdministration(): bool
562 {
563 return $this->container->rbac()->system()->checkAccess('read', USER_FOLDER_ID);
564 }
565
566 private function canWriteUserAdministration(): bool
567 {
568 return $this->container->rbac()->system()->checkAccess('write', USER_FOLDER_ID);
569 }
570
571 private function addTab(string $id, string $text, string $link, bool $can_access = true): void
572 {
573 if ($can_access) {
574 $this->container->tabs()->addTab($id, $text, $link);
575 }
576 }
577}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
Customizing of pimple-DIC for ILIAS.
Definition: Container.php:36
Builds data types.
Definition: Factory.php:36
A result encapsulates a value or an error and simplifies the handling of those.
Definition: Ok.php:31
Stream factory which enables the user to create streams without the knowledge of the concrete class.
Definition: Streams.php:32
static ofString(string $string)
Creates a new stream with an initial value.
Definition: Streams.php:41
documentForm(Closure $link, string $title, Closure $document_content, bool $may_be_new)
__construct(private readonly Config $config, private readonly Container $container, private readonly UI $ui, ?Closure $confirmation=null, ?WrapperFactory $http_wrapper=null, ?Factory $refinery=null)
deleteDocumentsConfirmation(string $form_link, string $submit_command, string $cancel_command, array $documents)
resetBox(DateTimeImmutable $reset_date, array $buttons=[])
addTab(string $id, string $text, string $link, bool $can_access=true)
targetWithDoc(object $gui, Document $document, string $cmd, string $method='getLinkTarget')
find(Closure $predicate, array $array)
@template A
retrieveValueOrDefaultFromQuery(string $key, Transformation $transformation, mixed $default=null)
addDocumentButton(string $add_document_link)
tabs(array $tabs, array $run_after=[])
willLinkWith($gui, array $parameters=[])
saveDocumentOrder(array $documents, array $order_by_document)
retrieveValueOrDefaultFromPost(string $key, Transformation $transformation, mixed $default=null)
setVariable(string $variable, $component)
withDocumentsAndOrder(Closure $proc)
@template A
withFormData(Form $form, Closure $then)
criterionForm(string $url, Document $document, ?CriterionContent $criterion=null)
idOrHash(object $gui, Closure $then)
resetButton(string $confirm_reset_link)
targetWithDocAndCriterion(object $gui, Document $document, Criterion $criterion, string $cmd, string $method='getLinkTarget')
const IL_CAL_UNIX
Class ilAdministrationGUI.
Class for date presentation.
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
const USER_FOLDER_ID
Definition: constants.php:33
A result encapsulates a value or an error and simplifies the handling of those.
Definition: Result.php:29
A transformation is a function from one datatype to another.
This describes a standard button.
Definition: Standard.php:27
A component is the most general form of an entity in the UI.
Definition: Component.php:28
withRequest(ServerRequestInterface $request)
Get a form like this where data from the request is attached.
getData()
Get the data in the form if all inputs are ok, where the transformation is applied if one was added.
This describes commonalities between all forms.
Definition: Form.php:34
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: Bulky.php:21
$url
Definition: shib_logout.php:68
$container
@noRector
Definition: wac.php:37
$text
Definition: xapiexit.php:21