ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
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,
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 if (null !== $this->command && $command === $this->command) {
162 // don't broadcast command determination event on consecutive
163 // method calls.
164 return $command;
165 }
166
167 $this->subject->notify(ilCtrlEvent::COMMAND_DETERMINATION, $command);
168 $this->command = $command;
169
170 return $command ?? ''; // remove null-coalesce
171 }
172
176 public function getCmdClass(): ?string
177 {
178 if (null !== ($cmd_class = $this->context->getCmdClass())) {
179 return strtolower($this->structure->getObjNameByName($cmd_class));
180 }
181
182 return '';
183 }
184
188 public function getNextClass($a_gui_class = null): ?string
189 {
190 if (null === $a_gui_class && null === $this->exec_object) {
191 return '';
192 }
193
194 if (null === $this->context->getPath()) {
195 return '';
196 }
197
198 $next_cid = $this->context->getPath()->getNextCid(
199 $this->getClassByObject($a_gui_class ?? $this->exec_object)
200 );
201
202 if (null !== $next_cid) {
203 return strtolower($this->structure->getObjNameByCid($next_cid) ?? '');
204 }
205
206 return '';
207 }
208
212 public function saveParameter(object $a_gui_obj, $a_parameter): void
213 {
214 $this->saveParameterByClass($this->getClassByObject($a_gui_obj), $a_parameter);
215 }
216
220 public function saveParameterByClass(string $a_class, $a_parameter): void
221 {
222 if (!empty($a_parameter)) {
223 if (is_array($a_parameter)) {
224 foreach ($a_parameter as $parameter) {
225 $this->structure->setPermanentParameterByClass($a_class, $parameter);
226 }
227 } else {
228 $this->structure->setPermanentParameterByClass($a_class, $a_parameter);
229 }
230 }
231 }
232
236 public function setParameter(object $a_gui_obj, string $a_parameter, $a_value): void
237 {
238 $this->setParameterByClass($this->getClassByObject($a_gui_obj), $a_parameter, $a_value);
239 }
240
244 public function setParameterByClass(string $a_class, string $a_parameter, $a_value): void
245 {
246 $this->structure->setTemporaryParameterByClass($a_class, $a_parameter, $a_value);
247 }
248
252 public function getParameterArray(object $a_gui_obj): array
253 {
254 return $this->getParameterArrayByClass($this->getClassByObject($a_gui_obj));
255 }
256
260 public function getParameterArrayByClass(string $a_class): array
261 {
262 if (null === $this->structure->getClassCidByName($a_class)) {
263 throw new ilCtrlException("Cannot find provided class '$a_class' in the control structure.");
264 }
265
266 $parameters = [];
267 $permanent_parameters = $this->structure->getPermanentParametersByClass($a_class);
268 if (null !== $permanent_parameters) {
269 foreach ($permanent_parameters as $parameter) {
270 $parameters[$parameter] = $this->getQueryParam($parameter);
271 }
272 }
273
274 $temporary_parameters = $this->structure->getTemporaryParametersByClass($a_class);
275 if (null !== $temporary_parameters) {
276 // override existing ones, as temporary parameters
277 // are prioritised over fetched ones.
278 foreach ($temporary_parameters as $key => $value) {
279 $parameters[$key] = $value;
280 }
281 }
282
283 return $parameters;
284 }
285
289 public function clearParameters(object $a_gui_obj): void
290 {
291 $this->clearParametersByClass($this->getClassByObject($a_gui_obj));
292 }
293
297 public function clearParametersByClass(string $a_class): void
298 {
299 // apparently permanent parameters should not be removable,
300 // therefore the line below stays commented:
301 // $this->structure->removePermanentParametersByClass($a_class);
302 $this->structure->removeTemporaryParametersByClass($a_class);
303 }
304
308 public function clearParameterByClass(string $a_class, string $a_parameter): void
309 {
310 $this->structure->removeSingleParameterByClass($a_class, $a_parameter);
311 }
312
316 public function getLinkTarget(
317 object $a_gui_obj,
318 ?string $a_cmd = null,
319 ?string $a_anchor = null,
320 bool $is_async = false,
321 bool $has_xml_style = false
322 ): string {
324 $this->getClassByObject($a_gui_obj),
325 $a_cmd,
326 $a_anchor,
327 $is_async,
328 $has_xml_style
329 );
330 }
331
335 public function getLinkTargetByClass(
336 $a_class,
337 ?string $a_cmd = null,
338 ?string $a_anchor = null,
339 bool $is_async = false,
340 bool $has_xml_style = false
341 ): string {
342 return $this->getTargetUrl(
343 $a_class,
344 $a_cmd,
345 $a_anchor,
346 $is_async,
347 $has_xml_style
348 ) ?? '';
349 }
350
354 public function getFormAction(
355 object $a_gui_obj,
356 ?string $a_fallback_cmd = null,
357 ?string $a_anchor = null,
358 bool $is_async = false,
359 bool $has_xml_style = false
360 ): string {
361 return $this->getFormActionByClass(
362 $this->getClassByObject($a_gui_obj),
363 $a_fallback_cmd,
364 $a_anchor,
365 $is_async,
366 $has_xml_style
367 );
368 }
369
373 public function getFormActionByClass(
374 $a_class,
375 ?string $a_fallback_cmd = null,
376 ?string $a_anchor = null,
377 bool $is_async = false,
378 bool $has_xml_style = false
379 ): string {
380 return $this->getTargetUrl(
381 $a_class,
382 $a_fallback_cmd,
383 $a_anchor,
384 $is_async,
385 $has_xml_style,
386 true
387 ) ?? '';
388 }
389
393 public function redirect(
394 object $a_gui_obj,
395 ?string $a_cmd = null,
396 ?string $a_anchor = null,
397 bool $is_async = false
398 ): void {
399 $this->redirectByClass(
400 $this->getClassByObject($a_gui_obj),
401 $a_cmd,
402 $a_anchor,
403 $is_async
404 );
405 }
406
410 public function redirectByClass(
411 $a_class,
412 ?string $a_cmd = null,
413 ?string $a_anchor = null,
414 bool $is_async = false
415 ): void {
416 $this->redirectToURL(
417 $this->getLinkTargetByClass(
418 $a_class,
419 $a_cmd,
420 $a_anchor,
421 $is_async
422 )
423 );
424 }
425
429 public function redirectToURL(string $target_url): void
430 {
431 // prepend the ILIAS HTTP path if it wasn't already.
432 if (defined("ILIAS_HTTP_PATH") &&
433 strpos($target_url, "://") === false &&
434 strpos($target_url, "/") !== 0
435 ) {
436 $target_url = ILIAS_HTTP_PATH . "/" . $target_url;
437 }
438
439 // this line can be dropped after discussion with TB or JF,
440 // it keeps the functionality of UI plugin hooks alive.
441 $target_url = $this->modifyUrlWithPluginHooks($target_url);
442
443 // initialize http response object
444 $response = new Response();
445
446 // there's an exceptional case for asynchronous file uploads
447 // where a json response is delivered.
448 if ('application/json' === $this->server_request->getHeaderLine('Accept')) {
449 try {
450 $body = Streams::ofString(
451 json_encode(
452 [
453 'redirect_url' => $target_url,
454 'success' => true,
455 'message' => 'called redirect after asynchronous file-upload request.',
456 ],
457 JSON_THROW_ON_ERROR
458 )
459 );
460 } catch (Throwable $exception) {
461 $body = Streams::ofString($exception->getMessage());
462 }
463
464 $response = $response->withBody($body);
465 } else {
466 $response = $response->withAddedHeader('Location', $target_url);
467 }
468
469 // manually trigger session_write_close() due to exceptions stored
470 // in the ILIAS database, otherwise this method is called by exit()
471 // which leads to the exceptions not being written to the database.
472 session_write_close();
473
474 try {
475 $this->response_sender->sendResponse($response);
476 } catch (ResponseSendingException $e) {
477 header("Location: $target_url");
478 if ('application/json' === $this->server_request->getHeaderLine('Accept')) {
479 $content = (null !== $response->getBody()) ?
480 $response->getBody()->getContents() :
481 [];
482
483 echo json_encode($content, JSON_THROW_ON_ERROR);
484 }
485 } catch (Throwable $t) {
486 header("Location: $target_url");
487 echo $t->getMessage();
488 }
489
490 exit;
491 }
492
496 public function setContextObject(int $obj_id, string $obj_type): void
497 {
498 // cannot process object without object type.
499 if (!empty($obj_type)) {
500 $this->context->setObjId($obj_id);
501 $this->context->setObjType($obj_type);
502 }
503 }
504
508 public function getContextObjId(): ?int
509 {
510 return $this->context->getObjId();
511 }
512
516 public function getContextObjType(): ?string
517 {
518 return $this->context->getObjType();
519 }
520
524 public function getCallHistory(): array
525 {
526 return $this->stacktrace;
527 }
528
532 public function lookupClassPath(string $a_class): string
533 {
534 $path = $this->structure->getRelativePathByName($a_class);
535 if (null === $path) {
536 throw new ilCtrlException("Class '$a_class' cannot be found in the control structure.");
537 }
538
539 return $path;
540 }
541
545 public function lookupOriginalClassName(string $a_class): ?string
546 {
547 return $this->structure->getObjNameByName($a_class);
548 }
549
553 public function getClassForClasspath(string $a_class_path): string
554 {
555 $path_info = pathinfo($a_class_path);
556
557 return substr($path_info['basename'], 6, -4);
558 }
559
563 public function setTargetScript(string $a_target_script): void
564 {
565 $this->context->setTargetScript($a_target_script);
566 }
567
571 public function isAsynch(): bool
572 {
573 return $this->context->isAsync();
574 }
575
579 public function setReturn(object $a_gui_obj, ?string $a_cmd = null): void
580 {
581 $this->setReturnByClass($this->getClassByObject($a_gui_obj), $a_cmd);
582 }
583
587 public function setReturnByClass(string $a_class, ?string $a_cmd = null): void
588 {
589 $this->structure->setReturnTargetByClass(
590 $a_class,
591 $this->getLinkTargetByClass(
592 $a_class,
593 $a_cmd
594 )
595 );
596 }
597
601 public function returnToParent(object $a_gui_obj, ?string $a_anchor = null): void
602 {
603 $class_name = $this->getClassByObject($a_gui_obj);
604 $target_url = $this->getParentReturnByClass($class_name);
605
606 // append redirect source to target url.
607 $target_url = $this->appendParameterString(
608 $target_url,
609 self::PARAM_REDIRECT,
610 $class_name
611 );
612
613 // append the provided anchor if necessary.
614 if (null !== $a_anchor) {
615 $target_url .= "#$a_anchor";
616 }
617
618 $this->redirectToURL($target_url);
619 }
620
624 public function getParentReturn(object $a_gui_obj): ?string
625 {
626 return $this->getParentReturnByClass($this->getClassByObject($a_gui_obj));
627 }
628
632 public function getParentReturnByClass(string $a_class): ?string
633 {
634 $path = $this->path_factory->find($this->context, $a_class);
635 if (null !== $path->getCidPath()) {
636 foreach ($path->getCidArray() as $cid) {
637 $current_class = $this->structure->getClassNameByCid($cid);
638 $return_target = $this->structure->getReturnTargetByClass($current_class);
639 if (null !== $return_target) {
640 return $return_target;
641 }
642 }
643 }
644
645 return null;
646 }
647
651 public function getRedirectSource(): ?string
652 {
653 return $this->context->getRedirectSource();
654 }
655
659 public function insertCtrlCalls($a_parent, $a_child, string $a_comp_prefix): void
660 {
661 throw new ilCtrlException(__METHOD__ . " is deprecated and must not be used.");
662 }
663
667 public function checkCurrentPathForClass(string $gui_class): bool
668 {
669 $class_cid = $this->structure->getClassCidByName($gui_class);
670 if (null === $class_cid) {
671 return false;
672 }
673
674 return strpos(
675 $this->context->getPath()->getCidPath() ?? '',
676 $class_cid
677 ) !== false;
678 }
679
683 public function getCurrentClassPath(): array
684 {
685 if (null === $this->context->getPath()->getCidPath()) {
686 return [];
687 }
688
689 $class_paths = [];
690 foreach ($this->context->getPath()->getCidArray(SORT_ASC) as $cid) {
691 $class_paths[] = $this->structure->getObjNameByCid($cid);
692 }
693
694 return $class_paths;
695 }
696
700 public function attachObserver(ilCtrlObserver $observer, ilCtrlEvent $event = ilCtrlEvent::ALL): void
701 {
702 $this->subject->attach($observer, $event);
703 }
704
708 public function detachObserver(ilCtrlObserver $observer, ilCtrlEvent $event = ilCtrlEvent::ALL): void
709 {
710 $this->subject->detach($observer, $event);
711 }
712
713 protected function getDeterminedCommand(): ?string
714 {
715 // retrieve $_GET and $_POST parameters.
716 $post_command = $this->getPostCommand();
717 $get_command = $this->getQueryParam(self::PARAM_CMD);
718 $table_command = $this->getTableCommand();
719
720 $is_post = (self::CMD_POST === $get_command);
721
722 // if the $_GET command is 'post', either the $_POST
723 // command or $_GETs fallback command is used.
724 // for now, the table command is used as fallback as well,
725 // but this will be removed once the implementation of
726 // table actions change.
727 $command = ($is_post) ?
728 $post_command ?? $table_command ?? $this->getQueryParam(self::PARAM_CMD_FALLBACK) :
729 $get_command;
730
731 // override the command that has been set during a
732 // request via ilCtrl::setCmd().
733 $context_command = $this->context->getCmd();
734 if (null !== $context_command && self::CMD_POST !== $context_command) {
735 $command = $context_command;
736 }
737
738 if (null === $command) {
739 return $command;
740 }
741
742 // if the command is for post requests, or the command
743 // is not considered safe, the csrf-validation must pass.
744 $cmd_class = $this->context->getCmdClass();
745 if (null !== $cmd_class && !$this->isCmdSecure($is_post, $cmd_class, $command)) {
746 $stored_token = $this->token_repository->getToken();
747 $sent_token = $this->getQueryParam(self::PARAM_CSRF_TOKEN);
748
749 if (null !== $sent_token && $stored_token->verifyWith($sent_token)) {
750 return $command;
751 }
752 return null;
753 }
754
755 return $command;
756 }
757
764 private function getQueryParam(string $parameter_name): ?string
765 {
766 if ($this->get_parameters->has($parameter_name)) {
767 return $this->get_parameters->retrieve(
768 $parameter_name,
769 $this->refinery->to()->string()
770 );
771 }
772
773 return null;
774 }
775
779 private function getTableCommand(): ?string
780 {
781 if ($this->post_parameters->has('table_top_cmd')) {
782 return $this->post_parameters->retrieve(
783 'table_top_cmd',
784 $this->refinery->custom()->transformation(function ($item): ?string {
785 return is_array($item) ? key($item) : null;
786 })
787 );
788 }
789 // Button on top of the table
790 if ($this->post_parameters->has('select_cmd2')) {
791 return $this->post_parameters->has('selected_cmd2')
792 ? $this->post_parameters->retrieve('selected_cmd2', $this->refinery->to()->string())
793 : null;
794 }
795 // Button at bottom of the table
796 if ($this->post_parameters->has('select_cmd')) {
797 return $this->post_parameters->has('selected_cmd')
798 ? $this->post_parameters->retrieve('selected_cmd', $this->refinery->to()->string())
799 : null;
800 }
801
802 return null;
803 }
804
809 private function getPostCommand(): ?string
810 {
811 if ($this->post_parameters->has(self::PARAM_CMD)) {
812 return $this->post_parameters->retrieve(
813 self::PARAM_CMD,
814 $this->refinery->custom()->transformation(
815 static function ($value): ?string {
816 if (!empty($value)) {
817 if (is_array($value)) {
818 // this most likely only works by accident, but
819 // the selected or clicked command button will
820 // always be sent as first array entry. This
821 // should definitely be done differently.
822 return (string) array_key_first($value);
823 }
824
825 return (string) $value;
826 }
827
828 return null;
829 }
830 )
831 );
832 }
833
834 return null;
835 }
836
849 private function getTargetUrl(
850 $a_class,
851 ?string $a_cmd = null,
852 ?string $a_anchor = null,
853 bool $is_async = false,
854 bool $is_escaped = false,
855 bool $is_post = false
856 ): ?string {
857 if (empty($a_class)) {
858 throw new ilCtrlException(__METHOD__ . " was provided with an empty class or class-array.");
859 }
860
861 $is_array = is_array($a_class);
862
863 $path = $this->path_factory->find($this->context, $a_class);
864 if (null !== ($exception = $path->getException())) {
865 throw $exception;
866 }
867
868 $base_class = $path->getBaseClass();
869 if (null === $base_class) {
870 throw new ilCtrlException("Cannot find a valid baseclass in the cid path '{$path->getCidPath()}'");
871 }
872
873 $target_url = $this->context->getTargetScript();
874 $target_url = $this->appendParameterString(
875 $target_url,
876 self::PARAM_BASE_CLASS,
877 urlencode($base_class), // encode in case of namespaced classes
878 $is_escaped
879 );
880
881 $cmd_class = ($is_array) ?
882 $a_class[array_key_last($a_class)] :
883 $a_class;
884
885 // only append the cid path and command class params
886 // if they exist.
887 if (null !== $path->getNextCid($base_class)) {
888 $target_url = $this->appendParameterString(
889 $target_url,
890 self::PARAM_CID_PATH,
891 $path->getCidPath(),
892 $is_escaped
893 );
894
895 $target_url = $this->appendParameterString(
896 $target_url,
897 self::PARAM_CMD_CLASS,
898 urlencode($cmd_class), // encode in case of namespaced classes
899 $is_escaped
900 );
901 }
902
903 // if the target url is generated for form actions,
904 // the command must be set to 'post'.
905 if ($is_post) {
906 $target_url = $this->appendParameterString(
907 $target_url,
908 self::PARAM_CMD,
909 self::CMD_POST,
910 $is_escaped
911 );
912 }
913
914 // the actual command is appended as fallback command
915 // for form actions and 'normal' get requests.
916 if (!empty($a_cmd)) {
917 $target_url = $this->appendParameterString(
918 $target_url,
919 ($is_post) ? self::PARAM_CMD_FALLBACK : self::PARAM_CMD,
920 $a_cmd,
921 $is_escaped
922 );
923 }
924
925 // collect all parameters of classes within the current
926 // targets path and append them to the target url.
927 foreach ($path->getCidArray(SORT_ASC) as $cid) {
928 $class_name = $this->structure->getClassNameByCid($cid);
929 if (null === $class_name) {
930 throw new ilCtrlException("Classname for cid '$cid' in current path cannot be found.");
931 }
932
933 $target_url = $this->appendParameterStringsByClass(
934 $class_name,
935 $target_url,
936 $is_escaped
937 );
938 }
939
940 // append a csrf token if the command is considered
941 // unsafe or the link is for form actions.
942 if (!$this->isCmdSecure($is_post, $cmd_class, $a_cmd)) {
943 $token = $this->token_repository->getToken();
944 $target_url = $this->appendParameterString(
945 $target_url,
946 self::PARAM_CSRF_TOKEN,
947 $token->getToken(),
948 $is_escaped
949 );
950 }
951
952 if ($is_async) {
953 $target_url = $this->appendParameterString(
954 $target_url,
955 self::PARAM_CMD_MODE,
956 self::CMD_MODE_ASYNC,
957 $is_escaped
958 );
959 }
960
961 if (!empty($a_anchor)) {
962 $target_url .= "#$a_anchor";
963 }
964
965 return $target_url;
966 }
967
974 private function modifyUrlWithPluginHooks(string $target_url): string
975 {
976 $ui_plugins = $this->component_factory->getActivePluginsInSlot("uihk");
977 foreach ($ui_plugins as $plugin_instance) {
980 $html = $plugin_instance
981 ->getUIClassInstance()
982 ->getHTML(
983 'components/ILIAS/Utilities',
984 'redirect',
985 ["html" => $target_url]
986 );
987
988 if (ilUIHookPluginGUI::KEEP !== $html['mode']) {
989 $target_url = $plugin_instance
990 ->getUIClassInstance()
991 ->modifyHTML(
992 $target_url,
993 $html
994 );
995 }
996 }
997
998 return $target_url;
999 }
1000
1008 private function isCmdSecure(bool $is_post, string $cmd_class, ?string $cmd = null): bool
1009 {
1010 // if no command is specified, the command is
1011 // considered safe if it's not a POST command.
1012 if (null === $cmd) {
1013 return !$is_post;
1014 }
1015
1016 // if the given command class doesn't exist, the
1017 // command is not considered safe as it might've been
1018 // tampered with.
1019 $obj_name = $this->structure->getObjNameByName($cmd_class);
1020 if (null === $obj_name) {
1021 return false;
1022 }
1023
1024 // if the command class does not yet implement the
1025 // ilCtrlSecurityInterface, the command is considered
1026 // safe if it's not a POST command.
1027 if (!is_a($obj_name, ilCtrlSecurityInterface::class, true)) {
1028 return !$is_post;
1029 }
1030
1031 // the post command is considered safe if it's contained
1032 // in the list of safe post commands.
1033 if ($is_post) {
1034 return in_array($cmd, $this->structure->getSafeCommandsByName($cmd_class), true);
1035 }
1036
1037 // the get command is considered safe if it's not
1038 // contained in the list of unsafe get commands.
1039 return !in_array($cmd, $this->structure->getUnsafeCommandsByName($cmd_class), true);
1040 }
1041
1051 string $class_name,
1052 string $target_url,
1053 bool $is_escaped = false
1054 ): string {
1055 $class_parameters = $this->getParameterArrayByClass($class_name);
1056 if (!empty($class_parameters)) {
1057 foreach ($class_parameters as $key => $value) {
1058 $target_url = $this->appendParameterString(
1059 $target_url,
1060 $key,
1061 $value,
1062 $is_escaped
1063 );
1064 }
1065 }
1066
1067 return $target_url;
1068 }
1069
1078 private function appendParameterString(
1079 string $url,
1080 string $parameter_name,
1081 $value,
1082 bool $is_escaped = false
1083 ): string {
1084 // transform value into a string, since null will fail we can
1085 // (temporarily) use the null coalescing operator.
1086 $value = $this->refinery->kindlyTo()->string()->transform($value ?? '');
1087
1088 if ('' === $value) {
1089 return $url;
1090 }
1091
1093 $parsed_url = parse_url(str_replace('&amp;', '&', $url));
1094
1095 $query_parameters = $this->query_parser->parseQueriesOfURL($parsed_url['query'] ?? '');
1096
1097 // update the given parameter or add it to the list.
1098 $query_parameters[$parameter_name] = $value;
1099
1100 $new_url = $parsed_url['path'] ?? $this->context->getTargetScript();
1101 // we currently only escape ampersands (don't blame me).
1102 $ampersand = ($is_escaped) ? '&amp;' : '&';
1103
1104 foreach ($query_parameters as $parameter => $parameter_value) {
1105 $new_url .= (strpos($new_url, '?') !== false) ?
1106 $ampersand . "$parameter=$parameter_value" :
1107 "?$parameter=$parameter_value";
1108 }
1109
1110 return $new_url;
1111 }
1112
1118 private function populateCall(string $class_name, string $cmd_mode): void
1119 {
1120 $obj_name = $this->structure->getObjNameByName($class_name);
1121
1122 $this->stacktrace[] = [
1123 self::PARAM_CMD_CLASS => $obj_name,
1124 self::PARAM_CMD_MODE => $cmd_mode,
1125 self::PARAM_CMD => $this->getDeterminedCommand(),
1126 ];
1127 }
1128
1135 private function getClassByObject($object): string
1136 {
1137 return (is_object($object)) ? get_class($object) : $object;
1138 }
1139}
$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:68
$context
Definition: webdav.php:31
$token
Definition: xapitoken.php:70
$response
Definition: xapitoken.php:93