ILIAS  trunk Revision v12.0_alpha-1227-g7ff6d300864
class.ilCtrl.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
23use ILIAS\Refinery\Factory as Refinery;
26use Psr\Http\Message\ServerRequestInterface;
27use GuzzleHttp\Psr7\Response;
28
35class ilCtrl implements ilCtrlInterface
36{
37 protected ?object $exec_object = null;
38 protected ?string $command = null;
39
45 protected array $stacktrace = [];
46
47 public function __construct(
49 protected ilCtrlTokenRepositoryInterface $token_repository,
50 protected ilCtrlPathFactoryInterface $path_factory,
51 protected ilCtrlContextInterface $context,
52 protected ResponseSenderStrategy $response_sender,
53 protected ServerRequestInterface $server_request,
54 protected RequestWrapper $post_parameters,
55 protected RequestWrapper $get_parameters,
56 protected Refinery $refinery,
57 protected ilComponentFactory $component_factory,
58 protected ilCtrlSubject $subject,
59 protected ilCtrlQueryParserInterface $query_parser,
60 ) {
61 }
62
63 public function __clone()
64 {
65 $this->structure = clone $this->structure;
66 }
67
71 public function callBaseClass(?string $a_base_class = null): void
72 {
73 // prioritise the context's baseclass over the given one.
74 $a_base_class = $this->context->getBaseClass() ?? $a_base_class;
75
76 // abort if no baseclass was provided.
77 if (null === $a_base_class) {
78 throw new ilCtrlException(__METHOD__ . " was not given a baseclass and the request doesn't include one either.");
79 }
80
81 // abort if the provided baseclass is unknown.
82 if (!$this->structure->isBaseClass($a_base_class)) {
83 throw new ilCtrlException("Provided class '$a_base_class' is not a baseclass");
84 }
85
86 // in case the baseclass was given by argument,
87 // set the context's baseclass.
88 $this->context->setBaseClass($a_base_class);
89
90 // no null-check needed as previous isBaseClass() was true.
91 $obj_name = $this->structure->getObjNameByName($a_base_class);
92 $this->forwardCommand(new $obj_name());
93 }
94
98 public function forwardCommand(object $a_gui_object)
99 {
100 $class_name = get_class($a_gui_object);
101
102 // @TODO: remove this check once an interface for command classes exists.
103 if (!method_exists($a_gui_object, 'executeCommand')) {
104 throw new ilCtrlException("$class_name doesn't implement executeCommand().");
105 }
106
107 $this->exec_object = $a_gui_object;
108 $this->populateCall($class_name, self::CMD_MODE_PROCESS);
109
110 // with forward command we cannot progress, or set
111 // the current command class. Otherwise, the path-
112 // finding gets mixed up, as it can only be used in
113 // getHTML() method calls.
114 $this->context
115 ->setCmdMode(self::CMD_MODE_PROCESS);
116
117 $this->subject->notify(ilCtrlEvent::COMMAND_CLASS_FORWARD, $class_name);
118
119 return $a_gui_object->executeCommand();
120 }
121
125 public function getHTML(object $a_gui_object, ?array $a_parameters = null): string
126 {
127 $class_name = get_class($a_gui_object);
128
129 // @TODO: remove this check once an interface for command classes exists.
130 if (!method_exists($a_gui_object, 'getHTML')) {
131 throw new ilCtrlException("$class_name doesn't implement getHTML().");
132 }
133
134 $isolatad_structure = $this->structure;
135 $isolated_context = $this->context;
136 $isolated_object = $this->exec_object;
137
138 $this->exec_object = $a_gui_object;
139 $this->populateCall($class_name, self::CMD_MODE_HTML);
140 $this->context
141 ->setCmdClass($class_name)
142 ->setCmdMode(self::CMD_MODE_HTML);
143
144 $html = (null !== $a_parameters) ?
145 $a_gui_object->getHTML($a_parameters) :
146 $a_gui_object->getHTML();
147
148 $this->structure = $isolatad_structure;
149 $this->context = $isolated_context;
150 $this->exec_object = $isolated_object;
151
152 return $html;
153 }
154
158 public function getCmd(?string $fallback_command = null): ?string
159 {
160 $command = $this->getDeterminedCommand() ?? $fallback_command;
161 $this->subject->notify(ilCtrlEvent::COMMAND_DETERMINATION, $command);
162 $this->command = $command;
163
164 return $command ?? ''; // remove null-coalesce
165 }
166
170 public function getCmdClass(): ?string
171 {
172 if (null !== ($cmd_class = $this->context->getCmdClass())) {
173 return strtolower($this->structure->getObjNameByName($cmd_class));
174 }
175
176 return '';
177 }
178
182 public function getNextClass($a_gui_class = null): ?string
183 {
184 if (null === $a_gui_class && null === $this->exec_object) {
185 return '';
186 }
187
188 if (null === $this->context->getPath()) {
189 return '';
190 }
191
192 $next_cid = $this->context->getPath()->getNextCid(
193 $this->getClassByObject($a_gui_class ?? $this->exec_object)
194 );
195
196 if (null !== $next_cid) {
197 return strtolower($this->structure->getObjNameByCid($next_cid) ?? '');
198 }
199
200 return '';
201 }
202
206 public function saveParameter(object $a_gui_obj, $a_parameter): void
207 {
208 $this->saveParameterByClass($this->getClassByObject($a_gui_obj), $a_parameter);
209 }
210
214 public function saveParameterByClass(string $a_class, $a_parameter): void
215 {
216 if (!empty($a_parameter)) {
217 if (is_array($a_parameter)) {
218 foreach ($a_parameter as $parameter) {
219 $this->structure->setPermanentParameterByClass($a_class, $parameter);
220 }
221 } else {
222 $this->structure->setPermanentParameterByClass($a_class, $a_parameter);
223 }
224 }
225 }
226
230 public function setParameter(object $a_gui_obj, string $a_parameter, $a_value): void
231 {
232 $this->setParameterByClass($this->getClassByObject($a_gui_obj), $a_parameter, $a_value);
233 }
234
238 public function setParameterByClass(string $a_class, string $a_parameter, $a_value): void
239 {
240 $this->structure->setTemporaryParameterByClass($a_class, $a_parameter, $a_value);
241 }
242
246 public function getParameterArray(object $a_gui_obj): array
247 {
248 return $this->getParameterArrayByClass($this->getClassByObject($a_gui_obj));
249 }
250
254 public function getParameterArrayByClass(string $a_class): array
255 {
256 if (null === $this->structure->getClassCidByName($a_class)) {
257 throw new ilCtrlException("Cannot find provided class '$a_class' in the control structure.");
258 }
259
260 $parameters = [];
261 $permanent_parameters = $this->structure->getPermanentParametersByClass($a_class);
262 if (null !== $permanent_parameters) {
263 foreach ($permanent_parameters as $parameter) {
264 $parameters[$parameter] = $this->getQueryParam($parameter);
265 }
266 }
267
268 $temporary_parameters = $this->structure->getTemporaryParametersByClass($a_class);
269 if (null !== $temporary_parameters) {
270 // override existing ones, as temporary parameters
271 // are prioritised over fetched ones.
272 foreach ($temporary_parameters as $key => $value) {
273 $parameters[$key] = $value;
274 }
275 }
276
277 return $parameters;
278 }
279
283 public function clearParameters(object $a_gui_obj): void
284 {
285 $this->clearParametersByClass($this->getClassByObject($a_gui_obj));
286 }
287
291 public function clearParametersByClass(string $a_class): void
292 {
293 // apparently permanent parameters should not be removable,
294 // therefore the line below stays commented:
295 // $this->structure->removePermanentParametersByClass($a_class);
296 $this->structure->removeTemporaryParametersByClass($a_class);
297 }
298
302 public function clearParameterByClass(string $a_class, string $a_parameter): void
303 {
304 $this->structure->removeSingleParameterByClass($a_class, $a_parameter);
305 }
306
310 public function getLinkTarget(
311 object $a_gui_obj,
312 ?string $a_cmd = null,
313 ?string $a_anchor = null,
314 bool $is_async = false,
315 bool $has_xml_style = false
316 ): string {
318 $this->getClassByObject($a_gui_obj),
319 $a_cmd,
320 $a_anchor,
321 $is_async,
322 $has_xml_style
323 );
324 }
325
329 public function getLinkTargetByClass(
330 $a_class,
331 ?string $a_cmd = null,
332 ?string $a_anchor = null,
333 bool $is_async = false,
334 bool $has_xml_style = false
335 ): string {
336 return $this->getTargetUrl(
337 $a_class,
338 $a_cmd,
339 $a_anchor,
340 $is_async,
341 $has_xml_style
342 ) ?? '';
343 }
344
348 public function getFormAction(
349 object $a_gui_obj,
350 ?string $a_fallback_cmd = null,
351 ?string $a_anchor = null,
352 bool $is_async = false,
353 bool $has_xml_style = false
354 ): string {
355 return $this->getFormActionByClass(
356 $this->getClassByObject($a_gui_obj),
357 $a_fallback_cmd,
358 $a_anchor,
359 $is_async,
360 $has_xml_style
361 );
362 }
363
367 public function getFormActionByClass(
368 $a_class,
369 ?string $a_fallback_cmd = null,
370 ?string $a_anchor = null,
371 bool $is_async = false,
372 bool $has_xml_style = false
373 ): string {
374 return $this->getTargetUrl(
375 $a_class,
376 $a_fallback_cmd,
377 $a_anchor,
378 $is_async,
379 $has_xml_style,
380 true
381 ) ?? '';
382 }
383
387 public function redirect(
388 object $a_gui_obj,
389 ?string $a_cmd = null,
390 ?string $a_anchor = null,
391 bool $is_async = false
392 ): void {
393 $this->redirectByClass(
394 $this->getClassByObject($a_gui_obj),
395 $a_cmd,
396 $a_anchor,
397 $is_async
398 );
399 }
400
404 public function redirectByClass(
405 $a_class,
406 ?string $a_cmd = null,
407 ?string $a_anchor = null,
408 bool $is_async = false
409 ): void {
410 $this->redirectToURL(
411 $this->getLinkTargetByClass(
412 $a_class,
413 $a_cmd,
414 $a_anchor,
415 $is_async
416 )
417 );
418 }
419
423 public function redirectToURL(string $target_url): void
424 {
425 // prepend the ILIAS HTTP path if it wasn't already.
426 if (defined("ILIAS_HTTP_PATH") &&
427 strpos($target_url, "://") === false &&
428 strpos($target_url, "/") !== 0
429 ) {
430 $target_url = ILIAS_HTTP_PATH . "/" . $target_url;
431 }
432
433 // this line can be dropped after discussion with TB or JF,
434 // it keeps the functionality of UI plugin hooks alive.
435 $target_url = $this->modifyUrlWithPluginHooks($target_url);
436
437 // initialize http response object
438 $response = new Response();
439
440 // there's an exceptional case for asynchronous file uploads
441 // where a json response is delivered.
442 if ('application/json' === $this->server_request->getHeaderLine('Accept')) {
443 try {
444 $body = Streams::ofString(
445 json_encode(
446 [
447 'redirect_url' => $target_url,
448 'success' => true,
449 'message' => 'called redirect after asynchronous file-upload request.',
450 ],
451 JSON_THROW_ON_ERROR
452 )
453 );
454 } catch (Throwable $exception) {
455 $body = Streams::ofString($exception->getMessage());
456 }
457
458 $response = $response->withBody($body);
459 } else {
460 $response = $response->withAddedHeader('Location', $target_url);
461 }
462
463 // manually trigger session_write_close() due to exceptions stored
464 // in the ILIAS database, otherwise this method is called by exit()
465 // which leads to the exceptions not being written to the database.
466 session_write_close();
467
468 try {
469 $this->response_sender->sendResponse($response);
470 } catch (ResponseSendingException $e) {
471 header("Location: $target_url");
472 if ('application/json' === $this->server_request->getHeaderLine('Accept')) {
473 $content = (null !== $response->getBody()) ?
474 $response->getBody()->getContents() :
475 [];
476
477 echo json_encode($content, JSON_THROW_ON_ERROR);
478 }
479 } catch (Throwable $t) {
480 header("Location: $target_url");
481 echo $t->getMessage();
482 }
483
484 exit;
485 }
486
490 public function setContextObject(int $obj_id, string $obj_type): void
491 {
492 // cannot process object without object type.
493 if (!empty($obj_type)) {
494 $this->context->setObjId($obj_id);
495 $this->context->setObjType($obj_type);
496 }
497 }
498
502 public function getContextObjId(): ?int
503 {
504 return $this->context->getObjId();
505 }
506
510 public function getContextObjType(): ?string
511 {
512 return $this->context->getObjType();
513 }
514
518 public function getCallHistory(): array
519 {
520 return $this->stacktrace;
521 }
522
526 public function lookupClassPath(string $a_class): string
527 {
528 $path = $this->structure->getRelativePathByName($a_class);
529 if (null === $path) {
530 throw new ilCtrlException("Class '$a_class' cannot be found in the control structure.");
531 }
532
533 return $path;
534 }
535
539 public function lookupOriginalClassName(string $a_class): ?string
540 {
541 return $this->structure->getObjNameByName($a_class);
542 }
543
547 public function getClassForClasspath(string $a_class_path): string
548 {
549 $path_info = pathinfo($a_class_path);
550
551 return substr($path_info['basename'], 6, -4);
552 }
553
557 public function setTargetScript(string $a_target_script): void
558 {
559 $this->context->setTargetScript($a_target_script);
560 }
561
565 public function isAsynch(): bool
566 {
567 return $this->context->isAsync();
568 }
569
573 public function setReturn(object $a_gui_obj, ?string $a_cmd = null): void
574 {
575 $this->setReturnByClass($this->getClassByObject($a_gui_obj), $a_cmd);
576 }
577
581 public function setReturnByClass(string $a_class, ?string $a_cmd = null): void
582 {
583 $this->structure->setReturnTargetByClass(
584 $a_class,
585 $this->getLinkTargetByClass(
586 $a_class,
587 $a_cmd
588 )
589 );
590 }
591
595 public function returnToParent(object $a_gui_obj, ?string $a_anchor = null): void
596 {
597 $class_name = $this->getClassByObject($a_gui_obj);
598 $target_url = $this->getParentReturnByClass($class_name);
599
600 // append redirect source to target url.
601 $target_url = $this->appendParameterString(
602 $target_url,
603 self::PARAM_REDIRECT,
604 $class_name
605 );
606
607 // append the provided anchor if necessary.
608 if (null !== $a_anchor) {
609 $target_url .= "#$a_anchor";
610 }
611
612 $this->redirectToURL($target_url);
613 }
614
618 public function getParentReturn(object $a_gui_obj): ?string
619 {
620 return $this->getParentReturnByClass($this->getClassByObject($a_gui_obj));
621 }
622
626 public function getParentReturnByClass(string $a_class): ?string
627 {
628 $path = $this->path_factory->find($this->context, $a_class);
629 if (null !== $path->getCidPath()) {
630 foreach ($path->getCidArray() as $cid) {
631 $current_class = $this->structure->getClassNameByCid($cid);
632 $return_target = $this->structure->getReturnTargetByClass($current_class);
633 if (null !== $return_target) {
634 return $return_target;
635 }
636 }
637 }
638
639 return null;
640 }
641
645 public function getRedirectSource(): ?string
646 {
647 return $this->context->getRedirectSource();
648 }
649
653 public function insertCtrlCalls($a_parent, $a_child, string $a_comp_prefix): void
654 {
655 throw new ilCtrlException(__METHOD__ . " is deprecated and must not be used.");
656 }
657
661 public function checkCurrentPathForClass(string $gui_class): bool
662 {
663 $class_cid = $this->structure->getClassCidByName($gui_class);
664 if (null === $class_cid) {
665 return false;
666 }
667
668 return strpos(
669 $this->context->getPath()->getCidPath() ?? '',
670 $class_cid
671 ) !== false;
672 }
673
677 public function getCurrentClassPath(): array
678 {
679 if (null === $this->context->getPath()->getCidPath()) {
680 return [];
681 }
682
683 $class_paths = [];
684 foreach ($this->context->getPath()->getCidArray(SORT_ASC) as $cid) {
685 $class_paths[] = $this->structure->getObjNameByCid($cid);
686 }
687
688 return $class_paths;
689 }
690
694 public function attachObserver(ilCtrlObserver $observer, ilCtrlEvent $event = ilCtrlEvent::ALL): void
695 {
696 $this->subject->attach($observer, $event);
697 }
698
702 public function detachObserver(ilCtrlObserver $observer, ilCtrlEvent $event = ilCtrlEvent::ALL): void
703 {
704 $this->subject->detach($observer, $event);
705 }
706
707 protected function getDeterminedCommand(): ?string
708 {
709 // retrieve $_GET and $_POST parameters.
710 $post_command = $this->getPostCommand();
711 $get_command = $this->getQueryParam(self::PARAM_CMD);
712 $table_command = $this->getTableCommand();
713
714 $is_post = (self::CMD_POST === $get_command);
715
716 // if the $_GET command is 'post', either the $_POST
717 // command or $_GETs fallback command is used.
718 // for now, the table command is used as fallback as well,
719 // but this will be removed once the implementation of
720 // table actions change.
721 $command = ($is_post) ?
722 $post_command ?? $table_command ?? $this->getQueryParam(self::PARAM_CMD_FALLBACK) :
723 $get_command;
724
725 // override the command that has been set during a
726 // request via ilCtrl::setCmd().
727 $context_command = $this->context->getCmd();
728 if (null !== $context_command && self::CMD_POST !== $context_command) {
729 $command = $context_command;
730 }
731
732 if (null === $command) {
733 return $command;
734 }
735
736 // if the command is for post requests, or the command
737 // is not considered safe, the csrf-validation must pass.
738 $cmd_class = $this->context->getCmdClass();
739 if (null !== $cmd_class && !$this->isCmdSecure($is_post, $cmd_class, $command)) {
740 $stored_token = $this->token_repository->getToken();
741 $sent_token = $this->getQueryParam(self::PARAM_CSRF_TOKEN);
742
743 if (null !== $sent_token && $stored_token->verifyWith($sent_token)) {
744 return $command;
745 }
746 return null;
747 }
748
749 return $command;
750 }
751
758 private function getQueryParam(string $parameter_name): ?string
759 {
760 if ($this->get_parameters->has($parameter_name)) {
761 return $this->get_parameters->retrieve(
762 $parameter_name,
763 $this->refinery->to()->string()
764 );
765 }
766
767 return null;
768 }
769
773 private function getTableCommand(): ?string
774 {
775 if ($this->post_parameters->has('table_top_cmd')) {
776 return $this->post_parameters->retrieve(
777 'table_top_cmd',
778 $this->refinery->custom()->transformation(function ($item): ?string {
779 return is_array($item) ? key($item) : null;
780 })
781 );
782 }
783 // Button on top of the table
784 if ($this->post_parameters->has('select_cmd2')) {
785 return $this->post_parameters->has('selected_cmd2')
786 ? $this->post_parameters->retrieve('selected_cmd2', $this->refinery->to()->string())
787 : null;
788 }
789 // Button at bottom of the table
790 if ($this->post_parameters->has('select_cmd')) {
791 return $this->post_parameters->has('selected_cmd')
792 ? $this->post_parameters->retrieve('selected_cmd', $this->refinery->to()->string())
793 : null;
794 }
795
796 return null;
797 }
798
803 private function getPostCommand(): ?string
804 {
805 if ($this->post_parameters->has(self::PARAM_CMD)) {
806 return $this->post_parameters->retrieve(
807 self::PARAM_CMD,
808 $this->refinery->custom()->transformation(
809 static function ($value): ?string {
810 if (!empty($value)) {
811 if (is_array($value)) {
812 // this most likely only works by accident, but
813 // the selected or clicked command button will
814 // always be sent as first array entry. This
815 // should definitely be done differently.
816 return (string) array_key_first($value);
817 }
818
819 return (string) $value;
820 }
821
822 return null;
823 }
824 )
825 );
826 }
827
828 return null;
829 }
830
843 private function getTargetUrl(
844 $a_class,
845 ?string $a_cmd = null,
846 ?string $a_anchor = null,
847 bool $is_async = false,
848 bool $is_escaped = false,
849 bool $is_post = false
850 ): ?string {
851 if (empty($a_class)) {
852 throw new ilCtrlException(__METHOD__ . " was provided with an empty class or class-array.");
853 }
854
855 $is_array = is_array($a_class);
856
857 $path = $this->path_factory->find($this->context, $a_class);
858 if (null !== ($exception = $path->getException())) {
859 throw $exception;
860 }
861
862 $base_class = $path->getBaseClass();
863 if (null === $base_class) {
864 throw new ilCtrlException("Cannot find a valid baseclass in the cid path '{$path->getCidPath()}'");
865 }
866
867 $target_url = $this->context->getTargetScript();
868 $target_url = $this->appendParameterString(
869 $target_url,
870 self::PARAM_BASE_CLASS,
871 urlencode($base_class), // encode in case of namespaced classes
872 $is_escaped
873 );
874
875 $cmd_class = ($is_array) ?
876 $a_class[array_key_last($a_class)] :
877 $a_class;
878
879 // only append the cid path and command class params
880 // if they exist.
881 if (null !== $path->getNextCid($base_class)) {
882 $target_url = $this->appendParameterString(
883 $target_url,
884 self::PARAM_CID_PATH,
885 $path->getCidPath(),
886 $is_escaped
887 );
888
889 $target_url = $this->appendParameterString(
890 $target_url,
891 self::PARAM_CMD_CLASS,
892 urlencode($cmd_class), // encode in case of namespaced classes
893 $is_escaped
894 );
895 }
896
897 // if the target url is generated for form actions,
898 // the command must be set to 'post'.
899 if ($is_post) {
900 $target_url = $this->appendParameterString(
901 $target_url,
902 self::PARAM_CMD,
903 self::CMD_POST,
904 $is_escaped
905 );
906 }
907
908 // the actual command is appended as fallback command
909 // for form actions and 'normal' get requests.
910 if (!empty($a_cmd)) {
911 $target_url = $this->appendParameterString(
912 $target_url,
913 ($is_post) ? self::PARAM_CMD_FALLBACK : self::PARAM_CMD,
914 $a_cmd,
915 $is_escaped
916 );
917 }
918
919 // collect all parameters of classes within the current
920 // targets path and append them to the target url.
921 foreach ($path->getCidArray(SORT_ASC) as $cid) {
922 $class_name = $this->structure->getClassNameByCid($cid);
923 if (null === $class_name) {
924 throw new ilCtrlException("Classname for cid '$cid' in current path cannot be found.");
925 }
926
927 $target_url = $this->appendParameterStringsByClass(
928 $class_name,
929 $target_url,
930 $is_escaped
931 );
932 }
933
934 // append a csrf token if the command is considered
935 // unsafe or the link is for form actions.
936 if (!$this->isCmdSecure($is_post, $cmd_class, $a_cmd)) {
937 $token = $this->token_repository->getToken();
938 $target_url = $this->appendParameterString(
939 $target_url,
940 self::PARAM_CSRF_TOKEN,
941 $token->getToken(),
942 $is_escaped
943 );
944 }
945
946 if ($is_async) {
947 $target_url = $this->appendParameterString(
948 $target_url,
949 self::PARAM_CMD_MODE,
950 self::CMD_MODE_ASYNC,
951 $is_escaped
952 );
953 }
954
955 if (!empty($a_anchor)) {
956 $target_url .= "#$a_anchor";
957 }
958
959 return $target_url;
960 }
961
968 private function modifyUrlWithPluginHooks(string $target_url): string
969 {
970 $ui_plugins = $this->component_factory->getActivePluginsInSlot("uihk");
971 foreach ($ui_plugins as $plugin_instance) {
974 $html = $plugin_instance
975 ->getUIClassInstance()
976 ->getHTML(
977 'components/ILIAS/Utilities',
978 'redirect',
979 ["html" => $target_url]
980 );
981
982 if (ilUIHookPluginGUI::KEEP !== $html['mode']) {
983 $target_url = $plugin_instance
984 ->getUIClassInstance()
985 ->modifyHTML(
986 $target_url,
987 $html
988 );
989 }
990 }
991
992 return $target_url;
993 }
994
1002 private function isCmdSecure(bool $is_post, string $cmd_class, ?string $cmd = null): bool
1003 {
1004 // if no command is specified, the command is
1005 // considered safe if it's not a POST command.
1006 if (null === $cmd) {
1007 return !$is_post;
1008 }
1009
1010 // if the given command class doesn't exist, the
1011 // command is not considered safe as it might've been
1012 // tampered with.
1013 $obj_name = $this->structure->getObjNameByName($cmd_class);
1014 if (null === $obj_name) {
1015 return false;
1016 }
1017
1018 // if the command class does not yet implement the
1019 // ilCtrlSecurityInterface, the command is considered
1020 // safe if it's not a POST command.
1021 if (!is_a($obj_name, ilCtrlSecurityInterface::class, true)) {
1022 return !$is_post;
1023 }
1024
1025 // the post command is considered safe if it's contained
1026 // in the list of safe post commands.
1027 if ($is_post) {
1028 return in_array($cmd, $this->structure->getSafeCommandsByName($cmd_class), true);
1029 }
1030
1031 // the get command is considered safe if it's not
1032 // contained in the list of unsafe get commands.
1033 return !in_array($cmd, $this->structure->getUnsafeCommandsByName($cmd_class), true);
1034 }
1035
1045 string $class_name,
1046 string $target_url,
1047 bool $is_escaped = false
1048 ): string {
1049 $class_parameters = $this->getParameterArrayByClass($class_name);
1050 if (!empty($class_parameters)) {
1051 foreach ($class_parameters as $key => $value) {
1052 $target_url = $this->appendParameterString(
1053 $target_url,
1054 $key,
1055 $value,
1056 $is_escaped
1057 );
1058 }
1059 }
1060
1061 return $target_url;
1062 }
1063
1072 private function appendParameterString(
1073 string $url,
1074 string $parameter_name,
1075 $value,
1076 bool $is_escaped = false
1077 ): string {
1078 // transform value into a string, since null will fail we can
1079 // (temporarily) use the null coalescing operator.
1080 $value = $this->refinery->kindlyTo()->string()->transform($value ?? '');
1081
1082 if ('' === $value) {
1083 return $url;
1084 }
1085
1087 $parsed_url = parse_url(str_replace('&amp;', '&', $url));
1088
1089 $query_parameters = $this->query_parser->parseQueriesOfURL($parsed_url['query'] ?? '');
1090
1091 // update the given parameter or add it to the list.
1092 $query_parameters[$parameter_name] = $value;
1093
1094 $new_url = $parsed_url['path'] ?? $this->context->getTargetScript();
1095 // we currently only escape ampersands (don't blame me).
1096 $ampersand = ($is_escaped) ? '&amp;' : '&';
1097
1098 foreach ($query_parameters as $parameter => $parameter_value) {
1099 $new_url .= (strpos($new_url, '?') !== false) ?
1100 $ampersand . "$parameter=$parameter_value" :
1101 "?$parameter=$parameter_value";
1102 }
1103
1104 return $new_url;
1105 }
1106
1112 private function populateCall(string $class_name, string $cmd_mode): void
1113 {
1114 $obj_name = $this->structure->getObjNameByName($class_name);
1115
1116 $this->stacktrace[] = [
1117 self::PARAM_CMD_CLASS => $obj_name,
1118 self::PARAM_CMD_MODE => $cmd_mode,
1119 self::PARAM_CMD => $this->getDeterminedCommand(),
1120 ];
1121 }
1122
1129 private function getClassByObject($object): string
1130 {
1131 return (is_object($object)) ? get_class($object) : $object;
1132 }
1133}
$structure
TOTAL STRUCTURE.
Builds data types.
Definition: Factory.php:36
Stream factory which enables the user to create streams without the knowledge of the concrete class.
Definition: Streams.php:32
return true
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Class ilCtrl provides processing control methods.
getCurrentClassPath()
@inheritDoc
getParentReturn(object $a_gui_obj)
@inheritDoc
populateCall(string $class_name, string $cmd_mode)
Helper function that populates a call in the current stacktrace.
__construct(protected ilCtrlStructureInterface $structure, protected ilCtrlTokenRepositoryInterface $token_repository, protected ilCtrlPathFactoryInterface $path_factory, protected ilCtrlContextInterface $context, protected ResponseSenderStrategy $response_sender, protected ServerRequestInterface $server_request, protected RequestWrapper $post_parameters, protected RequestWrapper $get_parameters, protected Refinery $refinery, protected ilComponentFactory $component_factory, protected ilCtrlSubject $subject, protected ilCtrlQueryParserInterface $query_parser,)
getCallHistory()
@inheritDoc
getFormActionByClass( $a_class, ?string $a_fallback_cmd=null, ?string $a_anchor=null, bool $is_async=false, bool $has_xml_style=false)
@inheritDoc
redirectByClass( $a_class, ?string $a_cmd=null, ?string $a_anchor=null, bool $is_async=false)
@inheritDoc
setReturnByClass(string $a_class, ?string $a_cmd=null)
@inheritDoc
getClassForClasspath(string $a_class_path)
@inheritDoc
insertCtrlCalls($a_parent, $a_child, string $a_comp_prefix)
@inheritDoc
getPostCommand()
Returns the current $_POST command.
getContextObjId()
@inheritDoc
getFormAction(object $a_gui_obj, ?string $a_fallback_cmd=null, ?string $a_anchor=null, bool $is_async=false, bool $has_xml_style=false)
@inheritDoc
setContextObject(int $obj_id, string $obj_type)
@inheritDoc
saveParameterByClass(string $a_class, $a_parameter)
@inheritDoc
getLinkTarget(object $a_gui_obj, ?string $a_cmd=null, ?string $a_anchor=null, bool $is_async=false, bool $has_xml_style=false)
@inheritDoc
lookupClassPath(string $a_class)
@inheritDoc
getTargetUrl( $a_class, ?string $a_cmd=null, ?string $a_anchor=null, bool $is_async=false, bool $is_escaped=false, bool $is_post=false)
Helper function that returns a target URL string.
getParameterArrayByClass(string $a_class)
@inheritDoc
redirectToURL(string $target_url)
@inheritDoc
getHTML(object $a_gui_object, ?array $a_parameters=null)
@inheritDoc
attachObserver(ilCtrlObserver $observer, ilCtrlEvent $event=ilCtrlEvent::ALL)
@inheritDoc
getTableCommand()
getQueryParam(string $parameter_name)
Returns a parameter with the given name from the current GET request.
callBaseClass(?string $a_base_class=null)
@inheritDoc
getCmdClass()
@inheritDoc
getNextClass($a_gui_class=null)
@inheritDoc
getRedirectSource()
@inheritDoc
setParameter(object $a_gui_obj, string $a_parameter, $a_value)
@inheritDoc
detachObserver(ilCtrlObserver $observer, ilCtrlEvent $event=ilCtrlEvent::ALL)
@inheritDoc
clearParameters(object $a_gui_obj)
@inheritDoc
object $exec_object
saveParameter(object $a_gui_obj, $a_parameter)
@inheritDoc
setTargetScript(string $a_target_script)
@inheritDoc
string $command
getContextObjType()
@inheritDoc
checkCurrentPathForClass(string $gui_class)
@inheritDoc
getClassByObject($object)
Helper function that returns the class name of a mixed (object or string) parameter.
clearParametersByClass(string $a_class)
@inheritDoc
getParentReturnByClass(string $a_class)
@inheritDoc
clearParameterByClass(string $a_class, string $a_parameter)
@inheritDoc
isCmdSecure(bool $is_post, string $cmd_class, ?string $cmd=null)
Returns whether a given command is considered safe or not.
appendParameterStringsByClass(string $class_name, string $target_url, bool $is_escaped=false)
Appends all parameters for a given class to the given URL.
getDeterminedCommand()
isAsynch()
@inheritDoc
returnToParent(object $a_gui_obj, ?string $a_anchor=null)
@inheritDoc
redirect(object $a_gui_obj, ?string $a_cmd=null, ?string $a_anchor=null, bool $is_async=false)
@inheritDoc
forwardCommand(object $a_gui_object)
@inheritDoc
getCmd(?string $fallback_command=null)
@inheritDoc
setParameterByClass(string $a_class, string $a_parameter, $a_value)
@inheritDoc
setReturn(object $a_gui_obj, ?string $a_cmd=null)
@inheritDoc
getParameterArray(object $a_gui_obj)
@inheritDoc
getLinkTargetByClass( $a_class, ?string $a_cmd=null, ?string $a_anchor=null, bool $is_async=false, bool $has_xml_style=false)
@inheritDoc
array $stacktrace
lookupOriginalClassName(string $a_class)
@inheritDoc
return['delivery_method'=> 'php',]
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
exit
ilCtrlEvent
@ ALL
event string being used if
Interface RequestWrapper.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$path
Definition: ltiservices.php:30
if(!file_exists('../ilias.ini.php'))
$url
Definition: shib_logout.php:70
$token
Definition: xapitoken.php:67
$response
Definition: xapitoken.php:90