ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
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;
51
53{
55 private readonly Closure $confirmation;
58
62 public function __construct(
63 private readonly Config $config,
64 private readonly Container $container,
65 private readonly UI $ui,
66 ?Closure $confirmation = null,
68 ?Factory $refinery = null
69 ) {
70 $this->confirmation = $confirmation ?? fn() => new Confirmation($this->container->language());
71 $this->http_wrapper = $http_wrapper ?? $this->container->http()->wrapper();
72 $this->refinery = $refinery ?? $this->container->refinery();
73 }
74
78 public function deleteDocumentsConfirmation(string $form_link, string $submit_command, string $cancel_command, array $documents): string
79 {
80 $items = array_column(array_map(fn($x) => [$x->id(), $x->content()->title()], $documents), 1, 0);
81
82 return (($this->confirmation)())->render(
83 $form_link,
84 $submit_command,
85 $cancel_command,
86 $this->ui->txt('sure_delete_documents_p'),
87 $items
88 );
89 }
90
94 public function deleteDocuments(array $documents): void
95 {
96 array_map(
97 $this->config->legalDocuments()->document()->repository()->deleteDocument(...),
98 $documents
99 );
100 }
101
105 public function withDocumentAndCriterion(Closure $proc): void
106 {
107 $document = $this->currentDocument()->value();
108 $criterion_id = ($this->container->http()->request()->getQueryParams()['criterion_id'] ?? null);
109 if (null === $criterion_id) {
110 throw new InvalidArgumentException('Missing query parameter criterion_id.');
111 }
112 $criterion_id = (int) $criterion_id;
113
114 $criterion = $this->find(
115 fn($criterion) => $criterion->id() === $criterion_id,
116 $document->criteria()
117 );
118 if (!$criterion) {
119 throw new InvalidArgumentException('Invalid criterion_id given.');
120 }
121
122 $proc($document, $criterion);
123 }
124
128 public function retrieveDocuments(): array
129 {
130 $ids = $this->retrieveIds();
131 $documents = $this->config->legalDocuments()->document()->repository()->select($ids);
132 if (count($documents) !== count($ids)) {
133 throw new InvalidArgumentException('List contains invalid documents.');
134 }
135
136 return $documents;
137 }
138
142 public function retrieveIds(): array
143 {
144 $ids = $this->retrieveValueOrDefaultFromPost(
145 'ids',
146 $this->container->refinery()->kindlyTo()->listOf(
147 $this->container->refinery()->kindlyTo()->int()
148 )
149 );
150
151 if (!$ids) {
152 //Try reading from UI-Table action
154 'legal_document_id',
155 $this->refinery->kindlyTo()->listOf($this->refinery->kindlyTo()->int()),
156 []
157 );
158 }
159
160 if (!$ids) {
161 //Try reading from UI-Table "apply to all objects"
163 'legal_document_id',
164 $this->refinery->kindlyTo()->listOf($this->refinery->kindlyTo()->string()),
165 []
166 );
167
168 if (current($ids) === 'ALL_OBJECTS') {
169 $ids = [];
170 foreach ($this->config->legalDocuments()->document()->repository()->all() as $document) {
171 $ids[] = $document->id();
172 }
173 }
174 }
175
176 return $ids ?: [];
177 }
178
179 private function retrieveValueOrDefaultFromPost(string $key, Transformation $transformation, mixed $default = null): mixed
180 {
181 return $this->container->http()->wrapper()->post()->retrieve(
182 $key,
183 $this->container->refinery()->byTrying([
184 $transformation,
185 $this->container->refinery()->always($default)
186 ])
187 );
188 }
189
190 private function retrieveValueOrDefaultFromQuery(string $key, Transformation $transformation, mixed $default = null): mixed
191 {
192 return $this->container->http()->wrapper()->query()->retrieve(
193 $key,
194 $this->container->refinery()->byTrying([
195 $transformation,
196 $this->container->refinery()->always($default)
197 ])
198 );
199 }
200
204 public function idOrHash(object $gui, Closure $then): void
205 {
206 $with_doc_id = fn($document) => $then($this->willLinkWith($gui, ['doc_id' => $document->id()]), $document->content()->title(), new NumberId($document), false);
207 $with_hash = fn(string $hash) => $then($this->willLinkWith($gui, ['hash' => $hash]), '', new HashId($hash), true);
208 $try_hash = fn() => new Ok($with_hash($this->requireDocumentHash()));
209
210 $this->currentDocument()
211 ->map($with_doc_id)
212 ->except($try_hash)
213 ->value();
214
215 }
216
217 public function targetWithDoc(object $gui, Document $document, string $cmd, string $method = 'getLinkTarget'): string
218 {
219 $link = $this->willLinkWith($gui, ['doc_id' => (string) $document->id()]);
220 return $link($cmd, $method);
221 }
222
223 public function targetWithDocAndCriterion(object $gui, Document $document, Criterion $criterion, string $cmd, string $method = 'getLinkTarget'): string
224 {
225 $link = $this->willLinkWith($gui, [
226 'doc_id' => (string) $document->id(),
227 'criterion_id' => (string) $criterion->id(),
228 ]);
229
230 return $link($cmd, $method);
231 }
232
238 public function willLinkWith($gui, array $parameters = []): Closure
239 {
240 $class = is_string($gui) ? $gui : $gui::class;
241 return function (string $cmd, ?string $method = null) use ($gui, $class, $parameters): string {
242 $method ??= $class === $gui ? 'getLinkTargetByClass' : 'getLinkTarget';
243 $array = $this->container->ctrl()->getParameterArrayByClass($class);
244 foreach ($parameters as $key => $value) {
245 $this->container->ctrl()->setParameterByClass($class, (string) $key, $value);
246 }
247 $link = $this->container->ctrl()->$method($gui, $cmd);
248 foreach ($parameters as $key => $_) {
249 $this->container->ctrl()->setParameterByClass($class, (string) $key, $array[$key] ?? '');
250 }
251
252 return $link;
253 };
254 }
255
259 public function withFormData(Form $form, Closure $then): Form
260 {
261 $request = $this->container->http()->request();
262 if ($request->getMethod() !== 'POST') {
263 return $form;
264 }
265 $form = $form->withRequest($request);
266 $data = $form->getData();
267
268 if ($data !== null) {
269 $then($data);
270 }
271
272 return $form;
273 }
274
281 public function find(Closure $predicate, array $array)
282 {
283 foreach ($array as $value) {
284 if ($predicate($value)) {
285 return $value;
286 }
287 }
288
289 return null;
290 }
291
295 public function currentDocument(): Result
296 {
297 $doc_id = $this->retrieveValueOrDefaultFromQuery(
298 'doc_id',
299 $this->refinery->kindlyTo()->int(),
300 );
301
302 if (!$doc_id) {
303 //Try reading from UI-Table action
304 $doc_id = $this->retrieveValueOrDefaultFromQuery(
305 'legal_document_id',
306 $this->refinery->kindlyTo()->listOf($this->refinery->kindlyTo()->int()),
307 []
308 );
309 $doc_id = current($doc_id) ?: null;
310 }
311
312 $repo = $this->config->legalDocuments()->document()->repository();
313
314 return $this->refinery->kindlyTo()->int()->applyTo(new Ok($doc_id))->then($repo->find(...));
315 }
316
317 public function criterionForm(string $url, Document $document, ?CriterionContent $criterion = null): Form
318 {
319 $groups = $this->config->legalDocuments()->document()->conditionGroups($criterion);
320 $group = $this->ui->create()->input()->field()->switchableGroup($groups->choices(), $this->ui->txt('form_criterion'));
321 $value = $criterion ? $criterion->type() : $groups->defaultSelection();
322 if ($value) {
323 $group = $group->withValue($value);
324 }
325
326 $title = $this->ui->create()->input()->field()->text($this->ui->txt('form_document'))->withValue($document->content()->title())->withDisabled(true);
327
328 $section = $this->ui->create()->input()->field()->section([
329 $title,
330 'content' => $group,
331 ], $this->ui->txt($criterion ? 'form_edit_criterion_head' : 'form_attach_criterion_head'));
332
333 return $this->ui->create()->input()->container()->form()->standard($url, [$section]);
334 }
335
336 public function requireDocumentHash(): string
337 {
338 return $this->container->http()->wrapper()->query()->retrieve('hash', $this->container->refinery()->to()->string());
339 }
340
345 public function tabs(array $tabs, array $run_after = []): void
346 {
347 foreach ($tabs as $tab) {
348 $this->addTab(...$tab);
349 if (isset($run_after[$tab[0]])) {
350 $run_after[$tab[0]]();
351 }
352 }
353 }
354
355 public function uploadContent(): string
356 {
357 $value = null;
358 $upload = $this->container->upload();
359 $upload->register(new PreProcessor(function (string $content) use (&$value): void {
360 $value = $content;
361 }));
362 $upload->process();
363 $result_array = $upload->getResults();
364 if (count($result_array) !== 1 || !current($result_array)->isOk()) {
365 throw new Exception('Unexpected upload result.');
366 }
367
368 return $value;
369 }
370
374 public function setContent($component): void
375 {
376 $this->ui->mainTemplate()->setContent($this->render($component));
377 }
378
379 public function addDocumentButton(string $add_document_link): Component
380 {
381 return $this->ui->create()->button()->primary(
382 $this->ui->txt('add_document_btn_label'),
383 $add_document_link
384 );
385 }
386
390 public function setVariable(string $variable, $component): void
391 {
392 $this->ui->mainTemplate()->setVariable($variable, $this->render($component));
393 }
394
398 public function render($component): string
399 {
400 if (is_string($component)) {
401 return $component;
402 }
403 return $this->container->ui()->renderer()->render($component);
404 }
405
409 public function resetBox(DateTimeImmutable $reset_date, array $buttons = []): Component
410 {
411 $reset_date = new ilDateTime($reset_date->getTimeStamp(), IL_CAL_UNIX);
412 return $this->ui->create()
413 ->messageBox()
414 ->info(sprintf($this->ui->txt('last_reset_date'), ilDatePresentation::formatDate($reset_date)))
415 ->withButtons($buttons);
416 }
417
418 public function resetButton(string $confirm_reset_link): Button
419 {
420 return $this->ui->create()->button()->standard(
421 $this->ui->txt('reset_for_all_users'),
422 $confirm_reset_link
423 );
424 }
425
430 public function documentForm(Closure $link, string $title, Closure $document_content, bool $may_be_new): Form
431 {
432 $edit_link = $link('editDocument');
433 $content_title = $may_be_new ? 'form_document' : 'form_document_new';
434
435 $section = $this->ui->create()->input()->field()->section([
436 'title' => $this->ui->create()->input()->field()->text($this->ui->txt('title'))->withRequired(true)->withValue($title),
437 'content' => $this->ui->create()->input()->field()->file(new UploadHandler($link, $document_content, $this->ui->txt(...)), $this->ui->txt($content_title))->withAcceptedMimeTypes([
438 'text/html',
439 'text/plain',
440 ])->withRequired($may_be_new),
441 ], $this->ui->txt($may_be_new ? 'form_new_doc_head' : 'form_edit_doc_head'));
442
443 return $this->ui->create()->input()->container()->form()->standard($edit_link, [$section]);
444 }
445
450 public function saveDocumentOrder(array $documents, array $order_by_document): void
451 {
452 $update = $this->config->legalDocuments()->document()->repository()->updateDocumentOrder(...);
453
454 usort($documents, fn($document, $other) => $order_by_document[$document->id()] - $order_by_document[$other->id()]);
455
456 array_map(
457 fn($document, int $order) => $update(new NumberId($document), $order),
458 $documents,
459 range(10, 10 * count($documents), 10)
460 );
461 }
462
469 public function withDocumentsAndOrder(Closure $proc)
470 {
471 // kindlyTo->int() does not accept numbers of the form "01".
472 $to_int = $this->container->refinery()->byTrying([
473 $this->container->refinery()->kindlyTo()->int(),
474 $this->container->refinery()->in()->series([
475 $this->container->refinery()->to()->string(),
476 $this->container->refinery()->custom()->transformation(fn($s) => ltrim($s, '0') ?: '0'),
477 $this->container->refinery()->kindlyTo()->int(),
478 ]),
479 ]);
480
481 $order = $this->container->http()->request()->getParsedBody();
482 if (!is_array($order)) {
483 throw new InvalidArgumentException('Invalid order given. List of numbers expected.');
484 }
485
486 $order = array_map($to_int, $order);
487 $document_ids = array_map($to_int, array_keys($order));
488 $order = array_combine($document_ids, array_values($order));
489
490 $documents = $this->config->legalDocuments()->document()->repository()->all();
491
492 foreach ($documents as $document) {
493 if (!isset($order[$document->id()])) {
494 $order[$document->id()] = $document->meta()->sorting();
495 }
496 }
497
498 return $proc($documents, $order);
499 }
500
501 public function exitWithJsonResponse($value): void
502 {
503 // ... The content type cannot be set to application/json, because the components/ILIAS/UI/src/templates/js/Input/Field/file.js:392
504 // does not expect that the content type is correct and parses it again ...
505 $this->container->http()->saveResponse($this->container->http()->response()/* ->withHeader('Content-Type', 'application/json') */->withBody(
506 Streams::ofString(json_encode($value))
507 ));
508
509 $this->container->http()->sendResponse();
510 $this->container->http()->close();
511 }
512
513 public function requireEditable(): void
514 {
515 if (!$this->config->editable()) {
516 $this->container['ilErr']->raiseError($this->container->language()->txt('permission_denied'), $this->container['ilErr']->WARNING);
517 }
518 }
519
520 public function externalSettingsMessage(bool $enabled): Component
521 {
522 $message_box = $this->ui->create()->messageBox()->info(
523 $this->ui->txt('withdrawal_usr_deletion') . ': ' . $this->ui->txt($enabled ? 'enabled' : 'disabled')
524 );
525
526 if (!$this->canWriteUserAdministration()) {
527 return $message_box;
528 }
529
530 return $message_box->withLinks([
531 $this->ui->create()->link()->standard(
532 $this->ui->txt('adm_external_setting_edit'),
533 $this->willLinkWith(ilObjUserFolderGUI::class, ['ref_id' => (string) USER_FOLDER_ID])('generalSettings')
534 )
535 ]);
536 }
537
538 public function isInvalidHTML(string $string): bool
539 {
540 return !$this->isValidHTML($string);
541 }
542
543 public function isValidHTML(string $string): bool
544 {
545 return (new ValidHTML())->isTrue($string);
546 }
547
548 public function canReadUserAdministration(): bool
549 {
550 return $this->container->rbac()->system()->checkAccess('read', USER_FOLDER_ID);
551 }
552
553 private function canWriteUserAdministration(): bool
554 {
555 return $this->container->rbac()->system()->checkAccess('write', USER_FOLDER_ID);
556 }
557
558 private function addTab(string $id, string $text, string $link, bool $can_access = true): void
559 {
560 if ($can_access) {
561 $this->container->tabs()->addTab($id, $text, $link);
562 }
563 }
564}
$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 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:33
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