1<?php 2/* Copyright (c) 1998-2012 ILIAS open source, Extended GPL, see docs/LICENSE */ 3require_once('class.ilCachedCtrl.php'); 4 5/** 6 * This class provides processing control methods. 7 * A global instance is available via variable $ilCtrl 8 * 9 * xml_style parameters: This mode was activated per default in the past, is now set to false but still being 10 * used and needed, if link information is passed to the xslt processing e.g. in content pages. 11 * 12 * @author Alex Killing <alex.killing@gmx.de> 13 * @version $Id$ 14 */ 15class ilCtrl 16{ 17 const IL_RTOKEN_NAME = 'rtoken'; 18 19 /** 20 * Maps lowercase class names to lists of parameter names that saved for them. 21 * 22 * See saveParameter/setParameter for difference to save_parameter. 23 * 24 * This is used in: saveParameter, saveParameterByClass, getParameterArrayByClass 25 * 26 * @var array<string, array<string, mixed>> 27 */ 28 protected $save_parameter; 29 30 /** 31 * Maps lowercase class names to lists of parameters set for them. 32 * 33 * See saveParameter/setParameter for difference to save_parameter. 34 * 35 * This is used in: setParameter, setParameterByClass, getParameterArrayByClass 36 * 37 * @var array<string, mixed[]> 38 */ 39 protected $parameter; 40 41 /** 42 * Return commands per class. 43 * 44 * Return command sare defined by an upper context classes. If a subcontext calls 45 * returnToParent() it will redirect to the return command of the next upper context that defined 46 * a return command. 47 * 48 * This is used in: setReturn, setReturnByClass, getParentReturnByClass, searchReturnClass 49 * 50 * @var array<string, string> 51 */ 52 protected $return; 53 54 /** 55 * Stores the order in which different GUI classes were called. 56 * 57 * TODO: Might better be called call_stack. 58 * 59 * This is used in: forwardCommand, getHTML, getCallHistory 60 */ 61 protected $call_hist = array(); // calling history 62 63 /** 64 * Stores which class calls which other class. 65 * 66 * This is used in: getNodeIdForTargetClass, fetchCallsOfClassFromCache, callOfClassNotKnown 67 */ 68 protected $calls = array(); 69 70 /** 71 * Request token, prevents XSS. 72 * 73 * This is used in: getRequestToken 74 * 75 * @var string 76 */ 77 protected $rtoken = false; 78 79 /** 80 * Base script for link targets, reloads and so on 81 * @var string 82 */ 83 protected $target_script = "ilias.php"; 84 85 /** 86 * @var string 87 */ 88 protected $module_dir; 89 90 /** 91 * control class constructor 92 */ 93 public function __construct() 94 { 95 $this->initializeMemberVariables(); 96 97 // this information should go to xml files one day 98 $this->stored_trees = array("ilrepositorygui", "ildashboardgui", 99 "illmpresentationgui", "illmeditorgui", 100 "iladministrationgui"); 101 } 102 103 /** 104 * Initialize member variables. 105 * 106 * This is used in __construct and initBaseClass. 107 */ 108 protected function initializeMemberVariables() 109 { 110 $this->save_parameter = array(); 111 $this->parameter = array(); // save parameter array 112 $this->return = array(); // return commmands 113 $this->tab = array(); 114 $this->current_node = 0; 115 $this->call_node = array(); 116 $this->root_class = ""; 117 } 118 119 /** 120 * Calls base class of current request. The base class is 121 * passed via $_GET["baseClass"] and is the first class in 122 * the call sequence of the request. Do not call this method 123 * within other scripts than ilias.php. 124 * @throws ilCtrlException 125 */ 126 public function callBaseClass() 127 { 128 global $DIC; 129 130 $ilDB = $DIC->database(); 131 132 $baseClass = strtolower($_GET["baseClass"]); 133 134 $module_class = ilCachedCtrl::getInstance(); 135 $mc_rec = $module_class->lookupModuleClass($baseClass); 136 137 $module = $mc_rec["module"]; 138 $class = $mc_rec["class"]; 139 $class_dir = $mc_rec["dir"]; 140 141 if ($module != "") { 142 $m_set = $ilDB->query("SELECT * FROM il_component WHERE name = " . 143 $ilDB->quote($module, "text")); 144 $m_rec = $ilDB->fetchAssoc($m_set); 145 } else { // check whether class belongs to a service 146 $mc_rec = $module_class->lookupServiceClass($baseClass); 147 148 $service = $mc_rec["service"]; 149 $class = $mc_rec["class"]; 150 $class_dir = $mc_rec["dir"]; 151 152 if ($service == "") { 153 include_once("./Services/UICore/exceptions/class.ilCtrlException.php"); 154 throw new ilCtrlException("Could not find entry in modules.xml or services.xml for " . 155 $baseClass . " <br/>" . str_replace("&", "<br />&", htmlentities($_SERVER["REQUEST_URI"]))); 156 } 157 158 $m_rec = ilComponent::getComponentInfo('Services', $service); 159 } 160 161 // forward processing to base class 162 $this->getCallStructure(strtolower($baseClass)); 163 $base_class_gui = new $class(); 164 $this->forwardCommand($base_class_gui); 165 } 166 167 /** 168 * get directory of current module 169 * @deprecated 170 * @return mixed 171 * @throws Exception 172 */ 173 public function getModuleDir() 174 { 175 throw new Exception("ilCtrl::getModuleDir is deprecated."); 176 //return $this->module_dir; 177 } 178 179 /** 180 * Forward flow of control to next gui class 181 * this invokes the executeCommand() method of the 182 * gui object that is passed via reference 183 * 184 * @param object $a_gui_object gui object that should receive 185 * @return mixed return data of invoked executeCommand() method 186 * @throws ilCtrlException 187 */ 188 public function forwardCommand($a_gui_object) 189 { 190 $class = strtolower(get_class($a_gui_object)); 191 $nr = $this->getNodeIdForTargetClass($this->current_node, $class); 192 $nr = $nr["node_id"]; 193 if ($nr != "") { 194 $current_node = $this->current_node; 195 196 $this->current_node = $nr; 197 198 // always populate the call history 199 // it will only be displayed in DEVMODE but is needed for UI plugins, too 200 $this->call_hist[] = array("class" => get_class($a_gui_object), 201 "mode" => "execComm", "cmd" => $this->getCmd()); 202 203 $html = $a_gui_object->executeCommand(); 204 205 // reset current node 206 $this->current_node = $current_node; 207 208 return $html; 209 } 210 211 include_once("./Services/UICore/exceptions/class.ilCtrlException.php"); 212 throw new ilCtrlException("ERROR: Can't forward to class $class."); 213 } 214 215 /** 216 * Gets an HTML output from another GUI class and 217 * returns the flow of control to the calling class. 218 * 219 * @param object $a_gui_object GUI class that implements getHTML() method to return its HTML 220 * @param array|null $a_parameters parameter array 221 * @return string 222 * @throws ilCtrlException 223 */ 224 public function getHTML($a_gui_object, array $a_parameters = null) 225 { 226 $class = strtolower(get_class($a_gui_object)); 227 228 $nr = $this->getNodeIdForTargetClass($this->current_node, $class); 229 $nr = $nr["node_id"]; 230 if ($nr != "") { 231 $current_node = $this->current_node; 232 233 // set current node to new gui class 234 $this->current_node = $nr; 235 236 // always populate the call history 237 // it will only be displayed in DEVMODE but is needed for UI plugins, too 238 $this->call_hist[] = array("class" => get_class($a_gui_object), 239 "mode" => "getHtml", "cmd" => $this->getCmd()); 240 241 // get block 242 if ($a_parameters != null) { 243 $html = $a_gui_object->getHTML($a_parameters); 244 } else { 245 $html = $a_gui_object->getHTML(); 246 } 247 248 // reset current node 249 $this->current_node = $current_node; 250 251 // return block 252 return $html; 253 } 254 255 include_once("./Services/UICore/exceptions/class.ilCtrlException.php"); 256 throw new ilCtrlException("ERROR: Can't getHTML from class $class."); 257 } 258 259 /** 260 * Set context of current user interface. A context is a ILIAS repository 261 * object (obj ID + obj type) with an additional optional subobject (ID + Type) 262 * 263 * @param integer object ID 264 * @param string object type 265 * @param integer subobject ID 266 * @param string subobject type 267 */ 268 public function setContext($a_obj_id, $a_obj_type, $a_sub_obj_id = 0, $a_sub_obj_type = "") 269 { 270 $this->context_obj_id = $a_obj_id; 271 $this->context_obj_type = $a_obj_type; 272 $this->context_sub_obj_id = $a_sub_obj_id; 273 $this->context_sub_obj_type = $a_sub_obj_type; 274 } 275 276 /** 277 * Get context object id 278 * 279 * @return int object id 280 */ 281 public function getContextObjId() 282 { 283 return $this->context_obj_id; 284 } 285 286 /** 287 * Get context object type 288 * 289 * @return string object type 290 */ 291 public function getContextObjType() 292 { 293 return $this->context_obj_type; 294 } 295 296 /** 297 * Get context subobject id 298 * 299 * @return int subobject id 300 */ 301 public function getContextSubObjId() 302 { 303 return $this->context_sub_obj_id; 304 } 305 306 /** 307 * Get context subobject type 308 * 309 * @return string subobject type 310 */ 311 public function getContextSubObjType() 312 { 313 return $this->context_sub_obj_type; 314 } 315 316 /** 317 * Searches a node for a given class ($a_class) "near" another 318 * node ($a_par_node). 319 * 320 * It first looks if the given class is a child class of the current node. 321 * If such a child node has been found, its id is returned. 322 * 323 * If not, this method determines, whether the given class is a sibling 324 * of the current node within the call structure. If this is the case, 325 * then the corresponding id is returned. 326 * 327 * At last the method searches for the given class along the path from 328 * the current node to the root class of the call structure. 329 * 330 * @param string $a_par_node id of starting node for the search 331 * @param string $a_class class that should be searched 332 * @param bool $a_check 333 * @return array|bool id of target node that has been found 334 * @throws ilCtrlException 335 */ 336 private function getNodeIdForTargetClass($a_par_node, $a_class, $a_check = false) 337 { 338 $class = strtolower($a_class); 339 $this->readClassInfo($class); 340 341 if ($a_par_node === 0 || $a_par_node == "") { 342 return array("node_id" => $this->getCidForClass($class), 343 "base_class" => $class); 344 } 345 346 $this->readNodeInfo($a_par_node); 347 348 $node_cid = $this->getCurrentCidOfNode($a_par_node); 349 350 // target class is class of current node id 351 if ($class == $this->getClassForCid($node_cid)) { 352 return array("node_id" => $a_par_node, 353 "base_class" => ""); 354 } 355 356 // target class is child of current node id 357 if (isset($this->calls[$this->getClassForCid($node_cid)]) && 358 is_array($this->calls[$this->getClassForCid($node_cid)]) && 359 in_array($a_class, $this->calls[$this->getClassForCid($node_cid)])) { 360 return array("node_id" => $a_par_node . ":" . $this->getCidForClass($class), 361 "base_class" => ""); 362 } 363 364 // target class is sibling 365 $par_cid = $this->getParentCidOfNode($a_par_node); 366 if ($par_cid != "") { 367 if (is_array($this->calls[$this->getClassForCid($par_cid)]) && 368 in_array($a_class, $this->calls[$this->getClassForCid($par_cid)])) { 369 return array("node_id" => 370 $this->removeLastCid($a_par_node) . ":" . $this->getCidForClass($class), 371 "base_class" => ""); 372 } 373 } 374 375 // target class is parent 376 $temp_node = $this->removeLastCid($a_par_node); 377 while ($temp_node != "") { 378 $temp_cid = $this->getCurrentCidOfNode($temp_node); 379 if ($this->getClassForCid($temp_cid) == $a_class) { 380 return array("node_id" => $temp_node, 381 "base_class" => ""); 382 } 383 $temp_node = $this->removeLastCid($temp_node); 384 } 385 386 // target class is another base class 387 $n_class = ""; 388 if ($a_class != "") { 389 $module_class = ilCachedCtrl::getInstance(); 390 $mc_rec = $module_class->lookupModuleClass($class); 391 $n_class = $mc_rec['lower_class']; 392 393 if ($n_class == "") { 394 $mc_rec = $module_class->lookupServiceClass($class); 395 $n_class = $mc_rec['lower_class']; 396 } 397 398 if ($n_class != "") { 399 $this->getCallStructure($n_class); 400 return array("node_id" => $this->getCidForClass($n_class), 401 "base_class" => $class); 402 } 403 } 404 405 if ($a_check) { 406 return false; 407 } 408 409 // Please do NOT change these lines. 410 // Developers must be aware, if they use classes unknown to the controller 411 // otherwise certain problem will be extremely hard to track down... 412 413 error_log("ERROR: Can't find target class $a_class for node $a_par_node " . 414 "(" . $this->cid_class[$this->getParentCidOfNode($a_par_node)] . ")"); 415 416 include_once("./Services/UICore/exceptions/class.ilCtrlException.php"); 417 throw new ilCtrlException("ERROR: Can't find target class $a_class for node $a_par_node " . 418 "(" . $this->cid_class[$this->getParentCidOfNode($a_par_node)] . ")."); 419 } 420 421 /** 422 * Check whether target is valid 423 * 424 * @param 425 * @return 426 */ 427 public function checkTargetClass($a_class) 428 { 429 if (!is_array($a_class)) { 430 $a_class = array($a_class); 431 } 432 433 $nr = $this->current_node; 434 foreach ($a_class as $class) { 435 $class = strtolower($class); 436 437 if (!$this->getCidForClass($class, true)) { 438 return false; 439 } 440 441 $nr = $this->getNodeIdForTargetClass($nr, $class, true); 442 $nr = $nr["node_id"]; 443 if ($nr === false) { 444 return false; 445 } 446 } 447 return true; 448 } 449 450 /** 451 * Get command target node 452 * 453 * @return string id of current command target node 454 */ 455 public function getCmdNode() 456 { 457 return $_GET["cmdNode"]; 458 } 459 460 /** 461 * Add a tab to tabs array (@deprecated use $ilTabs) 462 * 463 * @param string $a_lang_var language variable 464 * @param string $a_link link 465 * @param string $a_cmd command (must be same as in link) 466 * @param string $a_class command class (must be same as in link) 467 */ 468 public function addTab($a_lang_var, $a_link, $a_cmd, $a_class) 469 { 470 $a_class = strtolower($a_class); 471 472 $this->tab[] = array("lang_var" => $a_lang_var, 473 "link" => $a_link, "cmd" => $a_cmd, "class" => $a_class); 474 } 475 476 /** 477 * Get tabs array (@deprecated, use $ilTabs) 478 * 479 * @return array array of tab entries (array("lang_var", "link", "cmd", "class)) 480 */ 481 public function getTabs() 482 { 483 return $this->tab; 484 } 485 486 /** 487 * Get controller call history. 488 * 489 * This is used for the developer mode and presented in the footer 490 * 491 * @return array array of call history entries 492 */ 493 public function getCallHistory() 494 { 495 return $this->call_hist; 496 } 497 498 /** 499 * Get call structure of class context. This method must be called 500 * for the top level gui class in the leading php script. It must be 501 * called before the the current command is forwarded to the top level 502 * gui class. Example: 503 * 504 * include_once "classes/class.ilRepositoryGUI.php"; 505 * $ilCtrl->getCallStructure("ilrepositorygui"); 506 * $repository_gui = new ilRepositoryGUI(); 507 * $ilCtrl->forwardCommand($repository_gui); 508 * 509 * @param string $a_class gui class name 510 * 511 * @access public 512 */ 513 public function getCallStructure($a_class) 514 { 515 $this->readClassInfo($a_class); 516 } 517 518 /** 519 * Reads call structure from db 520 */ 521 public function readCallStructure($a_class, $a_nr = 0, $a_parent = 0) 522 { 523 global $DIC; 524 525 $ilDB = $DIC->database(); 526 527 $a_class = strtolower($a_class); 528 529 $a_nr++; 530 531 // determine call node structure 532 $this->call_node[$a_nr] = array("class" => $a_class, "parent" => $a_parent); 533 534 $call_set = $ilDB->query("SELECT * FROM ctrl_calls WHERE parent = " . 535 $ilDB->quote(strtolower($a_class), "text") . 536 " ORDER BY child", array("text")); 537 $a_parent = $a_nr; 538 while ($call_rec = $ilDB->fetchAssoc($call_set)) { 539 $a_nr = $this->readCallStructure($call_rec["child"], $a_nr, $a_parent); 540 $forw[] = $call_rec["child"]; 541 } 542 543 // determine root class 544 $this->root_class = $a_class; 545 return $a_nr; 546 } 547 548 /** 549 * Set parameters that should be passed in every form and link of a 550 * gui class. All links that relate to the specified gui object class and 551 * are build e.g. by using getLinkTarger() or getFormAction() will include 552 * this parameter. This is the mechanism to add url parameters to the standard 553 * url target everytime. 554 * 555 * A typical example is the "ref_id" that should be included in almost every 556 * link or form action url. So the constructor of ilRepositoryGUI includes 557 * the command: 558 * 559 * $this->ctrl->saveParameter($this, array("ref_id")); 560 * 561 * @param object $a_obj gui object that will process the parameter 562 * @param mixed $a_parameter parameter name (string) or array of parameter 563 * names 564 * 565 * @access public 566 */ 567 public function saveParameter($a_obj, $a_parameter) 568 { 569 if (is_object($a_obj)) { 570 $this->saveParameterByClass(get_class($a_obj), $a_parameter); 571 } 572 } 573 574 /** 575 * Save parameter for a class 576 * 577 * @param string class name 578 * @param string parameter name 579 */ 580 public function saveParameterByClass($a_class, $a_parameter) 581 { 582 if (is_array($a_parameter)) { 583 foreach ($a_parameter as $parameter) { 584 $this->save_parameter[strtolower($a_class)][] = $parameter; 585 } 586 } else { 587 $this->save_parameter[strtolower($a_class)][] = $a_parameter; 588 } 589 } 590 591 592 /** 593 * Set parameters that should be passed a form and link of a 594 * gui class. All links that relate to the specified gui object class and 595 * are build e.g. by using getLinkTarger() or getFormAction() will include 596 * this parameter. This is the mechanism to add url parameters to the standard 597 * url target. The difference to the saveParameter() method is, that setParameter() 598 * does not simply forward the url parameter of the last request. You can set 599 * a spefific value. 600 * 601 * If this parameter is also a "saved parameter" (set by saveParameter() method) 602 * the saved value will be overwritten. 603 * 604 * The method is usually used in conjunction with a getFormAction() or getLinkTarget() 605 * call. E.g.: 606 * 607 * $this->ctrl->setParameter($this, "obj_id", $data_row["obj_id"]); 608 * $obj_link = $this->ctrl->getLinkTarget($this, "view"); 609 * 610 * @param object $a_obj gui object 611 * @param string $a_parameter parameter name 612 * @param string $a_parameter parameter value 613 */ 614 public function setParameter($a_obj, $a_parameter, $a_value) 615 { 616 $this->parameter[strtolower(get_class($a_obj))][$a_parameter] = $a_value; 617 } 618 619 620 /** 621 * Same as setParameterByClass, except that a class name is passed. 622 * 623 * @param string $a_class gui class name 624 * @param string $a_parameter parameter name 625 * @param string $a_parameter parameter value 626 */ 627 public function setParameterByClass($a_class, $a_parameter, $a_value) 628 { 629 $this->parameter[strtolower($a_class)][$a_parameter] = $a_value; 630 } 631 632 /** 633 * Same as setParameterByClass, except that a class name is passed. 634 * 635 * @param string $a_class gui class name 636 * @param string $a_parameter parameter name 637 * @param string $a_parameter parameter value 638 */ 639 public function clearParameterByClass($a_class, $a_parameter) 640 { 641 unset($this->parameter[strtolower($a_class)][$a_parameter]); 642 } 643 644 /** 645 * Clears all parameters that have been set via setParameter for 646 * a GUI class. 647 * 648 * @param object $a_obj gui object 649 */ 650 public function clearParameters($a_obj) 651 { 652 $this->clearParametersByClass(strtolower(get_class($a_obj))); 653 } 654 655 /** 656 * Clears all parameters that have been set via setParameter for 657 * a GUI class. 658 * 659 * @param string $a_class gui class name 660 */ 661 public function clearParametersByClass($a_class) 662 { 663 $this->parameter[strtolower($a_class)] = array(); 664 } 665 666 protected function checkLPSettingsForward($a_gui_obj, $a_cmd_node) 667 { 668 global $DIC; 669 670 $objDefinition = $DIC["objDefinition"]; 671 672 // forward to learning progress settings if possible and accessible 673 if ($_GET["gotolp"] && 674 $a_gui_obj) { 675 $ref_id = $_GET["ref_id"]; 676 if (!$ref_id) { 677 $ref_id = $_REQUEST["ref_id"]; 678 } 679 680 $gui_class = get_class($a_gui_obj); 681 682 if ($gui_class == "ilSAHSEditGUI") { 683 // #1625 - because of scorm "sub-types" this is all very special 684 include_once "./Modules/ScormAicc/classes/class.ilObjSAHSLearningModule.php"; 685 $obj_id = ilObject::_lookupObjectId($ref_id); 686 switch (ilObjSAHSLearningModule::_lookupSubType($obj_id)) { 687 case "scorm2004": 688 $class = "ilObjSCORM2004LearningModuleGUI"; 689 break; 690 691 case "scorm": 692 $class = "ilObjSCORMLearningModuleGUI"; 693 break; 694 695 case "aicc": 696 $class = "ilObjAICCLearningModuleGUI"; 697 break; 698 699 case "hacp": 700 $class = "ilObjHACPLearningModuleGUI"; 701 break; 702 } 703 if ($GLOBALS["ilAccess"]->checkAccess("edit_learning_progress", "", $ref_id)) { 704 $this->redirectByClass(array($gui_class, $class, "illearningprogressgui", "illplistofsettingsgui"), ""); 705 } 706 } 707 // special case: cannot use any presentation GUIs 708 elseif ($gui_class == "ilLMPresentationGUI") { 709 $this->setParameterByClass("ilObjLearningModuleGUI", "gotolp", 1); 710 $this->redirectByClass(array("ilLMEditorGUI", "ilObjLearningModuleGUI"), ""); 711 } 712 713 include_once "Services/Object/classes/class.ilObjectLP.php"; 714 $type = ilObject::_lookupType($ref_id, true); 715 $class = "ilObj" . $objDefinition->getClassName($type) . "GUI"; 716 717 if ($gui_class == $class && 718 ilObjectLP::isSupportedObjectType($type) && 719 $GLOBALS["ilAccess"]->checkAccess("edit_learning_progress", "", $ref_id)) { 720 // add path to repository object gui if missing from cmdNode 721 if (!$a_cmd_node) { 722 $repo_node = $this->getNodeIdForTargetClass(null, "ilrepositorygui"); 723 $obj_node = $this->getNodeIdForTargetClass($repo_node["node_id"], $gui_class); 724 $a_cmd_node = $obj_node["node_id"]; 725 } 726 // find path to lp settings 727 $lp_node = $this->getNodeIdForTargetClass($a_cmd_node, "illearningprogressgui"); 728 $lp_settings_node = $this->getNodeIdForTargetClass($lp_node["node_id"], "illplistofsettingsgui"); 729 $_GET["cmdNode"] = $lp_settings_node["node_id"]; 730 $_GET["cmdClass"] = "ilLPListOfSettingsGUI"; 731 $_GET["cmd"] = ""; 732 return "illearningprogressgui"; 733 } 734 } 735 } 736 737 /** 738 * Get next class in the control path from the current class 739 * to the target command class. This is the class that should 740 * be instantiated and be invoked via $ilCtrl->forwardCommand($class) 741 * next. 742 * 743 * @return string class name of next class 744 */ 745 public function getNextClass($a_gui_class = null) 746 { 747 $cmdNode = $this->getCmdNode(); 748 if ($cmdNode == "") { 749 return ($class = $this->checkLPSettingsForward($a_gui_class, $cmdNode)) 750 ? $class 751 : false; 752 } else { 753 if ($this->current_node == $cmdNode) { 754 return ($class = $this->checkLPSettingsForward($a_gui_class, $cmdNode)) 755 ? $class 756 : ""; 757 } else { 758 $path = $this->getPathNew($this->current_node, $cmdNode); 759 $this->readCidInfo($this->getCurrentCidOfNode($path[1])); 760 return $this->cid_class[$this->getCurrentCidOfNode($path[1])]; 761 } 762 } 763 } 764 765 /** 766 * Get class path that can be used in include statements 767 * for a given class name. 768 * 769 * @param string $a_class_name class name 770 */ 771 public function lookupClassPath($a_class_name) 772 { 773 $a_class_name = strtolower($a_class_name); 774 775 $cached_ctrl = ilCachedCtrl::getInstance(); 776 $class_rec = $cached_ctrl->lookupClassFile($a_class_name); 777 778 if ($class_rec["plugin_path"] != "") { 779 return $class_rec["plugin_path"] . "/" . $class_rec["filename"]; 780 } else { 781 return $class_rec["filename"]; 782 } 783 } 784 785 /** 786 * this method assumes that the class path has the format "dir/class.<class_name>.php" 787 * 788 * @param string $a_class_path class path 789 * @access public 790 * 791 * @return string class name 792 */ 793 public function getClassForClasspath($a_class_path) 794 { 795 $path = pathinfo($a_class_path); 796 $file = $path["basename"]; 797 $class = substr($file, 6, strlen($file) - 10); 798 799 return $class; 800 } 801 802 /** 803 * Get path in call structure. 804 * 805 * @param string $a_source_node source node id 806 * @param string $a_source_node target node id 807 */ 808 private function getPathNew($a_source_node, $a_target_node) 809 { 810 if ($a_source_node == "1") { 811 $a_source_node = ""; 812 } 813 if (substr($a_target_node, 0, strlen($a_source_node)) != $a_source_node) { 814 $failure = "ERROR: Path not found. Source:" . $a_source_node . 815 ", Target:" . $a_target_node; 816 if (DEVMODE == 1) { 817 include_once("./Services/UICore/exceptions/class.ilCtrlException.php"); 818 throw new ilCtrlException($failure); 819 } 820 $GLOBALS['ilLog']->write(__METHOD__ . ' ' . $failure); 821 $this->redirectToURL('./ilias.php?baseClass=ilRepositoryGUI'); 822 } 823 $temp_node = $a_source_node; 824 825 $path = array(); 826 if ($a_source_node != "") { 827 $path = array($a_source_node); 828 } 829 830 $diffstart = ($a_source_node == "") 831 ? 0 832 : strlen($a_source_node) + 1; 833 $diff = substr($a_target_node, $diffstart); 834 $diff_arr = explode(":", $diff); 835 foreach ($diff_arr as $cid) { 836 if ($temp_node != "") { 837 $temp_node .= ":"; 838 } 839 $temp_node .= $cid; 840 $path[] = $temp_node; 841 } 842 return $path; 843 } 844 845 /** 846 * set target script name 847 * 848 * @param string $a_target_script target script name 849 */ 850 public function setTargetScript(string $a_target_script) 851 { 852 $this->target_script = $a_target_script; 853 } 854 855 856 /** 857 * Get target script name 858 * 859 * @return string target script name 860 */ 861 public function getTargetScript() : string 862 { 863 return $this->target_script; 864 } 865 866 867 /** 868 * Initialises new base class 869 * 870 * Note: this resets the whole current ilCtrl context completely. 871 * You can call callBaseClass() after that. 872 * 873 * @param string base class name 874 */ 875 public function initBaseClass($a_base_class) 876 { 877 $_GET["baseClass"] = $a_base_class; 878 $_GET["cmd"] = ""; 879 $_GET["cmdClass"] = ""; 880 $_GET["cmdNode"] = ""; 881 $this->initializeMemberVariables(); 882 } 883 884 /** 885 * Determines current get/post command 886 * 887 * @param string default command 888 * @param array safe commands: for these commands no token 889 * is checked for post requests 890 */ 891 public function getCmd($a_default_cmd = "", $a_safe_commands = "") 892 { 893 $cmd = ""; 894 if (isset($_GET["cmd"])) { 895 $cmd = $_GET["cmd"]; 896 } 897 if ($cmd == "post") { 898 if (isset($_POST["cmd"]) && is_array($_POST["cmd"])) { 899 reset($_POST["cmd"]); 900 } 901 $cmd = @key($_POST["cmd"]); 902 903 // verify command 904 if ($this->verified_cmd != "") { 905 return $this->verified_cmd; 906 } else { 907 if (!$this->verifyToken() && 908 (!is_array($a_safe_commands) || !in_array($cmd, $a_safe_commands))) { 909 return $a_default_cmd; 910 } 911 } 912 913 $this->verified_cmd = $cmd; 914 if ($cmd == "" && isset($_POST["table_top_cmd"])) { // selected command in multi-list (table2) 915 $cmd = @key($_POST["table_top_cmd"]); 916 $this->verified_cmd = $cmd; 917 $_POST[$_POST["cmd_sv"][$cmd]] = $_POST[$_POST["cmd_sv"][$cmd] . "_2"]; 918 } 919 if ($cmd == "" && isset($_POST["select_cmd2"])) { // selected command in multi-list (table2) 920 if (isset($_POST["select_cmd_all2"])) { 921 $_POST["select_cmd_all"] = $_POST["select_cmd_all2"]; 922 } else { 923 $_POST["select_cmd_all"] = $_POST["select_cmd_all2"] = null; 924 } 925 $cmd = $_POST["selected_cmd2"]; 926 $this->verified_cmd = $cmd; 927 } 928 if ($cmd == "" && isset($_POST["select_cmd"])) { // selected command in multi-list (table2) 929 if (isset($_POST["select_cmd_all"])) { 930 $_POST["select_cmd_all2"] = $_POST["select_cmd_all"]; 931 } else { 932 $_POST["select_cmd_all"] = $_POST["select_cmd_all2"] = null; 933 } 934 $cmd = $_POST["selected_cmd"]; 935 $this->verified_cmd = $cmd; 936 } 937 if ($cmd == "") { 938 $cmd = $_GET["fallbackCmd"]; 939 $this->verified_cmd = $cmd; 940 } 941 } 942 if ($cmd == "") { 943 $cmd = $a_default_cmd; 944 } 945 return $cmd; 946 } 947 948 /** 949 * Set the current command 950 * 951 * IMPORTANT NOTE: 952 * 953 * please use this function only in exceptional cases 954 * it is not intended for setting commands in forms or links! 955 * use the corresponding parameters of getFormAction() and 956 * getLinkTarget() instead. 957 */ 958 public function setCmd($a_cmd) 959 { 960 $_GET["cmd"] = $a_cmd; 961 } 962 963 /** 964 * Set the current command class 965 * 966 * IMPORTANT NOTE: 967 * 968 * please use this function only in exceptional cases 969 * it is not intended for setting the command class in forms or links! 970 * use the corresponding parameters of getFormAction() and 971 * getLinkTarget() instead. 972 */ 973 public function setCmdClass($a_cmd_class) 974 { 975 $a_cmd_class = strtolower($a_cmd_class); 976 $nr = $this->getNodeIdForTargetClass($this->current_node, $a_cmd_class); 977 $nr = $nr["node_id"]; 978 $_GET["cmdClass"] = $a_cmd_class; 979 $_GET["cmdNode"] = $nr; 980 } 981 982 /** 983 * Determines class that should execute the current command 984 * 985 * @return string class name 986 */ 987 public function getCmdClass() 988 { 989 return strtolower($_GET["cmdClass"]); 990 } 991 992 /** 993 * Get form action url for gui class object 994 * 995 * @param object gui object 996 * @param string fallback command 997 * @param string anchor 998 * @param bool asynchronous call 999 * @param bool xml style t/f 1000 * @return string script url 1001 */ 1002 public function getFormAction( 1003 $a_gui_obj, 1004 $a_fallback_cmd = "", 1005 $a_anchor = "", 1006 $a_asynch = false, 1007 $xml_style = false 1008 ) { 1009 $script = $this->getFormActionByClass( 1010 strtolower(get_class($a_gui_obj)), 1011 $a_fallback_cmd, 1012 $a_anchor, 1013 $a_asynch, 1014 $xml_style 1015 ); 1016 return $script; 1017 } 1018 1019 /** 1020 * Get form action url for gui class name 1021 * 1022 * @param string gui class name 1023 * @param string fallback command 1024 * @param string anchor 1025 * @param bool asynchronous call 1026 * @param bool xml style t/f 1027 * @return string script url 1028 */ 1029 public function getFormActionByClass( 1030 $a_class, 1031 $a_fallback_cmd = "", 1032 $a_anchor = "", 1033 $a_asynch = false, 1034 $xml_style = false 1035 ) { 1036 if (!is_array($a_class)) { 1037 $a_class = strtolower($a_class); 1038 } 1039 1040 $tok = $this->getRequestToken(); 1041 1042 if ($a_asynch) { 1043 $xml_style = false; 1044 } 1045 1046 $script = $this->getLinkTargetByClass($a_class, "post", "", $a_asynch, $xml_style); 1047 if ($a_fallback_cmd != "") { 1048 $script = ilUtil::appendUrlParameterString($script, "fallbackCmd=" . $a_fallback_cmd, $xml_style); 1049 } 1050 $script = ilUtil::appendUrlParameterString( 1051 $script, 1052 self::IL_RTOKEN_NAME . '=' . $this->getRequestToken(), 1053 $xml_style 1054 ); 1055 if ($a_anchor != "") { 1056 $script = $script . "#" . $a_anchor; 1057 } 1058 1059 return $script; 1060 } 1061 1062 /** 1063 * Append request token as url parameter 1064 * 1065 * @param string url 1066 * @param boolean xml style 1067 */ 1068 public function appendRequestTokenParameterString($a_url, $xml_style = false) 1069 { 1070 return ilUtil::appendUrlParameterString( 1071 $a_url, 1072 self::IL_RTOKEN_NAME . '=' . $this->getRequestToken(), 1073 $xml_style 1074 ); 1075 } 1076 1077 /** 1078 * Get request token. 1079 * 1080 * @return string request token for user and session 1081 */ 1082 public function getRequestToken() 1083 { 1084 global $DIC; 1085 1086 $ilUser = $DIC["ilUser"]; 1087 $ilDB = $DIC->database(); 1088 1089 1090 if ($this->rtoken != "") { 1091 return $this->rtoken; 1092 } else { 1093 if (is_object($ilDB) && is_object($ilUser) && $ilUser->getId() > 0 && 1094 $ilUser->getId() != ANONYMOUS_USER_ID) { 1095 $res = $ilDB->query("SELECT token FROM il_request_token WHERE user_id = " . 1096 $ilDB->quote($ilUser->getId(), "integer") . 1097 " AND session_id = " . $ilDB->quote(session_id(), "text")); 1098 $rec = $ilDB->fetchAssoc($res); 1099 if ($rec["token"] != "") { 1100 $this->rtoken = $rec["token"]; 1101 return $rec["token"]; 1102 } 1103 //echo "new rtoken, new entry for :".$ilUser->getId().":".session_id().":"; exit; 1104 $random = new \ilRandom(); 1105 $this->rtoken = md5(uniqid($random->int(), true)); 1106 1107 // delete entries older than one and a half days 1108 if ($random->int(1, 200) == 2) { 1109 $dt = new ilDateTime(time(), IL_CAL_UNIX); 1110 $dt->increment(IL_CAL_DAY, -1); 1111 $dt->increment(IL_CAL_HOUR, -12); 1112 $dq = "DELETE FROM il_request_token WHERE " . 1113 " stamp < " . $ilDB->quote($dt->get(IL_CAL_DATETIME), "timestamp"); 1114 $ilDB->manipulate($dq); 1115 } 1116 1117 // IMPORTANT: Please do NOT try to move this implementation to a 1118 // session basis. This will fail due to framesets that are used 1119 // occasionally in ILIAS, e.g. in the chat, where multiple 1120 // forms are loaded in different frames. 1121 $ilDB->manipulate("INSERT INTO il_request_token (user_id, token, stamp, session_id) VALUES " . 1122 "(" . 1123 $ilDB->quote($ilUser->getId(), "integer") . "," . 1124 $ilDB->quote($this->rtoken, "text") . "," . 1125 $ilDB->now() . "," . 1126 $ilDB->quote(session_id(), "text") . ")"); 1127 return $this->rtoken; 1128 } 1129 } 1130 return ""; 1131 } 1132 1133 /** 1134 * Verify Token 1135 * 1136 * @return boolean valid t/f 1137 */ 1138 private function verifyToken() 1139 { 1140 global $DIC; 1141 1142 $ilUser = $DIC["ilUser"]; 1143 1144 $ilDB = $DIC->database(); 1145 ; 1146 1147 if (is_object($ilUser) && is_object($ilDB) && $ilUser->getId() > 0 && 1148 $ilUser->getId() != ANONYMOUS_USER_ID) { 1149 if ($_GET["rtoken"] == "") { 1150 return false; 1151 } 1152 1153 $set = $ilDB->query("SELECT * FROM il_request_token WHERE " . 1154 " user_id = " . $ilDB->quote($ilUser->getId(), "integer") . " AND " . 1155 " token = " . $ilDB->quote($_GET[self::IL_RTOKEN_NAME]), "text"); 1156 if ($ilDB->numRows($set) > 0) { 1157 // remove tokens from older sessions 1158 // if we do this immediately, working with multiple windows does not work: 1159 // - window one: open form (with token a) 1160 // - window two: open form (with token b) 1161 // - submit window one: a is verified, but b must not be deleted immediately, otherwise 1162 // - window two: submit results in invalid token 1163 // see also bug #13551 1164 $dt = new ilDateTime(time(), IL_CAL_UNIX); 1165 $dt->increment(IL_CAL_DAY, -1); 1166 $dt->increment(IL_CAL_HOUR, -12); 1167 $ilDB->manipulate("DELETE FROM il_request_token WHERE " . 1168 " user_id = " . $ilDB->quote($ilUser->getId(), "integer") . " AND " . 1169 " session_id != " . $ilDB->quote(session_id(), "text") . " AND " . 1170 " stamp < " . $ilDB->quote($dt->get(IL_CAL_DATETIME), "timestamp")); 1171 return true; 1172 } else { 1173 return false; 1174 } 1175 1176 if ($_SESSION["rtokens"][$_GET[self::IL_RTOKEN_NAME]] != "") { 1177 // remove used token 1178 unset($_SESSION["rtokens"][$_GET[self::IL_RTOKEN_NAME]]); 1179 1180 // remove old tokens 1181 if (count($_SESSION["rtokens"]) > 100) { 1182 $to_remove = array(); 1183 $sec = 7200; // two hours 1184 1185 foreach ($_SESSION["rtokens"] as $tok => $time) { 1186 if (time() - $time > $sec) { 1187 $to_remove[] = $tok; 1188 } 1189 } 1190 foreach ($to_remove as $tok) { 1191 unset($_SESSION["rtokens"][$tok]); 1192 } 1193 } 1194 1195 return true; 1196 } 1197 return false; 1198 } else { 1199 return true; // do not verify, if user or db object is missing 1200 } 1201 1202 return false; 1203 } 1204 1205 /** 1206 * Redirect to another command 1207 * 1208 * @param object gui object 1209 * @param string command 1210 * @param string anchor 1211 */ 1212 public function redirect($a_gui_obj, $a_cmd = "", $a_anchor = "", $a_asynch = false) 1213 { 1214 $script = $this->getLinkTargetByClass( 1215 strtolower(get_class($a_gui_obj)), 1216 $a_cmd, 1217 "", 1218 $a_asynch, 1219 false 1220 ); 1221 if ($a_anchor != "") { 1222 $script = $script . "#" . $a_anchor; 1223 } 1224 $this->redirectToURL($script); 1225 } 1226 1227 1228 /** 1229 * @param $a_script 1230 */ 1231 public function redirectToURL($a_script) 1232 { 1233 global $DIC; 1234 1235 $ilPluginAdmin = null; 1236 if (isset($DIC["ilPluginAdmin"])) { 1237 $ilPluginAdmin = $DIC["ilPluginAdmin"]; 1238 } 1239 1240 if (!is_int(strpos($a_script, "://"))) { 1241 if (substr($a_script, 0, 1) != "/" && defined("ILIAS_HTTP_PATH")) { 1242 if (is_int(strpos($_SERVER["PHP_SELF"], "/setup/"))) { 1243 $a_script = "setup/" . $a_script; 1244 } 1245 $a_script = ILIAS_HTTP_PATH . "/" . $a_script; 1246 } 1247 } 1248 1249 // include the user interface hook 1250 if (is_object($ilPluginAdmin)) { 1251 $pl_names = $ilPluginAdmin->getActivePluginsForSlot(IL_COMP_SERVICE, "UIComponent", "uihk"); 1252 foreach ($pl_names as $pl) { 1253 $ui_plugin = ilPluginAdmin::getPluginObject(IL_COMP_SERVICE, "UIComponent", "uihk", $pl); 1254 $gui_class = $ui_plugin->getUIClassInstance(); 1255 $resp = $gui_class->getHTML("Services/Utilities", "redirect", array( "html" => $a_script )); 1256 if ($resp["mode"] != ilUIHookPluginGUI::KEEP) { 1257 $a_script = $gui_class->modifyHTML($a_script, $resp); 1258 } 1259 } 1260 } 1261 1262 // Manually trigger to write and close the session. This has the advantage that if an exception is thrown 1263 // during the writing of the session (ILIAS writes the session into the database by default) we get an exception 1264 // if the session_write_close() is triggered by exit() then the exception will be dismissed but the session 1265 // is never written, which is a nightmare to develop with. 1266 session_write_close(); 1267 1268 global $DIC; 1269 $http = $DIC->http(); 1270 switch ($http->request()->getHeaderLine('Accept')) { 1271 case 'application/json': 1272 $stream = \ILIAS\Filesystem\Stream\Streams::ofString(json_encode([ 1273 'success' => true, 1274 'message' => 'Called redirect after async fileupload request', 1275 "redirect_url" => $a_script, 1276 ])); 1277 $http->saveResponse($http->response()->withBody($stream)); 1278 break; 1279 default: 1280 $http->saveResponse($http->response()->withAddedHeader("Location", $a_script)); 1281 break; 1282 } 1283 $http->sendResponse(); 1284 exit; 1285 } 1286 1287 1288 /** 1289 * Redirect to other gui class using class name 1290 * 1291 * @param string command target class 1292 * @param string command 1293 */ 1294 public function redirectByClass($a_class, $a_cmd = "", $a_anchor = "", $a_asynch = false) 1295 { 1296 $script = $this->getLinkTargetByClass($a_class, $a_cmd, "", $a_asynch, false); 1297 if ($a_anchor != "") { 1298 $script = $script . "#" . $a_anchor; 1299 } 1300 $this->redirectToURL($script); 1301 } 1302 1303 /** 1304 * Is current command an asynchronous command? 1305 * 1306 * @return boolean asynchronous t/f 1307 */ 1308 public function isAsynch() 1309 { 1310 if (isset($_GET["cmdMode"]) && $_GET["cmdMode"] == "asynch") { 1311 return true; 1312 } else { 1313 return false; 1314 } 1315 } 1316 1317 1318 /** 1319 * Get link target for command using gui object 1320 * 1321 * @param object gui object (usually $this) 1322 * @param string command 1323 * @param string # anchor 1324 * @param boolean asynchronous mode 1325 * @param boolean xml style t/f 1326 * 1327 * @return string target link 1328 */ 1329 public function getLinkTarget( 1330 $a_gui_obj, 1331 $a_cmd = "", 1332 $a_anchor = "", 1333 $a_asynch = false, 1334 $xml_style = false 1335 ) { 1336 $script = $this->getLinkTargetByClass( 1337 strtolower(get_class($a_gui_obj)), 1338 $a_cmd, 1339 $a_anchor, 1340 $a_asynch, 1341 $xml_style 1342 ); 1343 return $script; 1344 } 1345 1346 1347 /** 1348 * Get link target for command using gui class name 1349 * 1350 * @param string/array command target class 1351 * @param string command 1352 * @param string # anchor 1353 * @param boolean asynchronous mode 1354 * @param boolean xml style t/f 1355 * 1356 * @return string target link 1357 */ 1358 public function getLinkTargetByClass( 1359 $a_class, 1360 $a_cmd = "", 1361 $a_anchor = "", 1362 $a_asynch = false, 1363 $xml_style = false 1364 ) { 1365 if ($a_asynch) { 1366 $xml_style = false; 1367 } 1368 1369 $script = $this->getTargetScript(); 1370 $script = $this->getUrlParameters($a_class, $script, $a_cmd, $xml_style); 1371 1372 if ($a_asynch) { 1373 $amp = "&"; 1374 $script .= $amp . "cmdMode=asynch"; 1375 } 1376 1377 if ($a_anchor != "") { 1378 $script = $script . "#" . $a_anchor; 1379 } 1380 1381 return $script; 1382 } 1383 1384 /** 1385 * Set return command 1386 */ 1387 public function setReturn($a_gui_obj, $a_cmd) 1388 { 1389 $script = $this->getTargetScript(); 1390 $script = $this->getUrlParameters(strtolower(get_class($a_gui_obj)), $script, $a_cmd); 1391 $this->return[strtolower(get_class($a_gui_obj))] = $script; 1392 } 1393 1394 /** 1395 * Set return command 1396 */ 1397 public function setReturnByClass($a_class, $a_cmd) 1398 { 1399 // may not be an array! 1400 $a_class = strtolower($a_class); 1401 1402 $script = $this->getTargetScript(); 1403 $script = $this->getUrlParameters($a_class, $script, $a_cmd); 1404 $this->return[strtolower($a_class)] = $script; 1405 } 1406 1407 /** 1408 * Redirects to next parent class that used setReturn. 1409 */ 1410 public function returnToParent($a_gui_obj, $a_anchor = "") 1411 { 1412 $script = $this->getParentReturn($a_gui_obj); 1413 1414 $script = ilUtil::appendUrlParameterString( 1415 $script, 1416 "redirectSource=" . strtolower(get_class($a_gui_obj)) 1417 ); 1418 $script = ilUtil::appendUrlParameterString( 1419 $script, 1420 "cmdMode=" . $_GET["cmdMode"] 1421 ); 1422 if ($a_anchor != "") { 1423 $script = $script . "#" . $a_anchor; 1424 } 1425 1426 $this->redirectToURL($script); 1427 } 1428 1429 1430 /** 1431 * Get return script url. 1432 * 1433 * Used in conjunction with ilTabs->setBackTarget and ilBlockGUI->addHeaderCommand. 1434 */ 1435 public function getParentReturn($a_gui_obj) 1436 { 1437 return $this->getParentReturnByClass(strtolower(get_class($a_gui_obj))); 1438 } 1439 1440 1441 /** 1442 * Get return script url 1443 * 1444 * Only used in getParentReturn. 1445 */ 1446 protected function getParentReturnByClass($a_class) 1447 { 1448 $a_class = strtolower($a_class); 1449 $ret_class = $this->searchReturnClass($a_class); 1450 if ($ret_class) { 1451 return $this->return[$ret_class]; 1452 } 1453 } 1454 1455 /** 1456 * Get return class. 1457 * 1458 * Only used in COPage/ilPCParagraphGUI and COPage/ilPCPlaceHolderGUI 1459 * 1460 * @param string|object $class 1461 * @return string|bool 1462 */ 1463 public function getReturnClass($a_class) 1464 { 1465 if (is_object($a_class)) { 1466 $class = strtolower(get_class($a_class)); 1467 } else { 1468 $class = strtolower($a_class); 1469 } 1470 return $this->searchReturnClass($class); 1471 } 1472 1473 1474 /** 1475 * Determine current return class 1476 */ 1477 private function searchReturnClass($a_class) 1478 { 1479 $a_class = strtolower($a_class); 1480 1481 $node = $this->getNodeIdForTargetClass($this->current_node, $a_class); 1482 $node = $node["node_id"]; 1483 $n_arr = explode(":", $node); 1484 for ($i = count($n_arr) - 2; $i >= 0; $i--) { 1485 if ($this->return[$this->getClassForCid($n_arr[$i])] != "") { 1486 return $this->getClassForCid($n_arr[$i]); 1487 } 1488 } 1489 1490 return false; 1491 } 1492 1493 /** 1494 * Get current redirect source 1495 * 1496 * @return string redirect source class 1497 */ 1498 public function getRedirectSource() 1499 { 1500 return $_GET["redirectSource"]; 1501 } 1502 1503 /** 1504 * Get URL parameters for a class and append them to a string 1505 * @param $a_class 1506 * @param $a_str 1507 * @param string $a_cmd command 1508 * @param bool $xml_style 1509 * @return string 1510 */ 1511 public function getUrlParameters($a_class, $a_str, $a_cmd = "", $xml_style = false) 1512 { 1513 $params = $this->getParameterArrayByClass($a_class, $a_cmd); 1514 1515 foreach ($params as $par => $value) { 1516 if (strlen((string) $value)) { 1517 $a_str = ilUtil::appendUrlParameterString($a_str, $par . "=" . $value, $xml_style); 1518 } 1519 } 1520 1521 return $a_str; 1522 } 1523 1524 /** 1525 * Get all set/save parameters for a gui object 1526 */ 1527 public function getParameterArray($a_gui_obj, $a_cmd = "") 1528 { 1529 $par_arr = $this->getParameterArrayByClass(strtolower(get_class($a_gui_obj)), $a_cmd); 1530 1531 return $par_arr; 1532 } 1533 1534 /** 1535 * Get all set/save parameters using gui class name 1536 * 1537 * @param string class name 1538 * @param string cmd 1539 $ @return array parameter array 1540 */ 1541 public function getParameterArrayByClass($a_class, $a_cmd = "") 1542 { 1543 if ($a_class == "") { 1544 return array(); 1545 } 1546 1547 if (!is_array($a_class)) { 1548 $a_class = array($a_class); 1549 } 1550 1551 $nr = $this->current_node; 1552 $new_baseclass = ""; 1553 foreach ($a_class as $class) { 1554 $class = strtolower($class); 1555 $nr = $this->getNodeIdForTargetClass($nr, $class); 1556 if ($nr["base_class"] != "") { 1557 $new_baseclass = $nr["base_class"]; 1558 } 1559 $nr = $nr["node_id"]; 1560 $target_class = $class; 1561 } 1562 1563 $path = $this->getPathNew(1, $nr); 1564 $params = array(); 1565 1566 // append parameters of parent classes 1567 foreach ($path as $node_id) { 1568 $class = ($node_id == "") 1569 ? strtolower($_GET["baseClass"]) 1570 : $this->getClassForCid($this->getCurrentCidOfNode($node_id)); 1571 if (isset($this->save_parameter[$class]) && is_array($this->save_parameter[$class])) { 1572 foreach ($this->save_parameter[$class] as $par) { 1573 if (isset($_GET[$par])) { 1574 $params[$par] = $_GET[$par]; 1575 } elseif (isset($_POST[$par])) { 1576 $params[$par] = ilUtil::stripSlashesRecursive($_POST[$par]); 1577 } 1578 } 1579 } 1580 1581 if (isset($this->parameter[$class]) && is_array($this->parameter[$class])) { 1582 foreach ($this->parameter[$class] as $par => $value) { 1583 $params[$par] = $value; 1584 } 1585 } 1586 } 1587 1588 if ($a_cmd != "") { 1589 $params["cmd"] = $a_cmd; 1590 } 1591 1592 $params["cmdClass"] = $target_class; 1593 $params["cmdNode"] = $nr; 1594 if ($new_baseclass == "") { 1595 $params["baseClass"] = $_GET["baseClass"]; 1596 } else { 1597 $params["baseClass"] = $new_baseclass; 1598 } 1599 1600 return $params; 1601 } 1602 1603 private function classCidUnknown($a_class) 1604 { 1605 return $this->class_cid[$a_class] == ""; 1606 } 1607 1608 /** 1609 * Get class id for class after fetching and storing corresponding information, if necessary. 1610 */ 1611 private function getCidForClass($a_class, $a_check = false) 1612 { 1613 if ($this->classCidUnknown($a_class)) { 1614 $this->readClassInfo($a_class); 1615 } 1616 if ($this->classCidUnknown($a_class)) { 1617 if ($a_check) { 1618 return false; 1619 } 1620 if (DEVMODE == 1) { 1621 $add = "<br><br>Please make sure your GUI class name ends with 'GUI' and that the filename is 'class.[YourClassName].php'. In exceptional cases you 1622 may solve the issue by putting an empty * @ilCtrl_Calls [YourClassName]: into your class header." . 1623 " In both cases you need to reload the control structure in the setup."; 1624 } 1625 include_once("./Services/UICore/exceptions/class.ilCtrlException.php"); 1626 throw new ilCtrlException("Cannot find cid for class " . $a_class . "." . $add); 1627 } 1628 return $this->class_cid[$a_class]; 1629 } 1630 1631 private function cidClassUnknown($a_cid) 1632 { 1633 return $this->cid_class[$a_cid] == ""; 1634 } 1635 1636 1637 /** 1638 * Get class for class id after fetching and storing corresponding information, if necessary. 1639 */ 1640 private function getClassForCid($a_cid) 1641 { 1642 if ($this->cidClassUnknown($a_cid)) { 1643 $this->readCidInfo($a_cid); 1644 } 1645 if ($this->cidClassUnknown($a_cid)) { 1646 include_once("./Services/UICore/exceptions/class.ilCtrlException.php"); 1647 throw new ilCtrlException("Cannot find class for cid " . $a_cid . "."); 1648 } 1649 return $this->cid_class[$a_cid]; 1650 } 1651 1652 private function fetchCallsOfClassFromCache($a_class, ilCachedCtrl $a_cached_ctrl) 1653 { 1654 foreach ($a_cached_ctrl->lookupCall($a_class) as $call) { 1655 if ($call["child"] != "" && $this->callOfClassNotKnown($a_class, $call['child'])) { 1656 $this->calls[$a_class][] = $call["child"]; 1657 } 1658 } 1659 } 1660 1661 /** 1662 * Save class respective to $a_cid and store corresponding 1663 * class calls for future reference. 1664 * 1665 * @param object $a_cid cid 1666 */ 1667 private function readCidInfo($a_cid) 1668 { 1669 if (isset($this->info_read_cid[$a_cid])) { 1670 return; 1671 } 1672 1673 $cached_ctrl = ilCachedCtrl::getInstance(); 1674 $cid_info = $cached_ctrl->lookupCid($a_cid); 1675 1676 if ($cid_info) { 1677 $this->updateClassCidMap($cid_info['class'], $a_cid); 1678 $this->fetchCallsOfClassFromCache($cid_info['class'], $cached_ctrl); 1679 $this->info_read_class[$cid_info["class"]] = true; 1680 } 1681 1682 $this->info_read_cid[$a_cid] = true; 1683 } 1684 1685 /** 1686 * Save classes respective to the class id's of a node and store corresponding 1687 * class calls for future reference. 1688 * 1689 * @param string $a_node 1690 */ 1691 private function readNodeInfo($a_node) 1692 { 1693 $class_ids = explode(":", $a_node); 1694 foreach ($class_ids as $cid) { 1695 $this->readCidInfo($cid); 1696 } 1697 } 1698 1699 /** 1700 * Save class id respective to $a_class and store corresponding 1701 * class calls for future reference. 1702 * 1703 * @param object $a_class class name 1704 */ 1705 private function readClassInfo($a_class) 1706 { 1707 $a_class = strtolower($a_class); 1708 if (isset($this->info_read_class[$a_class])) { 1709 return; 1710 } 1711 1712 $cached_ctrl = ilCachedCtrl::getInstance(); 1713 $class_info = $cached_ctrl->lookupClassFile($a_class); 1714 1715 if ($class_info) { 1716 $this->updateClassCidMap($a_class, $class_info['cid']); 1717 } 1718 $this->fetchCallsOfClassFromCache($a_class, $cached_ctrl); 1719 1720 $this->info_read_class[$a_class] = true; 1721 $this->info_read_cid[$this->class_cid[$a_class]] = true; 1722 } 1723 1724 private function callOfClassNotKnown($a_class, $a_child) 1725 { 1726 return !isset($this->calls[$a_class]) 1727 || !is_array($this->calls[$a_class]) 1728 || !in_array($a_child, $this->calls[$a_class]); 1729 } 1730 1731 private function updateClassCidMap($a_class, $a_cid) 1732 { 1733 $this->cid_class[$a_cid] = $a_class; 1734 $this->class_cid[$a_class] = $a_cid; 1735 } 1736 1737 /** 1738 * Get 2nd to last class id of node 1739 */ 1740 private function getParentCidOfNode($a_node) 1741 { 1742 $class_ids = explode(":", $a_node); 1743 return $class_ids[count($class_ids) - 2]; 1744 } 1745 1746 /** 1747 * Remove the class id that comes at the beginning the sequence. 1748 */ 1749 private function removeLastCid($a_node) 1750 { 1751 $lpos = strrpos($a_node, ":"); 1752 return substr($a_node, 0, $lpos); 1753 } 1754 1755 /** 1756 * Get cid of node 1757 */ 1758 private function getCurrentCidOfNode($a_node) 1759 { 1760 $n_arr = explode(":", $a_node); 1761 return $n_arr[count($n_arr) - 1]; 1762 } 1763 1764 /** 1765 * Insert ctrl calls record 1766 * 1767 * @param 1768 * @return 1769 */ 1770 public function insertCtrlCalls($a_parent, $a_child, $a_comp_prefix) 1771 { 1772 global $DIC; 1773 1774 $ilDB = $DIC->database(); 1775 ; 1776 1777 $a_parent = strtolower($a_parent); 1778 $a_child = strtolower($a_child); 1779 $a_comp_prefix = strtolower($a_comp_prefix); 1780 1781 $set = $ilDB->query( 1782 "SELECT * FROM ctrl_calls WHERE " . 1783 " parent = " . $ilDB->quote($a_parent, "text") . " AND " . 1784 " child = " . $ilDB->quote($a_child, "text") . " AND " . 1785 " comp_prefix = " . $ilDB->quote($a_comp_prefix, "text") 1786 ); 1787 if ($rec = $ilDB->fetchAssoc($set)) { 1788 return; 1789 } 1790 $ilDB->manipulate("INSERT INTO ctrl_calls " . 1791 "(parent, child, comp_prefix) VALUES (" . 1792 $ilDB->quote($a_parent, "text") . "," . 1793 $ilDB->quote($a_child, "text") . "," . 1794 $ilDB->quote($a_comp_prefix, "text") . 1795 ")"); 1796 } 1797 1798 /** 1799 * Check if current path contains a certain gui class 1800 * 1801 * @param $gui_class 1802 * @return bool 1803 * @throws ilCtrlException 1804 */ 1805 public function checkCurrentPathForClass($gui_class) 1806 { 1807 foreach (explode(":", $this->getCmdNode()) as $cid) { 1808 if ($cid != "" && strtolower($this->getClassForCid($cid)) == strtolower($gui_class)) { 1809 return true; 1810 } 1811 } 1812 return false; 1813 } 1814 1815 /** 1816 * Get current class path as array of class file names 1817 * 1818 * @return array 1819 * @throws ilCtrlException 1820 */ 1821 public function getCurrentClassPath() : array 1822 { 1823 $path = []; 1824 foreach (explode(":", $this->getCmdNode()) as $cid) { 1825 if ($cid != "") { 1826 $path[] = $this->getClassForCid($cid); 1827 } 1828 } 1829 if ($this->getCmdNode() == "" && $_GET["baseClass"] != "") { 1830 $path[] = $_GET["baseClass"]; 1831 } 1832 return $path; 1833 } 1834} 1835