ILIAS  release_8 Revision v8.24
class.ilPageObject.php
Go to the documentation of this file.
1<?php
2
19define("IL_INSERT_BEFORE", 0);
20define("IL_INSERT_AFTER", 1);
21define("IL_INSERT_CHILD", 2);
22
23/*
24
25 - move dom related code to PageDom class/interface
26 - move ilDB dependency to ar object
27 - move internal links related code to extra class
28 - make factory available through DIC, opt allow decentralized factory parts
29 - PC types
30 -- internal links used/implemented?
31 -- styles used/implemented?
32 - application classes need
33 -- page object
34 --- page object should return php5 domdoc (getDom() vs getDomDoc()?)
35 esp. plugins should use this
36 -- remove content element hook, if content is not allowed
37 - PC types could move to components (e.g. blog, login)
38 - How to modularize xsl?
39 -- read from db?
40 -- xml entries say that xslt code is used -> read file and include in
41 main xslt file
42
43*/
44
50abstract class ilPageObject
51{
52 protected int $create_user = 0;
56 protected array $id_elements;
57 public int $old_nr;
58 protected bool $page_not_found = false;
59 protected bool $show_page_act_info = false;
61 public static array $exists = array();
62 protected ilDBInterface $db;
63 protected ilObjUser $user;
64 protected ilLanguage $lng;
65 protected ilTree $tree;
66 protected int $id;
67 public ?php4DOMDocument $dom = null;
68 public string $xml = "";
69 public string $encoding = "";
71 public string $cur_dtd = "ilias_pg_8.dtd";
72 public bool $contains_int_link = false;
73 public bool $needs_parsing = false;
74 public string $parent_type = "";
75 public int $parent_id = 0;
76 public array $update_listeners = [];
77 public int $update_listener_cnt = 0;
78 public ?object $offline_handler = null; // see LMPresentation handleCodeParagraph
79 public bool $dom_builded = false;
80 public bool $history_saved = false;
81 protected string $language = "-";
82 protected static array $activation_data = array();
83 protected bool $import_mode = false;
84 protected ilLogger $log;
85 protected ?array $page_record = array();
86 protected bool $active = false;
88 protected string $rendermd5 = "";
89 protected string $renderedcontent = "";
90 protected string $renderedtime = "";
91 protected string $lastchange = "";
92 public int $last_change_user = 0;
93 protected bool $contains_question = false;
94 protected array $hier_ids = [];
95 protected array $first_row_ids = [];
96 protected array $first_col_ids = [];
97 protected array $list_item_ids = [];
98 protected array $file_item_ids = [];
99 protected ?string $activationstart = null; // IL_CAL_DATETIME format
100 protected ?string $activationend = null; // IL_CAL_DATETIME format
101 protected \ILIAS\COPage\ReadingTime\ReadingTimeManager $reading_time_manager;
102 protected $concrete_lang = "";
103
104 final public function __construct(
105 int $a_id = 0,
106 int $a_old_nr = 0,
107 string $a_lang = "-"
108 ) {
109 global $DIC;
110 $this->obj_definition = $DIC["objDefinition"];
111 $this->db = $DIC->database();
112 $this->user = $DIC->user();
113 $this->lng = $DIC->language();
114 $this->tree = $DIC->repositoryTree();
115 $this->log = ilLoggerFactory::getLogger('copg');
116
117 $this->reading_time_manager = new ILIAS\COPage\ReadingTime\ReadingTimeManager();
118
119 $this->parent_type = $this->getParentType();
120 $this->id = $a_id;
121 $this->setLanguage($a_lang);
122
123 $this->contains_int_link = false;
124 $this->needs_parsing = false;
125 $this->update_listeners = array();
126 $this->update_listener_cnt = 0;
127 $this->dom_builded = false;
128 $this->page_not_found = false;
129 $this->old_nr = $a_old_nr;
130 $this->encoding = "UTF-8";
131 $this->id_elements =
132 array("PageContent",
133 "TableRow",
134 "TableData",
135 "ListItem",
136 "FileItem",
137 "Section",
138 "Tab",
139 "ContentPopup",
140 "GridCell"
141 );
142 $this->setActive(true);
143 $this->show_page_act_info = false;
144
145 if ($a_id != 0) {
146 $this->read();
147 }
148
149 $this->initPageConfig();
150 $this->afterConstructor();
151 }
152
153 public function afterConstructor(): void
154 {
155 }
156
157 abstract public function getParentType(): string;
158
159 final public function initPageConfig(): void
160 {
162 $this->setPageConfig($cfg);
163 }
164
169 public function setLanguage(string $a_val): void
170 {
171 $this->language = $a_val;
172 }
173
174 public function getLanguage(): string
175 {
176 return $this->language;
177 }
178
179 public function setPageConfig(ilPageConfig $a_val): void
180 {
181 $this->page_config = $a_val;
182 }
183
184 public function setConcreteLang(string $a_val)
185 {
186 $this->concrete_lang = $a_val;
187 }
188
189 public function getConcreteLang(): string
190 {
192 }
193
194 public function getPageConfig(): ilPageConfig
195 {
196 return $this->page_config;
197 }
198
199 public static function randomhash(): string
200 {
201 $random = new \ilRandom();
202 return md5($random->int(1, 9999999) + str_replace(" ", "", (string) microtime()));
203 }
204
205 public function setRenderMd5(string $a_rendermd5): void
206 {
207 $this->rendermd5 = $a_rendermd5;
208 }
209
210 public function getRenderMd5(): string
211 {
212 return $this->rendermd5;
213 }
214
215 public function setRenderedContent(string $a_renderedcontent): void
216 {
217 $this->renderedcontent = $a_renderedcontent;
218 }
219
220 public function getRenderedContent(): string
221 {
223 }
224
225 public function setRenderedTime(string $a_renderedtime): void
226 {
227 $this->renderedtime = $a_renderedtime;
228 }
229
230 public function getRenderedTime(): string
231 {
232 return $this->renderedtime;
233 }
234
235 public function setLastChange(string $a_lastchange): void
236 {
237 $this->lastchange = $a_lastchange;
238 }
239
240 public function getLastChange(): string
241 {
242 return $this->lastchange;
243 }
244
245 public function setLastChangeUser(int $a_val): void
246 {
247 $this->last_change_user = $a_val;
248 }
249
250 public function getLastChangeUser(): int
251 {
253 }
254
255 public function setShowActivationInfo(bool $a_val): void
256 {
257 $this->show_page_act_info = $a_val;
258 }
259
260 public function getShowActivationInfo(): bool
261 {
263 }
264
265 public function getCreationUserId(): int
266 {
267 return $this->create_user;
268 }
269
273 public function read(): void
274 {
275 $this->setActive(true);
276 if ($this->old_nr == 0) {
277 $query = "SELECT * FROM page_object" .
278 " WHERE page_id = " . $this->db->quote($this->id, "integer") .
279 " AND parent_type=" . $this->db->quote($this->getParentType(), "text") .
280 " AND lang = " . $this->db->quote($this->getLanguage(), "text");
281 $pg_set = $this->db->query($query);
282 if (!$this->page_record = $this->db->fetchAssoc($pg_set)) {
283 throw new ilCOPageNotFoundException("Error: Page " . $this->id . " is not in database" .
284 " (parent type " . $this->getParentType() . ", lang: " . $this->getLanguage() . ").");
285 }
286 $this->setActive($this->page_record["active"]);
287 $this->setActivationStart($this->page_record["activation_start"]);
288 $this->setActivationEnd($this->page_record["activation_end"]);
289 $this->setShowActivationInfo($this->page_record["show_activation_info"]);
290 } else {
291 $query = "SELECT * FROM page_history" .
292 " WHERE page_id = " . $this->db->quote($this->id, "integer") .
293 " AND parent_type=" . $this->db->quote($this->getParentType(), "text") .
294 " AND nr = " . $this->db->quote($this->old_nr, "integer") .
295 " AND lang = " . $this->db->quote($this->getLanguage(), "text");
296 $pg_set = $this->db->query($query);
297 $this->page_record = $this->db->fetchAssoc($pg_set);
298 }
299 if (!$this->page_record) {
300 throw new ilCOPageNotFoundException("Error: Page " . $this->id . " is not in database" .
301 " (parent type " . $this->getParentType() . ", lang: " . $this->getLanguage() . ").");
302 }
303 $this->xml = $this->page_record["content"];
304 $this->setParentId((int) $this->page_record["parent_id"]);
305 $this->last_change_user = (int) ($this->page_record["last_change_user"] ?? 0);
306 $this->create_user = (int) ($this->page_record["create_user"] ?? 0);
307 $this->setRenderedContent((string) ($this->page_record["rendered_content"] ?? ""));
308 $this->setRenderMd5((string) ($this->page_record["render_md5"] ?? ""));
309 $this->setRenderedTime((string) ($this->page_record["rendered_time"] ?? ""));
310 $this->setLastChange((string) ($this->page_record["last_change"] ?? ""));
311 }
312
317 public static function _exists(
318 string $a_parent_type,
319 int $a_id,
320 string $a_lang = "",
321 bool $a_no_cache = false
322 ): bool {
323 global $DIC;
324
325 $db = $DIC->database();
326
327 if (!$a_no_cache && isset(self::$exists[$a_parent_type . ":" . $a_id . ":" . $a_lang])) {
328 return self::$exists[$a_parent_type . ":" . $a_id . ":" . $a_lang];
329 }
330
331 $and_lang = "";
332 if ($a_lang != "") {
333 $and_lang = " AND lang = " . $db->quote($a_lang, "text");
334 }
335
336 $query = "SELECT page_id FROM page_object WHERE page_id = " . $db->quote($a_id, "integer") . " " .
337 "AND parent_type = " . $db->quote($a_parent_type, "text") . $and_lang;
338 $set = $db->query($query);
339 if ($row = $db->fetchAssoc($set)) {
340 self::$exists[$a_parent_type . ":" . $a_id . ":" . $a_lang] = true;
341 return true;
342 } else {
343 self::$exists[$a_parent_type . ":" . $a_id . ":" . $a_lang] = false;
344 return false;
345 }
346 }
347
351 public static function _existsAndNotEmpty(
352 string $a_parent_type,
353 int $a_id,
354 string $a_lang = "-"
355 ): bool {
356 return ilPageUtil::_existsAndNotEmpty($a_parent_type, $a_id, $a_lang);
357 }
358
362 public function buildDom(bool $a_force = false)
363 {
364 if ($this->dom_builded && !$a_force) {
365 return true;
366 }
367 $options = 0;
368 if ($this->getXMLContent() === "") {
369 $this->setXMLContent("<PageObject></PageObject>");
370 }
371
372 //$options = DOMXML_LOAD_VALIDATING;
373 //$options = LIBXML_DTDLOAD;
374 //$options = LIBXML_NOXMLDECL;
375 $this->dom = domxml_open_mem($this->getXMLContent(true), $options, $error);
376 $xpc = xpath_new_context($this->dom);
377 $path = "//PageObject";
378 $res = xpath_eval($xpc, $path);
379 if (count($res->nodeset) == 1) {
380 $this->node = $res->nodeset[0];
381 } else {
382 $mess = "No PageObject Node found: " . $this->getParentType() . ", " . $this->getId() . ", " . $this->getXMLContent(true);
383 if (defined('DEVMODE') && DEVMODE) {
384 throw new ilCOPageException($mess);
385 } else {
386 $this->log->error($mess);
387 }
388 }
389
390 if (empty($error)) {
391 $this->dom_builded = true;
392 return true;
393 } else {
394 return $error;
395 }
396 }
397
398 public function freeDom(): void
399 {
400 unset($this->dom);
401 }
402
406 public function getDom(): ?php4DOMDocument
407 {
408 return $this->dom;
409 }
410
414 public function getDomDoc(): DOMDocument
415 {
416 if ($this->dom instanceof php4DOMDocument) {
417 return $this->dom->myDOMDocument;
418 }
420 $dom = $this->dom;
421 return $dom;
422 }
423
424 public function setId(int $a_id): void
425 {
426 $this->id = $a_id;
427 }
428
429 public function getId(): int
430 {
431 return $this->id;
432 }
433
434 public function setParentId(int $a_id): void
435 {
436 $this->parent_id = $a_id;
437 }
438
439 public function getParentId(): int
440 {
441 return $this->parent_id;
442 }
443
447 public function addUpdateListener(
448 object $a_object,
449 string $a_method,
450 $a_parameters = ""
451 ): void {
452 $cnt = $this->update_listener_cnt;
453 $this->update_listeners[$cnt]["object"] = $a_object;
454 $this->update_listeners[$cnt]["method"] = $a_method;
455 $this->update_listeners[$cnt]["parameters"] = $a_parameters;
456 $this->update_listener_cnt++;
457 }
458
459 public function callUpdateListeners(): void
460 {
461 for ($i = 0; $i < $this->update_listener_cnt; $i++) {
462 $object = $this->update_listeners[$i]["object"];
463 $method = $this->update_listeners[$i]["method"];
464 $parameters = $this->update_listeners[$i]["parameters"];
465 $object->$method($parameters);
466 }
467 }
468
469 public function setActive(bool $a_active): void
470 {
471 $this->active = $a_active;
472 }
473
474 public function getActive(
475 bool $a_check_scheduled_activation = false
476 ): bool {
477 if ($a_check_scheduled_activation && !$this->active) {
478 $start = new ilDateTime($this->getActivationStart(), IL_CAL_DATETIME);
479 $end = new ilDateTime($this->getActivationEnd(), IL_CAL_DATETIME);
480 $now = new ilDateTime(time(), IL_CAL_UNIX);
481 if (!ilDateTime::_before($now, $start) && !ilDateTime::_after($now, $end)) {
482 return true;
483 }
484 }
485 return $this->active;
486 }
487
491 public static function preloadActivationDataByParentId(int $a_parent_id): void
492 {
493 global $DIC;
494
495 $db = $DIC->database();
496 $set = $db->query(
497 "SELECT page_id, parent_type, lang, active, activation_start, activation_end, show_activation_info FROM page_object " .
498 " WHERE parent_id = " . $db->quote($a_parent_id, "integer")
499 );
500 while ($rec = $db->fetchAssoc($set)) {
501 self::$activation_data[$rec["page_id"] . ":" . $rec["parent_type"] . ":" . $rec["lang"]] = $rec;
502 }
503 }
504
508 public static function _lookupActive(
509 int $a_id,
510 string $a_parent_type,
511 bool $a_check_scheduled_activation = false,
512 string $a_lang = "-"
513 ): bool {
514 global $DIC;
515
516 $db = $DIC->database();
517
518 // language must be set at least to "-"
519 if ($a_lang == "") {
520 $a_lang = "-";
521 }
522
523 if (isset(self::$activation_data[$a_id . ":" . $a_parent_type . ":" . $a_lang])) {
524 $rec = self::$activation_data[$a_id . ":" . $a_parent_type . ":" . $a_lang];
525 } else {
526 $set = $db->queryF(
527 "SELECT active, activation_start, activation_end FROM page_object WHERE page_id = %s" .
528 " AND parent_type = %s AND lang = %s",
529 array("integer", "text", "text"),
530 array($a_id, $a_parent_type, $a_lang)
531 );
532 $rec = $db->fetchAssoc($set);
533 if (!$rec) {
534 return true;
535 }
536 }
537
538 $rec["n"] = ilUtil::now();
539 if (!$rec["active"] && $a_check_scheduled_activation) {
540 if ($rec["n"] >= $rec["activation_start"] &&
541 $rec["n"] <= $rec["activation_end"]) {
542 return true;
543 }
544 }
545
546 return (bool) $rec["active"];
547 }
548
552 public static function _isScheduledActivation(
553 int $a_id,
554 string $a_parent_type,
555 string $a_lang = "-"
556 ): bool {
557 global $DIC;
558
559 $db = $DIC->database();
560
561 // language must be set at least to "-"
562 if ($a_lang == "") {
563 $a_lang = "-";
564 }
565
566 //echo "<br>";
567 //var_dump(self::$activation_data); exit;
568 if (isset(self::$activation_data[$a_id . ":" . $a_parent_type . ":" . $a_lang])) {
569 $rec = self::$activation_data[$a_id . ":" . $a_parent_type . ":" . $a_lang];
570 } else {
571 $set = $db->queryF(
572 "SELECT active, activation_start, activation_end FROM page_object WHERE page_id = %s" .
573 " AND parent_type = %s AND lang = %s",
574 array("integer", "text", "text"),
575 array($a_id, $a_parent_type, $a_lang)
576 );
577 $rec = $db->fetchAssoc($set);
578 }
579
580 if (!$rec["active"] && $rec["activation_start"] != "") {
581 return true;
582 }
583
584 return false;
585 }
586
590 public static function _writeActive(
591 int $a_id,
592 string $a_parent_type,
593 bool $a_active
594 ): void {
595 global $DIC;
596
597 $db = $DIC->database();
598
599 // language must be set at least to "-"
600 $a_lang = "-";
601
602 $db->manipulateF(
603 "UPDATE page_object SET active = %s, activation_start = %s, " .
604 " activation_end = %s WHERE page_id = %s" .
605 " AND parent_type = %s AND lang = %s",
606 array("int", "timestamp", "timestamp", "integer", "text", "text"),
607 array((int) $a_active, null, null, $a_id, $a_parent_type, $a_lang)
608 );
609 }
610
614 public static function _lookupActivationData(
615 int $a_id,
616 string $a_parent_type,
617 string $a_lang = "-"
618 ): array {
619 global $DIC;
620
621 $db = $DIC->database();
622
623 // language must be set at least to "-"
624 if ($a_lang == "") {
625 $a_lang = "-";
626 }
627
628 if (isset(self::$activation_data[$a_id . ":" . $a_parent_type . ":" . $a_lang])) {
629 $rec = self::$activation_data[$a_id . ":" . $a_parent_type . ":" . $a_lang];
630 } else {
631 $set = $db->queryF(
632 "SELECT active, activation_start, activation_end, show_activation_info FROM page_object WHERE page_id = %s" .
633 " AND parent_type = %s AND lang = %s",
634 array("integer", "text", "text"),
635 array($a_id, $a_parent_type, $a_lang)
636 );
637 $rec = $db->fetchAssoc($set);
638 if (!$rec) {
639 return [
640 "active" => 1,
641 "activation_start" => null,
642 "activation_end" => null,
643 "show_activation_info" => 0
644 ];
645 }
646 }
647
648 return $rec;
649 }
650
651 public static function lookupParentId(int $a_id, string $a_type): int
652 {
653 global $DIC;
654
655 $db = $DIC->database();
656
657 $res = $db->query("SELECT parent_id FROM page_object WHERE page_id = " . $db->quote($a_id, "integer") . " " .
658 "AND parent_type=" . $db->quote($a_type, "text"));
659 $rec = $db->fetchAssoc($res);
660 return (int) ($rec["parent_id"] ?? 0);
661 }
662
663 public static function _writeParentId(string $a_parent_type, int $a_pg_id, int $a_par_id): void
664 {
665 global $DIC;
666
667 $db = $DIC->database();
668 $db->manipulateF(
669 "UPDATE page_object SET parent_id = %s WHERE page_id = %s" .
670 " AND parent_type = %s",
671 array("integer", "integer", "text"),
672 array($a_par_id, $a_pg_id, $a_parent_type)
673 );
674 }
675
679 public function setActivationStart(?string $a_activationstart): void
680 {
681 if ($a_activationstart == "") {
682 $a_activationstart = null;
683 }
684 $this->activationstart = $a_activationstart;
685 }
686
687 public function getActivationStart(): ?string
688 {
689 return $this->activationstart;
690 }
691
696 public function setActivationEnd(?string $a_activationend): void
697 {
698 if ($a_activationend == "") {
699 $a_activationend = null;
700 }
701 $this->activationend = $a_activationend;
702 }
703
704 public function getActivationEnd(): ?string
705 {
706 return $this->activationend;
707 }
708
712 public function getContentObject(
713 string $a_hier_id,
714 string $a_pc_id = ""
715 ): ?ilPageContent {
716 $child_node = null;
717 $cont_node = $this->getContentNode($a_hier_id, $a_pc_id);
718 if (!is_object($cont_node)) {
719 return null;
720 }
721 $node_name = $cont_node->node_name();
722 if (in_array($node_name, ["PageObject", "TableRow"])) {
723 return null;
724 }
725 if ($node_name == "PageContent") {
726 $child_node = $cont_node->first_child();
727 $node_name = $child_node->node_name();
728 }
729
730 // table extra handling (@todo: get rid of it)
731 if ($node_name == "Table") {
732 if ($child_node->get_attribute("DataTable") == "y") {
733 $tab = new ilPCDataTable($this);
734 } else {
735 $tab = new ilPCTable($this);
736 }
737 $tab->setNode($cont_node);
738 $tab->setHierId($a_hier_id);
739 $tab->setPcId($a_pc_id);
740 return $tab;
741 }
742
743 // media extra handling (@todo: get rid of it)
744 if ($node_name == "MediaObject") {
745 $mal_node = $child_node->first_child();
746 //echo "ilPageObject::getContentObject:nodename:".$mal_node->node_name().":<br>";
747 $id_arr = explode("_", $mal_node->get_attribute("OriginId"));
748 $mob_id = $id_arr[count($id_arr) - 1];
749
750 // see also #32331
751 if (ilObject::_lookupType($mob_id) !== "mob") {
752 $mob_id = 0;
753 }
754
755 //$mob = new ilObjMediaObject($mob_id);
756 $mob = new ilPCMediaObject($this);
757 $mob->readMediaObject($mob_id);
758
759 //$mob->setDom($this->dom);
760 $mob->setNode($cont_node);
761 $mob->setHierId($a_hier_id);
762 $mob->setPcId($a_pc_id);
763 return $mob;
764 }
765
766 //
767 // generic procedure
768 //
769
770 $pc_def = ilCOPagePCDef::getPCDefinitionByName($node_name);
771
772 // check if pc definition has been found
773 if (!is_array($pc_def)) {
774 throw new ilCOPageUnknownPCTypeException('Unknown PC Name "' . $node_name . '".');
775 }
776 $pc_class = "ilPC" . $pc_def["name"];
777 $pc_path = "./" . $pc_def["component"] . "/" . $pc_def["directory"] . "/class." . $pc_class . ".php";
778 require_once($pc_path);
779 $pc = new $pc_class($this);
780 $pc->setNode($cont_node);
781 $pc->setHierId($a_hier_id);
782 $pc->setPcId($a_pc_id);
783 return $pc;
784 }
785
789 public function getContentObjectForPcId(string $pcid): ?ilPageContent
790 {
791 $hier_ids = $this->getHierIdsForPCIds([$pcid]);
792 return $this->getContentObject($hier_ids[$pcid], $pcid);
793 }
794
798 public function getParentContentObjectForPcId(string $pcid): ?ilPageContent
799 {
800 $content_object = $this->getContentObjectForPcId($pcid);
801 $node = $content_object->getNode();
802 $node = $node->parent_node();
803 while ($node) {
804 if ($node->node_name() == "PageContent") {
805 $pcid = $node->get_attribute("PCID");
806 if ($pcid != "") {
807 return $this->getContentObjectForPcId($pcid);
808 }
809 }
810 $node = $node->parent_node();
811 }
812 return null;
813 }
814
815 public function getContentNode(string $a_hier_id, string $a_pc_id = ""): ?php4DOMElement
816 {
817 $xpc = xpath_new_context($this->dom);
818 if ($a_hier_id == "pg") {
819 return $this->node;
820 } else {
821 // get per pc id
822 if ($a_pc_id != "") {
823 $path = "//*[@PCID = '$a_pc_id']";
824 $res = xpath_eval($xpc, $path);
825 if (count($res->nodeset) == 1) {
826 $cont_node = $res->nodeset[0];
827 return $cont_node;
828 }
829 }
830
831 // fall back to hier id
832 $path = "//*[@HierId = '$a_hier_id']";
833 $res = xpath_eval($xpc, $path);
834 if (count($res->nodeset) == 1) {
835 $cont_node = $res->nodeset[0];
836 return $cont_node;
837 }
838 }
839 return null;
840 }
841
842
847 public function checkForTag(
848 string $a_content_tag,
849 string $a_hier_id,
850 string $a_pc_id = ""
851 ): bool {
852 $xpc = xpath_new_context($this->dom);
853 // get per pc id
854 if ($a_pc_id != "") {
855 $path = "//*[@PCID = '$a_pc_id']//" . $a_content_tag;
856 $res = xpath_eval($xpc, $path);
857 if (count($res->nodeset) > 0) {
858 return true;
859 }
860 }
861
862 // fall back to hier id
863 $path = "//*[@HierId = '$a_hier_id']//" . $a_content_tag;
864 $res = xpath_eval($xpc, $path);
865 if (count($res->nodeset) > 0) {
866 return true;
867 }
868 return false;
869 }
870
871 public function getNode(): php4DOMElement
872 {
873 return $this->node;
874 }
875
882 public function setXMLContent(string $a_xml, string $a_encoding = "UTF-8"): void
883 {
884 $this->encoding = $a_encoding;
885 $this->xml = $a_xml;
886 }
887
892 public function appendXMLContent(string $a_xml): void
893 {
894 $this->xml .= $a_xml;
895 }
896
900 public function getXMLContent(bool $a_incl_head = false): string
901 {
902 // build full http path for XML DOCTYPE header.
903 // Under windows a relative path doesn't work :-(
904 if ($a_incl_head) {
905 //echo "+".$this->encoding."+";
906 $enc_str = (!empty($this->encoding))
907 ? "encoding=\"" . $this->encoding . "\""
908 : "";
909 return "<?xml version=\"1.0\" $enc_str ?>" .
910 "<!DOCTYPE PageObject SYSTEM \"" . ILIAS_ABSOLUTE_PATH . "/xml/" . $this->cur_dtd . "\">" .
912 } else {
913 return $this->xml;
914 }
915 }
916
922 public function copyXmlContent(
923 bool $a_clone_mobs = false,
924 int $a_new_parent_id = 0,
925 int $obj_copy_id = 0,
926 bool $self_ass = true
927 ): string {
928 $xml = $this->getXMLContent();
929 $temp_dom = domxml_open_mem(
930 '<?xml version="1.0" encoding="UTF-8"?>' . $xml,
932 $error
933 );
934 if (empty($error)) {
935 $this->handleCopiedContent($temp_dom, $self_ass, $a_clone_mobs, $a_new_parent_id, $obj_copy_id);
936 }
937 $xml = $temp_dom->dump_mem(0, $this->encoding);
938 $xml = preg_replace('/<\?xml[^>]*>/i', "", $xml);
939 $xml = preg_replace('/<!DOCTYPE[^>]*>/i', "", $xml);
940
941 return $xml;
942 }
943
944 // @todo 1: begin: generalize, remove concrete dependencies
945
955 public function handleCopiedContent(
956 php4DOMDocument $a_dom,
957 bool $a_self_ass = true,
958 bool $a_clone_mobs = false,
959 int $new_parent_id = 0,
960 int $obj_copy_id = 0
961 ): void {
962 $defs = ilCOPagePCDef::getPCDefinitions();
963
964 // handle question elements
965 if ($a_self_ass) {
966 $this->newQuestionCopies($a_dom);
967 } else {
968 $this->removeQuestions($a_dom);
969 }
970
971 // handle interactive images
972 $this->newIIMCopies($a_dom);
973
974 // handle media objects
975 if ($a_clone_mobs) {
976 $this->newMobCopies($a_dom);
977 }
978
979 // @todo 1: move all functions from above to the new domdoc
980 $dom = $a_dom;
981 if ($a_dom instanceof php4DOMDocument) {
982 $dom = $a_dom->myDOMDocument;
983 }
984 foreach ($defs as $def) {
985 //ilCOPagePCDef::requirePCClassByName($def["name"]);
986 $cl = $def["pc_class"];
987 if ($cl == 'ilPCPlugged') {
988 // the page object is provided for ilPageComponentPlugin
989 ilPCPlugged::handleCopiedPluggedContent($this, $dom);
990 } else {
991 $cl::handleCopiedContent($dom, $a_self_ass, $a_clone_mobs, $new_parent_id, $obj_copy_id);
992 }
993 }
994 }
995
1002 public function handleDeleteContent($a_node = null, $move_operation = false): void
1003 {
1004 if (!isset($a_node)) {
1005 $xpc = xpath_new_context($this->dom);
1006 $path = "//PageContent";
1007 $res = xpath_eval($xpc, $path);
1008 $nodes = $res->nodeset;
1009 } else {
1010 $nodes = array($a_node);
1011 }
1012
1013 foreach ($nodes as $node) {
1014 if ($node instanceof php4DOMNode) {
1015 $node = $node->myDOMNode;
1016 }
1017
1019 if ($node->firstChild->nodeName == 'Plugged') {
1020 ilPCPlugged::handleDeletedPluggedNode($this, $node->firstChild, $move_operation);
1021 }
1022 }
1023 }
1024
1029 public function newIIMCopies(php4DOMDocument $temp_dom): void
1030 {
1031 // Get question IDs
1032 $path = "//InteractiveImage/MediaAlias";
1033 $xpc = xpath_new_context($temp_dom);
1034 $res = xpath_eval($xpc, $path);
1035
1036 $q_ids = array();
1037 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
1038 $or_id = $res->nodeset[$i]->get_attribute("OriginId");
1039
1040 $inst_id = ilInternalLink::_extractInstOfTarget($or_id);
1041 $mob_id = ilInternalLink::_extractObjIdOfTarget($or_id);
1042
1043 if (!($inst_id > 0)) {
1044 if ($mob_id > 0) {
1045 $media_object = new ilObjMediaObject($mob_id);
1046
1047 // now copy this question and change reference to
1048 // new question id
1049 $new_mob = $media_object->duplicate();
1050
1051 $res->nodeset[$i]->set_attribute("OriginId", "il__mob_" . $new_mob->getId());
1052 }
1053 }
1054 }
1055 }
1056
1060 public function newMobCopies(php4DOMDocument $temp_dom): void
1061 {
1062 // Get question IDs
1063 $path = "//MediaObject/MediaAlias";
1064 $xpc = xpath_new_context($temp_dom);
1065 $res = xpath_eval($xpc, $path);
1066
1067 $q_ids = array();
1068 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
1069 $or_id = $res->nodeset[$i]->get_attribute("OriginId");
1070
1071 $inst_id = ilInternalLink::_extractInstOfTarget($or_id);
1072 $mob_id = ilInternalLink::_extractObjIdOfTarget($or_id);
1073
1074 if (!($inst_id > 0)) {
1075 if ($mob_id > 0) {
1076 $media_object = new ilObjMediaObject($mob_id);
1077
1078 // now copy this question and change reference to
1079 // new question id
1080 $new_mob = $media_object->duplicate();
1081
1082 $res->nodeset[$i]->set_attribute("OriginId", "il__mob_" . $new_mob->getId());
1083 }
1084 }
1085 }
1086 }
1087
1092 public function newQuestionCopies(php4DOMDocument $temp_dom): void
1093 {
1094 // Get question IDs
1095 $path = "//Question";
1096 $xpc = xpath_new_context($temp_dom);
1097 $res = xpath_eval($xpc, $path);
1098
1099 $q_ids = array();
1100 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
1101 $qref = $res->nodeset[$i]->get_attribute("QRef");
1102
1103 $inst_id = ilInternalLink::_extractInstOfTarget($qref);
1105
1106 if (!($inst_id > 0)) {
1107 if ($q_id > 0) {
1108 $question = null;
1109 try {
1110 $question = assQuestion::_instantiateQuestion($q_id);
1111 } catch (Exception $e) {
1112 }
1113 // check due to #16557
1114 if (is_object($question) && $question->isComplete()) {
1115 // check if page for question exists
1116 // due to a bug in early 4.2.x version this is possible
1117 if (!ilPageObject::_exists("qpl", $q_id)) {
1118 $question->createPageObject();
1119 }
1120
1121 // now copy this question and change reference to
1122 // new question id
1123 $duplicate_id = $question->duplicate(false);
1124 $res->nodeset[$i]->set_attribute("QRef", "il__qst_" . $duplicate_id);
1125 }
1126 }
1127 }
1128 }
1129 }
1130
1134 public function removeQuestions(php4DOMDocument $temp_dom): void
1135 {
1136 // Get question IDs
1137 $path = "//Question";
1138 $xpc = xpath_new_context($temp_dom);
1139 $res = xpath_eval($xpc, $path);
1140 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
1141 $parent_node = $res->nodeset[$i]->parent_node();
1142 $parent_node->unlink_node($parent_node);
1143 }
1144 }
1145
1146 // @todo: end
1147
1148 public function countPageContents(): int
1149 {
1150 // Get question IDs
1151 $this->buildDom();
1152 $path = "//PageContent";
1153 $xpc = xpath_new_context($this->dom);
1154 $res = xpath_eval($xpc, $path);
1155 return count($res->nodeset);
1156 }
1157
1162 public function getXMLFromDom(
1163 bool $a_incl_head = false,
1164 bool $a_append_mobs = false,
1165 bool $a_append_bib = false,
1166 string $a_append_str = "",
1167 bool $a_omit_pageobject_tag = false,
1168 int $style_id = 0
1169 ): string {
1170 if ($a_incl_head) {
1171 //echo "\n<br>#".$this->encoding."#";
1172 return $this->dom->dump_mem(0, $this->encoding);
1173 } else {
1174 // append multimedia object elements
1175 if ($a_append_mobs || $a_append_bib) {
1176 $mobs = "";
1177 $bibs = "";
1178 if ($a_append_mobs) {
1179 $mobs = $this->getMultimediaXML();
1180 }
1181 if ($a_append_bib) {
1182 // deprecated
1183 // $bibs = $this->getBibliographyXML();
1184 }
1185 $trans = $this->getLanguageVariablesXML($style_id);
1186 //echo htmlentities($this->dom->dump_node($this->node)); exit;
1187 return "<dummy>" . $this->dom->dump_node($this->node) . $mobs . $bibs . $trans . $a_append_str . "</dummy>";
1188 } else {
1189 if (is_object($this->dom)) {
1190 if ($a_omit_pageobject_tag) {
1191 $xml = "";
1192 $childs = $this->node->child_nodes();
1193 for ($i = 0, $iMax = count($childs); $i < $iMax; $i++) {
1194 $xml .= $this->dom->dump_node($childs[$i]);
1195 }
1196 } else {
1197 $xml = $this->dom->dump_mem(0, $this->encoding);
1198 $xml = preg_replace('/<\?xml[^>]*>/i', "", $xml);
1199 $xml = preg_replace('/<!DOCTYPE[^>]*>/i', "", $xml);
1200
1201 // don't use dump_node. This gives always entities.
1202 //return $this->dom->dump_node($this->node);
1203 }
1204 return $xml;
1205 } else {
1206 return "";
1207 }
1208 }
1209 }
1210 }
1211
1215 public function getLanguageVariablesXML(int $style_id = 0): string
1216 {
1217 $xml = "<LVs>";
1218 $lang_vars = array(
1219 "ed_paste_clip",
1220 "ed_edit",
1221 "ed_edit_prop",
1222 "ed_delete",
1223 "ed_moveafter",
1224 "ed_movebefore",
1225 "ed_go",
1226 "ed_class",
1227 "ed_width",
1228 "ed_align_left",
1229 "ed_align_right",
1230 "ed_align_center",
1231 "ed_align_left_float",
1232 "ed_align_right_float",
1233 "ed_delete_item",
1234 "ed_new_item_before",
1235 "ed_new_item_after",
1236 "ed_copy_clip",
1237 "please_select",
1238 "ed_split_page",
1239 "ed_item_up",
1240 "ed_item_down",
1241 "ed_split_page_next",
1242 "ed_enable",
1243 "de_activate",
1244 "ed_paste",
1245 "ed_edit_multiple",
1246 "ed_cut",
1247 "ed_copy",
1248 "ed_insert_templ",
1249 "ed_click_to_add_pg",
1250 "download"
1251 );
1252
1253 // collect lang vars from pc elements
1255 foreach ($defs as $def) {
1256 $lang_vars[] = "pc_" . $def["pc_type"];
1257 $lang_vars[] = "ed_insert_" . $def["pc_type"];
1258
1259 //ilCOPagePCDef::requirePCClassByName($def["name"]);
1260 $cl = $def["pc_class"];
1261 $lvs = call_user_func($def["pc_class"] . '::getLangVars');
1262 foreach ($lvs as $lv) {
1263 $lang_vars[] = $lv;
1264 }
1265 }
1266
1267 // workaround for #30561, should go to characteristic manager
1268 $dummy_pc = new ilPCSectionGUI($this, null, "");
1269 $dummy_pc->setStyleId($style_id);
1270 foreach (["section", "table", "flist_li", "list_u", "list_o",
1271 "table", "table_cell"] as $type) {
1272 $dummy_pc->getCharacteristicsOfCurrentStyle([$type]);
1273 foreach ($dummy_pc->getCharacteristics() as $char => $txt) {
1274 $xml .= "<LV name=\"char_" . $type . "_" . $char . "\" value=\"" . $txt . "\"/>";
1275 }
1276 }
1277 $type = "media_cont";
1278 $dummy_pc = new ilPCMediaObjectGUI($this, null, "");
1279 $dummy_pc->setStyleId($style_id);
1280 $dummy_pc->getCharacteristicsOfCurrentStyle([$type]);
1281 foreach ($dummy_pc->getCharacteristics() as $char => $txt) {
1282 $xml .= "<LV name=\"char_" . $type . "_" . $char . "\" value=\"" . $txt . "\"/>";
1283 }
1284 foreach (["text_block", "heading1", "heading2", "heading3"] as $type) {
1285 $dummy_pc = new ilPCParagraphGUI($this, null, "");
1286 $dummy_pc->setStyleId($style_id);
1287 $dummy_pc->getCharacteristicsOfCurrentStyle([$type]);
1288 foreach ($dummy_pc->getCharacteristics() as $char => $txt) {
1289 $xml .= "<LV name=\"char_" . $type . "_" . $char . "\" value=\"" . $txt . "\"/>";
1290 }
1291 }
1292 foreach ($lang_vars as $lang_var) {
1293 $xml .= $this->getLangVarXML($lang_var);
1294 }
1295 $xml .= "</LVs>";
1296 return $xml;
1297 }
1298
1299 protected function getLangVarXML(string $var): string
1300 {
1301 $val = $this->lng->txt("cont_" . $var);
1302 $val = str_replace('"', "&quot;", $val);
1303 return "<LV name=\"$var\" value=\"" . $val . "\"/>";
1304 }
1305
1306 // @todo begin: move this to paragraph class
1307
1308 public function getFirstParagraphText(): string
1309 {
1310 if ($this->dom) {
1311 $xpc = xpath_new_context($this->dom);
1312 $path = "//Paragraph[1]";
1313 $res = xpath_eval($xpc, $path);
1314 if (count($res->nodeset) > 0) {
1315 $cont_node = $res->nodeset[0]->parent_node();
1316 $par = new ilPCParagraph($this);
1317 $par->setNode($cont_node);
1318 $text = $par->getText();
1319 return $text;
1320 }
1321 }
1322 return "";
1323 }
1324
1325 public function getParagraphForPCID(string $pcid): ?ilPCParagraph
1326 {
1327 if ($this->dom) {
1328 $xpc = xpath_new_context($this->dom);
1329 $path = "//PageContent[@PCID='" . $pcid . "']/Paragraph[1]";
1330 $res = xpath_eval($xpc, $path);
1331 if (count($res->nodeset) > 0) {
1332 $cont_node = $res->nodeset[0]->parent_node();
1333 $par = new ilPCParagraph($this);
1334 $par->setNode($cont_node);
1335 return $par;
1336 }
1337 }
1338 return null;
1339 }
1340
1344 public function setParagraphContent(string $a_hier_id, string $a_content): void
1345 {
1346 $node = $this->getContentNode($a_hier_id);
1347 if (is_object($node)) {
1348 $node->set_content($a_content);
1349 }
1350 }
1351
1352 // @todo end
1353
1360 // @todo: can we do this better
1361 public function setContainsIntLink(bool $a_contains_link): void
1362 {
1363 $this->contains_int_link = $a_contains_link;
1364 }
1365
1370 // @todo: can we do this better
1371 public function containsIntLink(): bool
1372 {
1373 return $this->contains_int_link;
1374 }
1375
1376 public function setImportMode(bool $a_val): void
1377 {
1378 $this->import_mode = $a_val;
1379 }
1380
1381 public function getImportMode(): bool
1382 {
1383 return $this->import_mode;
1384 }
1385
1386 public function needsImportParsing(?bool $a_parse = null): bool
1387 {
1388 if ($a_parse === true) {
1389 $this->needs_parsing = true;
1390 }
1391 if ($a_parse === false) {
1392 $this->needs_parsing = false;
1393 }
1394 return $this->needs_parsing;
1395 }
1396
1397 // @todo: can we do this better
1398 public function setContainsQuestion(bool $a_val): void
1399 {
1400 $this->contains_question = $a_val;
1401 }
1402
1403 public function getContainsQuestion(): bool
1404 {
1405 return $this->contains_question;
1406 }
1407
1408
1413 // @todo: move to media class
1414 public function collectMediaObjects(bool $a_inline_only = true): array
1415 {
1416 //echo htmlentities($this->getXMLFromDom());
1417 // determine all media aliases of the page
1418 $xpc = xpath_new_context($this->dom);
1419 $path = "//MediaObject/MediaAlias";
1420 $res = xpath_eval($xpc, $path);
1421 $mob_ids = array();
1422 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
1423 $id_arr = explode("_", $res->nodeset[$i]->get_attribute("OriginId"));
1424 $mob_id = $id_arr[count($id_arr) - 1];
1425 $mob_ids[$mob_id] = $mob_id;
1426 }
1427
1428 // determine all media aliases of interactive images
1429 $xpc = xpath_new_context($this->dom);
1430 $path = "//InteractiveImage/MediaAlias";
1431 $res = xpath_eval($xpc, $path);
1432 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
1433 $id_arr = explode("_", $res->nodeset[$i]->get_attribute("OriginId"));
1434 $mob_id = $id_arr[count($id_arr) - 1];
1435 $mob_ids[$mob_id] = $mob_id;
1436 }
1437
1438 // determine all inline internal media links
1439 $xpc = xpath_new_context($this->dom);
1440 $path = "//IntLink[@Type = 'MediaObject']";
1441 $res = xpath_eval($xpc, $path);
1442
1443 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
1444 if (($res->nodeset[$i]->get_attribute("TargetFrame") == "") ||
1445 (!$a_inline_only)) {
1446 $target = $res->nodeset[$i]->get_attribute("Target");
1447 $id_arr = explode("_", $target);
1448 if (($id_arr[1] == IL_INST_ID) ||
1449 (substr($target, 0, 4) == "il__")) {
1450 $mob_id = $id_arr[count($id_arr) - 1];
1451 if (ilObject::_exists($mob_id)) {
1452 $mob_ids[$mob_id] = $mob_id;
1453 }
1454 }
1455 }
1456 }
1457
1458 return $mob_ids;
1459 }
1460
1461
1465 // @todo: can we do this better?
1466 public function getInternalLinks(bool $a_cnt_multiple = false): array
1467 {
1468 // get all internal links of the page
1469 $xpc = xpath_new_context($this->dom);
1470 $path = "//IntLink";
1471 $res = xpath_eval($xpc, $path);
1472
1473 $links = array();
1474 $cnt_multiple = 1;
1475 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
1476 $add = "";
1477 if ($a_cnt_multiple) {
1478 $add = ":" . $cnt_multiple;
1479 }
1480 $target = $res->nodeset[$i]->get_attribute("Target");
1481 $type = $res->nodeset[$i]->get_attribute("Type");
1482 $targetframe = $res->nodeset[$i]->get_attribute("TargetFrame");
1483 $anchor = $res->nodeset[$i]->get_attribute("Anchor");
1484 $links[$target . ":" . $type . ":" . $targetframe . ":" . $anchor . $add] =
1485 array("Target" => $target,
1486 "Type" => $type,
1487 "TargetFrame" => $targetframe,
1488 "Anchor" => $anchor
1489 );
1490
1491 // get links (image map areas) for inline media objects
1492 if ($type == "MediaObject" && $targetframe == "") {
1493 if (substr($target, 0, 4) == "il__") {
1494 $id_arr = explode("_", $target);
1495 $id = $id_arr[count($id_arr) - 1];
1496
1498 foreach ($med_links as $key => $med_link) {
1499 $links[$key] = $med_link;
1500 }
1501 }
1502 }
1503 //echo "<br>-:".$target.":".$type.":".$targetframe.":-";
1504 $cnt_multiple++;
1505 }
1506 unset($xpc);
1507
1508 // get all media aliases
1509 $xpc = xpath_new_context($this->dom);
1510 $path = "//MediaAlias";
1511 $res = xpath_eval($xpc, $path);
1512
1513 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
1514 $oid = $res->nodeset[$i]->get_attribute("OriginId");
1515 if (substr($oid, 0, 4) == "il__") {
1516 $id_arr = explode("_", $oid);
1517 $id = $id_arr[count($id_arr) - 1];
1518
1520 foreach ($med_links as $key => $med_link) {
1521 $links[$key] = $med_link;
1522 }
1523 }
1524 }
1525 unset($xpc);
1526
1527 return $links;
1528 }
1529
1534 // @todo: move to media class
1535 public function getMultimediaXML(): string
1536 {
1537 $mob_ids = $this->collectMediaObjects();
1538
1539 // get xml of corresponding media objects
1540 $mobs_xml = "";
1541 foreach ($mob_ids as $mob_id => $dummy) {
1542 if (ilObject::_lookupType($mob_id) == "mob") {
1543 $mob_obj = new ilObjMediaObject($mob_id);
1544 $mobs_xml .= $mob_obj->getXML(IL_MODE_OUTPUT, $a_inst = 0, true);
1545 }
1546 }
1547 return $mobs_xml;
1548 }
1549
1553 // @todo: move to media class
1554 public function getMediaAliasElement(int $a_mob_id, int $a_nr = 1): string
1555 {
1556 $xpc = xpath_new_context($this->dom);
1557 $path = "//MediaObject/MediaAlias[@OriginId='il__mob_$a_mob_id']";
1558 $res = xpath_eval($xpc, $path);
1559 $mal_node = $res->nodeset[$a_nr - 1];
1560 $mob_node = $mal_node->parent_node();
1561
1562 return $this->dom->dump_node($mob_node);
1563 }
1564
1568 public function validateDom(bool $throw = false): ?array
1569 {
1570 $this->stripHierIDs();
1571
1572 // possible fix for #14820
1573 //libxml_disable_entity_loader(false);
1574
1575 $error = null;
1576 $this->dom->validate($error, $throw);
1577 return $error;
1578 }
1579
1592 public function addHierIDs(): void
1593 {
1594 $this->hier_ids = array();
1595 $this->first_row_ids = array();
1596 $this->first_col_ids = array();
1597 $this->list_item_ids = array();
1598 $this->file_item_ids = array();
1599
1600 // set hierarchical ids for Paragraphs, Tables, TableRows and TableData elements
1601 $xpc = xpath_new_context($this->dom);
1602 //$path = "//Paragraph | //Table | //TableRow | //TableData";
1603
1604 $sep = $path = "";
1605 foreach ($this->id_elements as $el) {
1606 $path .= $sep . "//" . $el;
1607 $sep = " | ";
1608 }
1609
1610 $res = xpath_eval($xpc, $path);
1611 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
1612 $cnode = $res->nodeset[$i];
1613 $ctag = $cnode->node_name();
1614
1615 // get hierarchical id of previous sibling
1616 $sib_hier_id = "";
1617 while ($cnode = $cnode->previous_sibling()) {
1618 if (($cnode->node_type() == XML_ELEMENT_NODE)
1619 && $cnode->has_attribute("HierId")) {
1620 $sib_hier_id = $cnode->get_attribute("HierId");
1621 //$sib_hier_id = $id_attr->value();
1622 break;
1623 }
1624 }
1625
1626 if ($sib_hier_id != "") { // set id to sibling id "+ 1"
1627 $node_hier_id = ilPageContent::incEdId($sib_hier_id);
1628 $res->nodeset[$i]->set_attribute("HierId", $node_hier_id);
1629 $this->hier_ids[] = $node_hier_id;
1630 if ($ctag == "TableData") {
1631 if (substr($node_hier_id, strlen($node_hier_id) - 2) == "_1") {
1632 $this->first_row_ids[] = $node_hier_id;
1633 }
1634 }
1635 if ($ctag == "ListItem") {
1636 $this->list_item_ids[] = $node_hier_id;
1637 }
1638 if ($ctag == "FileItem") {
1639 $this->file_item_ids[] = $node_hier_id;
1640 }
1641 } else { // no sibling -> node is first child
1642 // get hierarchical id of next parent
1643 $cnode = $res->nodeset[$i];
1644 $par_hier_id = "";
1645 while ($cnode = $cnode->parent_node()) {
1646 if (($cnode->node_type() == XML_ELEMENT_NODE)
1647 && $cnode->has_attribute("HierId")) {
1648 $par_hier_id = $cnode->get_attribute("HierId");
1649 //$par_hier_id = $id_attr->value();
1650 break;
1651 }
1652 }
1653 //echo "<br>par:".$par_hier_id." ($ctag)";
1654 if (($par_hier_id != "") && ($par_hier_id != "pg")) { // set id to parent_id."_1"
1655 $node_hier_id = $par_hier_id . "_1";
1656 $res->nodeset[$i]->set_attribute("HierId", $node_hier_id);
1657 $this->hier_ids[] = $node_hier_id;
1658 if ($ctag == "TableData") {
1659 $this->first_col_ids[] = $node_hier_id;
1660 if (substr($par_hier_id, strlen($par_hier_id) - 2) == "_1") {
1661 $this->first_row_ids[] = $node_hier_id;
1662 }
1663 }
1664 if ($ctag == "ListItem") {
1665 $this->list_item_ids[] = $node_hier_id;
1666 }
1667 if ($ctag == "FileItem") {
1668 $this->file_item_ids[] = $node_hier_id;
1669 }
1670 } else { // no sibling, no parent -> first node
1671 $node_hier_id = "1";
1672 $res->nodeset[$i]->set_attribute("HierId", $node_hier_id);
1673 $this->hier_ids[] = $node_hier_id;
1674 }
1675 }
1676 }
1677
1678 // set special hierarchical id "pg" for pageobject
1679 $xpc = xpath_new_context($this->dom);
1680 $path = "//PageObject";
1681 $res = xpath_eval($xpc, $path);
1682 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) { // should only be 1
1683 $res->nodeset[$i]->set_attribute("HierId", "pg");
1684 $this->hier_ids[] = "pg";
1685 }
1686 unset($xpc);
1687 }
1688
1692 public function getHierIds(): array
1693 {
1694 return $this->hier_ids;
1695 }
1696
1700 // @todo: move to table classes
1701 public function getFirstRowIds(): array
1702 {
1703 return $this->first_row_ids;
1704 }
1705
1709 // @todo: move to table classes
1710 public function getFirstColumnIds(): array
1711 {
1712 return $this->first_col_ids;
1713 }
1714
1718 // @todo: move to list class
1719 public function getListItemIds(): array
1720 {
1721 return $this->list_item_ids;
1722 }
1723
1727 // @todo: move to file item class
1728 public function getFileItemIds(): array
1729 {
1730 return $this->file_item_ids;
1731 }
1732
1736 public function stripHierIDs(): void
1737 {
1738 if (is_object($this->dom)) {
1739 $xpc = xpath_new_context($this->dom);
1740 $path = "//*[@HierId]";
1741 $res = xpath_eval($xpc, $path);
1742 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) { // should only be 1
1743 if ($res->nodeset[$i]->has_attribute("HierId")) {
1744 $res->nodeset[$i]->remove_attribute("HierId");
1745 }
1746 }
1747 unset($xpc);
1748 }
1749 }
1750
1754 public function getHierIdsForPCIds(array $a_pc_ids): array
1755 {
1756 if (!is_array($a_pc_ids) || count($a_pc_ids) == 0) {
1757 return array();
1758 }
1759 $ret = array();
1760
1761 if (is_object($this->dom)) {
1762 $xpc = xpath_new_context($this->dom);
1763 $path = "//*[@PCID]";
1764 $res = xpath_eval($xpc, $path);
1765 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) { // should only be 1
1766 $pc_id = $res->nodeset[$i]->get_attribute("PCID");
1767 if (in_array($pc_id, $a_pc_ids)) {
1768 $ret[$pc_id] = $res->nodeset[$i]->get_attribute("HierId");
1769 }
1770 }
1771 unset($xpc);
1772 }
1773 //var_dump($ret);
1774 return $ret;
1775 }
1776
1777 public function getHierIdForPcId(string $pcid): string
1778 {
1779 $hier_ids = $this->getHierIdsForPCIds([$pcid]);
1780 return $hier_ids[$pcid] ?? "";
1781 }
1782
1786 public function getPCIdsForHierIds(array $hier_ids): array
1787 {
1788 if (!is_array($hier_ids) || count($hier_ids) == 0) {
1789 return [];
1790 }
1791 $ret = [];
1792 $this->addHierIDs();
1793 if (is_object($this->dom)) {
1794 $xpc = xpath_new_context($this->dom);
1795 $path = "//*[@HierId]";
1796 $res = xpath_eval($xpc, $path);
1797 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) { // should only be 1
1798 $hier_id = $res->nodeset[$i]->get_attribute("HierId");
1799 if (in_array($hier_id, $hier_ids)) {
1800 $ret[$hier_id] = $res->nodeset[$i]->get_attribute("PCID");
1801 }
1802 }
1803 unset($xpc);
1804 }
1805 return $ret;
1806 }
1807
1808 public function getPCIdForHierId(string $hier_id): string
1809 {
1810 $hier_ids = $this->getPCIdsForHierIds([$hier_id]);
1811 return ($hier_ids[$hier_id] ?? "");
1812 }
1813
1818 public function addFileSizes(): void
1819 {
1820 $xpc = xpath_new_context($this->dom);
1821 $path = "//FileItem";
1822 $res = xpath_eval($xpc, $path);
1823 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
1824 $cnode = $res->nodeset[$i];
1825 $size_node = $this->dom->create_element("Size");
1826 $size_node = $cnode->append_child($size_node);
1827
1828 $childs = $cnode->child_nodes();
1829 $size = "";
1830 for ($j = 0, $jMax = count($childs); $j < $jMax; $j++) {
1831 if ($childs[$j]->node_name() == "Identifier") {
1832 if ($childs[$j]->has_attribute("Entry")) {
1833 $entry = $childs[$j]->get_attribute("Entry");
1834 $entry_arr = explode("_", $entry);
1835 $id = $entry_arr[count($entry_arr) - 1];
1836 $info_repo = new ilObjFileInfoRepository();
1837 $info = $info_repo->getByObjectId((int) $id);
1838 $size = $info->getFileSize()->inBytes();
1839
1840 //$size = ilObjFileAccess::_lookupFileSize($id, false);
1841 }
1842 }
1843 }
1844 $size_node->set_content($size);
1845 }
1846
1847 unset($xpc);
1848 }
1849
1854 public function resolveIntLinks(array $a_link_map = null): bool
1855 {
1856 $changed = false;
1857
1858 $this->log->debug("start");
1859
1860 // resolve normal internal links
1861 $xpc = xpath_new_context($this->dom);
1862 $path = "//IntLink";
1863 $res = xpath_eval($xpc, $path);
1864 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
1865 $target = $res->nodeset[$i]->get_attribute("Target");
1866 $type = $res->nodeset[$i]->get_attribute("Type");
1867
1868 if ($a_link_map == null) {
1869 $new_target = ilInternalLink::_getIdForImportId($type, $target);
1870 $this->log->debug("no map, type: " . $type . ", target: " . $target . ", new target: " . $new_target);
1871 } else {
1872 $nt = explode("_", $a_link_map[$target]);
1873 $new_target = false;
1874 if ($nt[1] == IL_INST_ID) {
1875 $new_target = "il__" . $nt[2] . "_" . $nt[3];
1876 }
1877 $this->log->debug("map, type: " . $type . ", target: " . $target . ", new target: " . $new_target);
1878 }
1879 if ($new_target !== false && !is_null($new_target)) {
1880 $res->nodeset[$i]->set_attribute("Target", $new_target);
1881 $changed = true;
1882 } else { // check wether link target is same installation
1884 IL_INST_ID > 0 && $type != "RepositoryItem") {
1885 $new_target = ilInternalLink::_removeInstFromTarget($target);
1886 if (ilInternalLink::_exists($type, $new_target)) {
1887 $res->nodeset[$i]->set_attribute("Target", $new_target);
1888 $changed = true;
1889 }
1890 }
1891 }
1892 }
1893 unset($xpc);
1894
1895 // resolve internal links in map areas
1896 $xpc = xpath_new_context($this->dom);
1897 $path = "//MediaAlias";
1898 $res = xpath_eval($xpc, $path);
1899 //echo "<br><b>page::resolve</b><br>";
1900 //echo "Content:".htmlentities($this->getXMLFromDOM()).":<br>";
1901 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
1902 $orig_id = $res->nodeset[$i]->get_attribute("OriginId");
1903 $id_arr = explode("_", $orig_id);
1904 $mob_id = $id_arr[count($id_arr) - 1];
1906 }
1907 return $changed;
1908 }
1909
1915 public function resolveMediaAliases(
1916 array $a_mapping,
1917 bool $a_reuse_existing_by_import = false
1918 ): bool {
1919 // resolve normal internal links
1920 $xpc = xpath_new_context($this->dom);
1921 $path = "//MediaAlias";
1922 $res = xpath_eval($xpc, $path);
1923 $changed = false;
1924 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
1925 // get the ID of the import file from the xml
1926 $old_id = $res->nodeset[$i]->get_attribute("OriginId");
1927 $old_id = explode("_", $old_id);
1928 $old_id = $old_id[count($old_id) - 1];
1929 $new_id = "";
1930 $import_id = "";
1931 // get the new id from the current mapping
1932 if (($a_mapping[$old_id] ?? 0) > 0) {
1933 $new_id = $a_mapping[$old_id];
1934 if ($a_reuse_existing_by_import) {
1935 // this should work, if the lm has been imported in a translation installation and re-exported
1936 $import_id = ilObject::_lookupImportId($new_id);
1937 $imp = explode("_", $import_id);
1938 if ($imp[1] == IL_INST_ID && $imp[2] == "mob" && ilObject::_lookupType($imp[3]) == "mob") {
1939 $new_id = $imp[3];
1940 }
1941 }
1942 }
1943 // now check, if the translation has been done just by changing text in the exported
1944 // translation file
1945 if ($import_id == "" && $a_reuse_existing_by_import) {
1946 // if the old_id is also referred by the page content of the default language
1947 // we assume that this media object is unchanged
1948 $med_of_def_lang = ilObjMediaObject::_getMobsOfObject(
1949 $this->getParentType() . ":pg",
1950 $this->getId(),
1951 0,
1952 "-"
1953 );
1954 if (in_array($old_id, $med_of_def_lang)) {
1955 $new_id = $old_id;
1956 }
1957 }
1958 if ($new_id != "") {
1959 $res->nodeset[$i]->set_attribute("OriginId", "il__mob_" . $new_id);
1960 $changed = true;
1961 }
1962 }
1963 unset($xpc);
1964 return $changed;
1965 }
1966
1972 public function resolveIIMMediaAliases(array $a_mapping): bool
1973 {
1974 // resolve normal internal links
1975 $xpc = xpath_new_context($this->dom);
1976 $path = "//InteractiveImage/MediaAlias";
1977 $res = xpath_eval($xpc, $path);
1978 $changed = false;
1979 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
1980 $old_id = $res->nodeset[$i]->get_attribute("OriginId");
1981 if (($a_mapping[$old_id] ?? 0) > 0) {
1982 $res->nodeset[$i]->set_attribute("OriginId", "il__mob_" . $a_mapping[$old_id]);
1983 $changed = true;
1984 }
1985 }
1986 unset($xpc);
1987
1988 return $changed;
1989 }
1990
1996 public function resolveFileItems(array $a_mapping): bool
1997 {
1998 // resolve normal internal links
1999 $xpc = xpath_new_context($this->dom);
2000 $path = "//FileItem/Identifier";
2001 $res = xpath_eval($xpc, $path);
2002 $changed = false;
2003 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
2004 $old_id = $res->nodeset[$i]->get_attribute("Entry");
2005 $old_id = explode("_", $old_id);
2006 $old_id = $old_id[count($old_id) - 1];
2007 if (($a_mapping[$old_id] ?? 0) > 0) {
2008 $res->nodeset[$i]->set_attribute("Entry", "il__file_" . $a_mapping[$old_id]);
2009 $changed = true;
2010 }
2011 }
2012 unset($xpc);
2013
2014 return $changed;
2015 }
2016
2022 public function resolveQuestionReferences(array $a_mapping): bool
2023 {
2024 // resolve normal internal links
2025 $xpc = xpath_new_context($this->dom);
2026 $path = "//Question";
2027 $res = xpath_eval($xpc, $path);
2028 $updated = false;
2029 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
2030 $qref = $res->nodeset[$i]->get_attribute("QRef");
2031
2032 if (isset($a_mapping[$qref])) {
2033 $res->nodeset[$i]->set_attribute("QRef", "il__qst_" . $a_mapping[$qref]["pool"]);
2034 $updated = true;
2035 }
2036 }
2037 unset($xpc);
2038
2039 return $updated;
2040 }
2041
2042
2048 public function moveIntLinks(array $a_from_to): bool
2049 {
2050 $this->buildDom();
2051
2052 $changed = false;
2053 // resolve normal internal links
2054 $xpc = xpath_new_context($this->dom);
2055 $path = "//IntLink";
2056 $res = xpath_eval($xpc, $path);
2057 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
2058 $target = $res->nodeset[$i]->get_attribute("Target");
2059 $type = $res->nodeset[$i]->get_attribute("Type");
2060 $obj_id = ilInternalLink::_extractObjIdOfTarget($target);
2061 if (($a_from_to[$obj_id] ?? 0) > 0 && is_int(strpos($target, "__"))) {
2062 if ($type == "PageObject" && ilLMObject::_lookupType($a_from_to[$obj_id]) == "pg") {
2063 $res->nodeset[$i]->set_attribute("Target", "il__pg_" . $a_from_to[$obj_id]);
2064 $changed = true;
2065 }
2066 if ($type == "StructureObject" && ilLMObject::_lookupType($a_from_to[$obj_id]) == "st") {
2067 $res->nodeset[$i]->set_attribute("Target", "il__st_" . $a_from_to[$obj_id]);
2068 $changed = true;
2069 }
2070 if ($type == "PortfolioPage") {
2071 $res->nodeset[$i]->set_attribute("Target", "il__ppage_" . $a_from_to[$obj_id]);
2072 $changed = true;
2073 }
2074 if ($type == "WikiPage") {
2075 $res->nodeset[$i]->set_attribute("Target", "il__wpage_" . $a_from_to[$obj_id]);
2076 $changed = true;
2077 }
2078 }
2079 }
2080 unset($xpc);
2081
2082 // map areas
2083 $this->addHierIDs();
2084 $xpc = xpath_new_context($this->dom);
2085 $path = "//MediaAlias";
2086 $res = xpath_eval($xpc, $path);
2087
2088 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
2089 $media_object_node = $res->nodeset[$i]->parent_node();
2090 $page_content_node = $media_object_node->parent_node();
2091 $c_hier_id = $page_content_node->get_attribute("HierId");
2092
2093 // first check, wheter we got instance map areas -> take these
2094 $std_alias_item = new ilMediaAliasItem(
2095 $this->dom,
2096 $c_hier_id,
2097 "Standard"
2098 );
2099 $areas = $std_alias_item->getMapAreas();
2100 $correction_needed = false;
2101 if (count($areas) > 0) {
2102 // check if correction needed
2103 foreach ($areas as $area) {
2104 if ($area["Type"] == "PageObject" ||
2105 $area["Type"] == "StructureObject") {
2106 $t = $area["Target"];
2108 if ($a_from_to[$tid] > 0) {
2109 $correction_needed = true;
2110 }
2111 }
2112 }
2113 } else {
2114 $areas = array();
2115
2116 // get object map areas and check whether at least one must
2117 // be corrected
2118 $oid = $res->nodeset[$i]->get_attribute("OriginId");
2119 if (substr($oid, 0, 4) == "il__") {
2120 $id_arr = explode("_", $oid);
2121 $id = $id_arr[count($id_arr) - 1];
2122
2123 $mob = new ilObjMediaObject($id);
2124 $med_item = $mob->getMediaItem("Standard");
2125 $med_areas = $med_item->getMapAreas();
2126
2127 foreach ($med_areas as $area) {
2128 $link_type = ($area->getLinkType() == "int")
2129 ? "IntLink"
2130 : "ExtLink";
2131
2132 $areas[] = array(
2133 "Nr" => $area->getNr(),
2134 "Shape" => $area->getShape(),
2135 "Coords" => $area->getCoords(),
2136 "Link" => array(
2137 "LinkType" => $link_type,
2138 "Href" => $area->getHref(),
2139 "Title" => $area->getTitle(),
2140 "Target" => $area->getTarget(),
2141 "Type" => $area->getType(),
2142 "TargetFrame" => $area->getTargetFrame()
2143 )
2144 );
2145
2146 if ($area->getType() == "PageObject" ||
2147 $area->getType() == "StructureObject") {
2148 $t = $area->getTarget();
2150 if ($a_from_to[$tid] > 0) {
2151 $correction_needed = true;
2152 }
2153 //var_dump($a_from_to);
2154 }
2155 }
2156 }
2157 }
2158
2159 // correct map area links
2160 if ($correction_needed) {
2161 $changed = true;
2162 $std_alias_item->deleteAllMapAreas();
2163 foreach ($areas as $area) {
2164 if ($area["Link"]["LinkType"] == "IntLink") {
2165 $target = $area["Link"]["Target"];
2166 $type = $area["Link"]["Type"];
2167 $obj_id = ilInternalLink::_extractObjIdOfTarget($target);
2168 if ($a_from_to[$obj_id] > 0) {
2169 if ($type == "PageObject" && ilLMObject::_lookupType($a_from_to[$obj_id]) == "pg") {
2170 $area["Link"]["Target"] = "il__pg_" . $a_from_to[$obj_id];
2171 }
2172 if ($type == "StructureObject" && ilLMObject::_lookupType($a_from_to[$obj_id]) == "st") {
2173 $area["Link"]["Target"] = "il__st_" . $a_from_to[$obj_id];
2174 }
2175 }
2176 }
2177
2178 $std_alias_item->addMapArea(
2179 $area["Shape"],
2180 $area["Coords"],
2181 $area["Link"]["Title"],
2182 array("Type" => $area["Link"]["Type"],
2183 "TargetFrame" => $area["Link"]["TargetFrame"],
2184 "Target" => $area["Link"]["Target"],
2185 "Href" => $area["Link"]["Href"],
2186 "LinkType" => $area["Link"]["LinkType"],
2187 )
2188 );
2189 }
2190 }
2191 }
2192 unset($xpc);
2193
2194 return $changed;
2195 }
2196
2201 public static function _handleImportRepositoryLinks(
2202 string $a_rep_import_id,
2203 string $a_rep_type,
2204 int $a_rep_ref_id
2205 ): void {
2206 //echo "-".$a_rep_import_id."-".$a_rep_ref_id."-";
2207 $sources = ilInternalLink::_getSourcesOfTarget(
2208 "obj",
2209 ilInternalLink::_extractObjIdOfTarget($a_rep_import_id),
2210 (int) ilInternalLink::_extractInstOfTarget($a_rep_import_id)
2211 );
2212 //var_dump($sources);
2213 foreach ($sources as $source) {
2214 if ($source["type"] == "lm:pg") {
2215 if (self::_exists("lm", $source["id"], $source["lang"])) {
2216 $page_obj = new ilLMPage($source["id"], 0, $source["lang"]);
2217 if (!$page_obj->page_not_found) {
2218 $page_obj->handleImportRepositoryLink(
2219 $a_rep_import_id,
2220 $a_rep_type,
2221 $a_rep_ref_id
2222 );
2223 }
2224 $page_obj->update();
2225 }
2226 }
2227 }
2228 }
2229
2230 // @todo: generalize, internal links usage info
2232 string $a_rep_import_id,
2233 string $a_rep_type,
2234 int $a_rep_ref_id
2235 ): void {
2236 $this->buildDom();
2237
2238 // resolve normal internal links
2239 $xpc = xpath_new_context($this->dom);
2240 $path = "//IntLink";
2241 $res = xpath_eval($xpc, $path);
2242 //echo "1";
2243 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
2244 //echo "2";
2245 $target = $res->nodeset[$i]->get_attribute("Target");
2246 $type = $res->nodeset[$i]->get_attribute("Type");
2247 if ($target == $a_rep_import_id && $type == "RepositoryItem") {
2248 //echo "setting:"."il__".$a_rep_type."_".$a_rep_ref_id;
2249 $res->nodeset[$i]->set_attribute(
2250 "Target",
2251 "il__" . $a_rep_type . "_" . $a_rep_ref_id
2252 );
2253 }
2254 }
2255 unset($xpc);
2256 }
2257
2262 array $a_mapping,
2263 int $a_source_ref_id
2264 ): void {
2265 $type = "";
2266 $tree = $this->tree;
2267 $objDefinition = $this->obj_definition;
2268
2269 $this->buildDom();
2270 $this->log->debug("Handle repository links...");
2271
2272 // pc classes hook, @todo: move rest of function to this hook, too
2274 foreach ($defs as $def) {
2275 //ilCOPagePCDef::requirePCClassByName($def["name"]);
2276 if (method_exists($def["pc_class"], 'afterRepositoryCopy')) {
2277 call_user_func($def["pc_class"] . '::afterRepositoryCopy', $this, $a_mapping, $a_source_ref_id);
2278 }
2279 }
2280
2281
2282 // resolve normal internal links
2283 $xpc = xpath_new_context($this->dom);
2284 $path = "//IntLink";
2285 $res = xpath_eval($xpc, $path);
2286 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
2287 $target = $res->nodeset[$i]->get_attribute("Target");
2288 $type = $res->nodeset[$i]->get_attribute("Type");
2289 $this->log->debug("Target: " . $target);
2290 $t = explode("_", $target);
2291 if ($type == "RepositoryItem" && ((int) $t[1] == 0 || (int) $t[1] == IL_INST_ID)) {
2292 if (isset($a_mapping[$t[3]])) {
2293 // we have a mapping -> replace the ID
2294 $this->log->debug("... replace " . $t[3] . " with " . $a_mapping[$t[3]] . ".");
2295 $res->nodeset[$i]->set_attribute(
2296 "Target",
2297 "il__obj_" . $a_mapping[$t[3]]
2298 );
2299 } elseif ($this->tree->isGrandChild($a_source_ref_id, $t[3])) {
2300 // we have no mapping, but the linked object is child of the original node -> remove link
2301 $this->log->debug("... remove links.");
2302 if ($res->nodeset[$i]->parent_node()->node_name() == "MapArea") { // simply remove map areas
2303 $parent = $res->nodeset[$i]->parent_node();
2304 $parent->unlink_node($parent);
2305 } else { // replace link by content of the link for other internal links
2306 $source_node = $res->nodeset[$i];
2307 $new_node = $source_node->clone_node(true);
2308 $new_node->unlink_node($new_node);
2309 $childs = $new_node->child_nodes();
2310 for ($j = 0, $jMax = count($childs); $j < $jMax; $j++) {
2311 $this->log->debug("... move node $j " . $childs[$j]->node_name() . " before " . $source_node->node_name());
2312 $source_node->insert_before($childs[$j], $source_node);
2313 }
2314 $source_node->unlink_node($source_node);
2315 }
2316 }
2317 }
2318 }
2319 unset($xpc);
2320
2321 // resolve normal external links
2322 $ilias_url = parse_url(ILIAS_HTTP_PATH);
2323 $xpc = xpath_new_context($this->dom);
2324 $path = "//ExtLink";
2325 $res = xpath_eval($xpc, $path);
2326 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
2327 $href = $res->nodeset[$i]->get_attribute("Href");
2328 $this->log->debug("Href: " . $href);
2329
2330 $url = parse_url($href);
2331
2332 // only handle links on same host
2333 $this->log->debug("Host: " . ($url["host"] ?? ""));
2334 if (($url["host"] ?? "") !== "" && $url["host"] !== $ilias_url["host"]) {
2335 continue;
2336 }
2337
2338 // get parameters
2339 $par = [];
2340 if (substr($href, strlen($href) - 5) === ".html") {
2341 $parts = explode(
2342 "_",
2343 basename(
2344 substr($url["path"], 0, strlen($url["path"]) - 5)
2345 )
2346 );
2347 if (array_shift($parts) !== "goto") {
2348 continue;
2349 }
2350 $par["client_id"] = array_shift($parts);
2351 $par["target"] = implode("_", $parts);
2352 } else {
2353 foreach (explode("&", ($url["query"] ?? "")) as $p) {
2354 $p = explode("=", $p);
2355 if (isset($p[0]) && isset($p[1])) {
2356 $par[$p[0]] = $p[1];
2357 }
2358 }
2359 }
2360
2361 $target_client_id = $par["client_id"] ?? "";
2362 if ($target_client_id != "" && $target_client_id != CLIENT_ID) {
2363 continue;
2364 }
2365
2366 // get ref id
2367 $ref_id = 0;
2368 if (is_int(strpos($href, "ilias.php"))) {
2369 $ref_id = (int) ($par["ref_id"] ?? 0);
2370 } elseif (isset($par["target"]) && $par["target"] !== "") {
2371 $t = explode("_", $par["target"]);
2372 if ($objDefinition->isRBACObject($t[0] ?? "")) {
2373 $ref_id = (int) ($t[1] ?? 0);
2374 $type = $t[0] ?? "";
2375 }
2376 }
2377 if ($ref_id > 0) {
2378 if (isset($a_mapping[$ref_id])) {
2379 $new_ref_id = $a_mapping[$ref_id];
2380 // we have a mapping -> replace the ID
2381 if (is_int(strpos($href, "ilias.php"))) {
2382 $new_href = str_replace("ref_id=" . ($par["ref_id"] ?? ""), "ref_id=" . $new_ref_id, $href);
2383 } else {
2384 $nt = str_replace($type . "_" . $ref_id, $type . "_" . $new_ref_id, $par["target"]);
2385 $new_href = str_replace($par["target"], $nt, $href);
2386 }
2387 if ($new_href != "") {
2388 $this->log->debug("... ext link replace " . $href . " with " . $new_href . ".");
2389 $res->nodeset[$i]->set_attribute("Href", $new_href);
2390 }
2391 } elseif ($tree->isGrandChild($a_source_ref_id, $ref_id)) {
2392 // we have no mapping, but the linked object is child of the original node -> remove link
2393 $this->log->debug("... remove ext links.");
2394 if ($res->nodeset[$i]->parent_node()->node_name() == "MapArea") { // simply remove map areas
2395 $parent = $res->nodeset[$i]->parent_node();
2396 $parent->unlink_node($parent);
2397 } else { // replace link by content of the link for other internal links
2398 $source_node = $res->nodeset[$i];
2399 $new_node = $source_node->clone_node(true);
2400 $new_node->unlink_node($new_node);
2401 $childs = $new_node->child_nodes();
2402 for ($j = 0, $jMax = count($childs); $j < $jMax; $j++) {
2403 $this->log->debug("... move node $j " . $childs[$j]->node_name() . " before " . $source_node->node_name());
2404 $source_node->insert_before($childs[$j], $source_node);
2405 }
2406 $source_node->unlink_node($source_node);
2407 }
2408 }
2409 }
2410 }
2411 unset($xpc);
2412 }
2413
2417 public function createFromXML(): void
2418 {
2419 $empty = false;
2420 if ($this->getXMLContent() == "") {
2421 $this->setXMLContent("<PageObject></PageObject>");
2422 $empty = true;
2423 }
2424
2425 $content = $this->getXMLContent();
2426 $this->buildDom(true);
2427 $dom_doc = $this->getDomDoc();
2428
2429 $errors = $this->validateDom(true);
2430
2431 $iel = $this->containsDeactivatedElements($content);
2432 $inl = $this->containsIntLinks($content);
2433 // create object
2434 $this->db->insert("page_object", array(
2435 "page_id" => array("integer", $this->getId()),
2436 "parent_id" => array("integer", $this->getParentId()),
2437 "lang" => array("text", $this->getLanguage()),
2438 "content" => array("clob", $content),
2439 "parent_type" => array("text", $this->getParentType()),
2440 "create_user" => array("integer", $this->user->getId()),
2441 "last_change_user" => array("integer", $this->user->getId()),
2442 "active" => array("integer", (int) $this->getActive()),
2443 "activation_start" => array("timestamp", $this->getActivationStart()),
2444 "activation_end" => array("timestamp", $this->getActivationEnd()),
2445 "show_activation_info" => array("integer", (int) $this->getShowActivationInfo()),
2446 "inactive_elements" => array("integer", $iel),
2447 "int_links" => array("integer", $inl),
2448 "created" => array("timestamp", ilUtil::now()),
2449 "last_change" => array("timestamp", ilUtil::now()),
2450 "is_empty" => array("integer", $empty)
2451 ));
2452
2453 // after update event
2454 $this->__afterUpdate($dom_doc, $content, true, $empty);
2455 }
2456
2464 public function updateFromXML(): bool
2465 {
2466 $this->log->debug("ilPageObject, updateFromXML(): start, id: " . $this->getId());
2467
2468 $content = $this->getXMLContent();
2469
2470 $this->log->debug("ilPageObject, updateFromXML(): content: " . substr($content, 0, 100));
2471
2472 $this->buildDom(true);
2473 $dom_doc = $this->getDomDoc();
2474
2475 $errors = $this->validateDom(true);
2476
2477 $iel = $this->containsDeactivatedElements($content);
2478 $inl = $this->containsIntLinks($content);
2479
2480 $this->db->update("page_object", array(
2481 "content" => array("clob", $content),
2482 "parent_id" => array("integer", $this->getParentId()),
2483 "last_change_user" => array("integer", $this->user->getId()),
2484 "last_change" => array("timestamp", ilUtil::now()),
2485 "active" => array("integer", $this->getActive()),
2486 "activation_start" => array("timestamp", $this->getActivationStart()),
2487 "activation_end" => array("timestamp", $this->getActivationEnd()),
2488 "inactive_elements" => array("integer", $iel),
2489 "int_links" => array("integer", $inl),
2490 ), array(
2491 "page_id" => array("integer", $this->getId()),
2492 "parent_type" => array("text", $this->getParentType()),
2493 "lang" => array("text", $this->getLanguage())
2494 ));
2495
2496 // after update event
2497 $this->__afterUpdate($dom_doc, $content);
2498
2499 $this->log->debug("ilPageObject, updateFromXML(): end");
2500
2501 return true;
2502 }
2503
2508 final protected function __afterUpdate(
2509 DOMDocument $a_domdoc,
2510 string $a_xml,
2511 bool $a_creation = false,
2512 bool $a_empty = false
2513 ): void {
2514 // we do not need this if we are creating an empty page
2515 if (!$a_creation || !$a_empty) {
2516 // save internal link information
2517 // the page object is responsible to do this, since it "offers" the
2518 // internal link feature pc and page classes
2519 $this->saveInternalLinks($a_domdoc);
2520
2521 // save style usage
2522 $this->saveStyleUsage($a_domdoc);
2523
2524 // save estimated reading time
2525 $this->reading_time_manager->saveTime($this);
2526
2527 // pc classes hook
2529 foreach ($defs as $def) {
2530 //ilCOPagePCDef::requirePCClassByName($def["name"]);
2531 $cl = $def["pc_class"];
2532 call_user_func($def["pc_class"] . '::afterPageUpdate', $this, $a_domdoc, $a_xml, $a_creation);
2533 }
2534 }
2535
2536 // call page hook
2537 $this->afterUpdate($a_domdoc, $a_xml);
2538
2539 // call update listeners
2540 $this->callUpdateListeners();
2541 }
2542
2546 public function afterUpdate(DOMDocument $domdoc, string $xml): void
2547 {
2548 }
2549
2556 public function update(bool $a_validate = true, bool $a_no_history = false)
2557 {
2558 $this->log->debug("start..., id: " . $this->getId());
2559
2560 $lm_set = new ilSetting("lm");
2561
2562 // add missing pc ids
2563 if (!$this->checkPCIds()) {
2564 $this->insertPCIds();
2565 }
2566
2567 // test validating
2568 if ($a_validate) {
2569 $errors = $this->validateDom();
2570 }
2571 //var_dump($errors); exit;
2572 if (empty($errors) && !$this->getEditLock()) {
2573 $lock = $this->getEditLockInfo();
2574 $errors[0] = array(0 => 0,
2575 1 => $this->lng->txt("cont_not_saved_edit_lock_expired") . "<br />" .
2576 $this->lng->txt("obj_usr") . ": " .
2577 ilUserUtil::getNamePresentation($lock["edit_lock_user"]) . "<br />" .
2578 $this->lng->txt("content_until") . ": " .
2579 ilDatePresentation::formatDate(new ilDateTime($lock["edit_lock_until"], IL_CAL_UNIX))
2580 );
2581 }
2582
2583 // check for duplicate pc ids
2584 $this->log->debug("checking duplicate ids");
2585 if ($this->hasDuplicatePCIds()) {
2586 $errors[0] = $this->lng->txt("cont_could_not_save_duplicate_pc_ids") .
2587 " (" . implode(", ", $this->getDuplicatePCIds()) . ")";
2588 }
2589
2590 if (!empty($errors)) {
2591 $this->log->debug("ilPageObject, update(): errors: " . print_r($errors, true));
2592 }
2593
2594 //echo "-".htmlentities($this->getXMLFromDom())."-"; exit;
2595 if (empty($errors)) {
2596 // @todo 1: is this page type or pc content type
2597 // related -> plugins should be able to hook in!?
2598
2599 $this->log->debug("perform automatic modifications");
2600 $this->performAutomaticModifications();
2601
2602 // get xml content
2603 $content = $this->getXMLFromDom();
2604 $dom_doc = $this->getDomDoc();
2605
2606 // this needs to be locked
2607
2608 // write history entry
2609 $old_set = $this->db->query("SELECT * FROM page_object WHERE " .
2610 "page_id = " . $this->db->quote($this->getId(), "integer") . " AND " .
2611 "parent_type = " . $this->db->quote($this->getParentType(), "text") . " AND " .
2612 "lang = " . $this->db->quote($this->getLanguage(), "text"));
2613 $last_nr_set = $this->db->query("SELECT max(nr) as mnr FROM page_history WHERE " .
2614 "page_id = " . $this->db->quote($this->getId(), "integer") . " AND " .
2615 "parent_type = " . $this->db->quote($this->getParentType(), "text") . " AND " .
2616 "lang = " . $this->db->quote($this->getLanguage(), "text"));
2617 $last_nr = $this->db->fetchAssoc($last_nr_set);
2618 if ($old_rec = $this->db->fetchAssoc($old_set)) {
2619 // only save, if something has changed
2620 // added user id to the check for ilias 5.0, 7.10.2014
2621 if (($content != $old_rec["content"] || $this->user->getId() != $old_rec["last_change_user"]) &&
2622 !$a_no_history && !$this->history_saved && $lm_set->get("page_history", 1)) {
2623 if ($old_rec["content"] != "<PageObject></PageObject>") {
2624 $this->db->manipulateF(
2625 "DELETE FROM page_history WHERE " .
2626 "page_id = %s AND parent_type = %s AND hdate = %s AND lang = %s",
2627 array("integer", "text", "timestamp", "text"),
2628 array($old_rec["page_id"],
2629 $old_rec["parent_type"],
2630 $old_rec["last_change"],
2631 $old_rec["lang"]
2632 )
2633 );
2634 // the following lines are a workaround for
2635 // bug 6741
2636 $last_c = $old_rec["last_change"];
2637 if ($last_c == "") {
2638 $last_c = ilUtil::now();
2639 }
2640
2641 $this->db->insert("page_history", array(
2642 "page_id" => array("integer", $old_rec["page_id"]),
2643 "parent_type" => array("text", $old_rec["parent_type"]),
2644 "lang" => array("text", $old_rec["lang"]),
2645 "hdate" => array("timestamp", $last_c),
2646 "parent_id" => array("integer", $old_rec["parent_id"]),
2647 "content" => array("clob", $old_rec["content"]),
2648 "user_id" => array("integer", $old_rec["last_change_user"]),
2649 "ilias_version" => array("text", ILIAS_VERSION_NUMERIC),
2650 "nr" => array("integer", (int) $last_nr["mnr"] + 1)
2651 ));
2652 $old_content = $old_rec["content"];
2653 $old_domdoc = new DOMDocument();
2654 $old_nr = $last_nr["mnr"] + 1;
2655 $old_domdoc->loadXML('<?xml version="1.0" encoding="UTF-8"?>' . $old_content);
2656
2657 // after history entry creation event
2658 $this->log->debug("calling __afterHistoryEntry $old_nr");
2659 $this->__afterHistoryEntry($old_domdoc, $old_content, $old_nr);
2660
2661 // only save one time
2662 }
2663 $this->history_saved = true;
2664 }
2665 }
2666 //echo htmlentities($content);
2667 $em = (trim($content) == "<PageObject/>")
2668 ? 1
2669 : 0;
2670
2671 // @todo: pass dom instead?
2672 $this->log->debug("checking deactivated elements");
2673 $iel = $this->containsDeactivatedElements($content);
2674 $this->log->debug("checking internal links");
2675 $inl = $this->containsIntLinks($content);
2676 $this->db->update("page_object", array(
2677 "content" => array("clob", $content),
2678 "parent_id" => array("integer", $this->getParentId()),
2679 "last_change_user" => array("integer", $this->user->getId()),
2680 "last_change" => array("timestamp", ilUtil::now()),
2681 "is_empty" => array("integer", $em),
2682 "active" => array("integer", $this->getActive()),
2683 "activation_start" => array("timestamp", $this->getActivationStart()),
2684 "activation_end" => array("timestamp", $this->getActivationEnd()),
2685 "show_activation_info" => array("integer", $this->getShowActivationInfo()),
2686 "inactive_elements" => array("integer", $iel),
2687 "int_links" => array("integer", $inl),
2688 ), array(
2689 "page_id" => array("integer", $this->getId()),
2690 "parent_type" => array("text", $this->getParentType()),
2691 "lang" => array("text", $this->getLanguage())
2692 ));
2693
2694 // after update event
2695 $this->log->debug("calling __afterUpdate()");
2696 $this->__afterUpdate($dom_doc, $content);
2697
2698 $this->log->debug(
2699 "...ending, updated and returning true, content: " . substr(
2700 $this->getXMLContent(),
2701 0,
2702 100
2703 )
2704 );
2705
2706 //echo "<br>PageObject::update:".htmlentities($this->getXMLContent()).":";
2707 return true;
2708 } else {
2709 return $errors;
2710 }
2711 }
2712
2713 public function delete(): void
2714 {
2715 $copg_logger = ilLoggerFactory::getLogger('copg');
2716 $copg_logger->debug(
2717 "ilPageObject: Delete called for ID '" . $this->getId() . "'," .
2718 " parent type: '" . $this->getParentType() . "', " .
2719 " hist nr: '" . $this->old_nr . "', " .
2720 " lang: '" . $this->getLanguage() . "', "
2721 );
2722
2723 $mobs = array();
2724 if (!$this->page_not_found) {
2725 $this->buildDom();
2726 $mobs = $this->collectMediaObjects(false);
2727 }
2728 $mobs2 = ilObjMediaObject::_getMobsOfObject($this->getParentType() . ":pg", $this->getId(), false);
2729 foreach ($mobs2 as $m) {
2730 if (!in_array($m, $mobs)) {
2731 $mobs[] = $m;
2732 }
2733 }
2734
2735 $copg_logger->debug("ilPageObject: ... found " . count($mobs) . " media objects.");
2736
2737 $this->__beforeDelete();
2738
2739 // treat plugged content
2740 $this->handleDeleteContent();
2741
2742 // delete style usages
2743 $this->deleteStyleUsages(false);
2744
2745 // delete internal links
2746 $this->deleteInternalLinks();
2747
2748 // delete all mob usages
2749 ilObjMediaObject::_deleteAllUsages($this->getParentType() . ":pg", $this->getId());
2750
2751 // delete news
2753 $this->getParentId(),
2754 $this->getParentType(),
2755 $this->getId(),
2756 "pg"
2757 );
2758
2759 // delete page_object entry
2760 $this->db->manipulate("DELETE FROM page_object " .
2761 "WHERE page_id = " . $this->db->quote($this->getId(), "integer") .
2762 " AND parent_type= " . $this->db->quote($this->getParentType(), "text"));
2763
2764 // delete media objects
2765 foreach ($mobs as $mob_id) {
2766 $copg_logger->debug("ilPageObject: ... processing mob " . $mob_id . ".");
2767
2768 if (ilObject::_lookupType($mob_id) != 'mob') {
2769 $copg_logger->debug("ilPageObject: ... type mismatch. Ignoring mob " . $mob_id . ".");
2770 continue;
2771 }
2772
2773 if (ilObject::_exists($mob_id)) {
2774 $copg_logger->debug("ilPageObject: ... delete mob " . $mob_id . ".");
2775
2776 $mob_obj = new ilObjMediaObject($mob_id);
2777 $mob_obj->delete();
2778 } else {
2779 $copg_logger->debug("ilPageObject: ... missing mob " . $mob_id . ".");
2780 }
2781 }
2782
2783 $this->__afterDelete();
2784 }
2785
2789 final protected function __beforeDelete(): void
2790 {
2791 // pc classes hook
2793 foreach ($defs as $def) {
2794 //ilCOPagePCDef::requirePCClassByName($def["name"]);
2795 $cl = $def["pc_class"];
2796 call_user_func($def["pc_class"] . '::beforePageDelete', $this);
2797 }
2798 }
2799
2800 final protected function __afterDelete(): void
2801 {
2802 $this->afterDelete();
2803 }
2804
2805 protected function afterDelete(): void
2806 {
2807 }
2808
2809 final protected function __afterHistoryEntry(
2810 DOMDocument $a_old_domdoc,
2811 string $a_old_content,
2812 int $a_old_nr
2813 ): void {
2814 // save style usage
2815 $this->saveStyleUsage($a_old_domdoc, $a_old_nr);
2816
2817 // pc classes hook
2819 foreach ($defs as $def) {
2820 //ilCOPagePCDef::requirePCClassByName($def["name"]);
2821 $cl = $def["pc_class"];
2822 call_user_func(
2823 $def["pc_class"] . '::afterPageHistoryEntry',
2824 $this,
2825 $a_old_domdoc,
2826 $a_old_content,
2827 $a_old_nr
2828 );
2829 }
2830 }
2831
2835 public function saveStyleUsage(
2836 DOMDocument $a_domdoc,
2837 int $a_old_nr = 0
2838 ): void {
2839 $sname = "";
2840 $stype = "";
2841 $template = "";
2842 // media aliases
2843 $xpath = new DOMXPath($a_domdoc);
2844 $path = "//Paragraph | //Section | //MediaAlias | //FileItem" .
2845 " | //Table | //TableData | //Tabs | //List";
2846 $nodes = $xpath->query($path);
2847 $usages = array();
2848 foreach ($nodes as $node) {
2849 switch ($node->localName) {
2850 case "Paragraph":
2851 $sname = $node->getAttribute("Characteristic");
2852 $stype = "text_block";
2853 $template = 0;
2854 break;
2855
2856 case "Section":
2857 $sname = $node->getAttribute("Characteristic");
2858 $stype = "section";
2859 $template = 0;
2860 break;
2861
2862 case "MediaAlias":
2863 $sname = $node->getAttribute("Class");
2864 $stype = "media_cont";
2865 $template = 0;
2866 break;
2867
2868 case "FileItem":
2869 $sname = $node->getAttribute("Class");
2870 $stype = "flist_li";
2871 $template = 0;
2872 break;
2873
2874 case "Table":
2875 $sname = $node->getAttribute("Template");
2876 if ($sname == "") {
2877 $sname = $node->getAttribute("Class");
2878 $stype = "table";
2879 $template = 0;
2880 } else {
2881 $stype = "table";
2882 $template = 1;
2883 }
2884 break;
2885
2886 case "TableData":
2887 $sname = $node->getAttribute("Class");
2888 $stype = "table_cell";
2889 $template = 0;
2890 break;
2891
2892 case "Tabs":
2893 $sname = $node->getAttribute("Template");
2894 if ($sname != "") {
2895 if ($node->getAttribute("Type") == "HorizontalAccordion") {
2896 $stype = "haccordion";
2897 }
2898 if ($node->getAttribute("Type") == "VerticalAccordion") {
2899 $stype = "vaccordion";
2900 }
2901 }
2902 $template = 1;
2903 break;
2904
2905 case "List":
2906 $sname = $node->getAttribute("Class");
2907 if ($node->getAttribute("Type") == "Ordered") {
2908 $stype = "list_o";
2909 } else {
2910 $stype = "list_u";
2911 }
2912 $template = 0;
2913 break;
2914 }
2915 if ($sname != "" && $stype != "") {
2916 $usages[$sname . ":" . $stype . ":" . $template] = array("sname" => $sname,
2917 "stype" => $stype,
2918 "template" => $template
2919 );
2920 }
2921 }
2922
2923 $this->deleteStyleUsages($a_old_nr);
2924
2925 foreach ($usages as $u) {
2926 $id = $this->db->nextId('page_style_usage');
2927 $this->db->manipulate("INSERT INTO page_style_usage " .
2928 "(id, page_id, page_type, page_lang, page_nr, template, stype, sname) VALUES (" .
2929 $this->db->quote($id, "integer") . "," .
2930 $this->db->quote($this->getId(), "integer") . "," .
2931 $this->db->quote($this->getParentType(), "text") . "," .
2932 $this->db->quote($this->getLanguage(), "text") . "," .
2933 $this->db->quote($a_old_nr, "integer") . "," .
2934 $this->db->quote($u["template"], "integer") . "," .
2935 $this->db->quote($u["stype"], "text") . "," .
2936 $this->db->quote(ilStr::subStr($u["sname"], 0, 30), "text") .
2937 ")");
2938 }
2939 }
2940
2944 public function deleteStyleUsages(int $a_old_nr = 0): void
2945 {
2946 $and_old_nr = "";
2947 if ($a_old_nr !== 0) {
2948 $and_old_nr = " AND page_nr = " . $this->db->quote($a_old_nr, "integer");
2949 }
2950
2951 $this->db->manipulate(
2952 "DELETE FROM page_style_usage WHERE " .
2953 " page_id = " . $this->db->quote($this->getId(), "integer") .
2954 " AND page_type = " . $this->db->quote($this->getParentType(), "text") .
2955 " AND page_lang = " . $this->db->quote($this->getLanguage(), "text") .
2956 $and_old_nr
2957 );
2958 }
2959
2960
2966 public function getLastUpdateOfIncludedElements(): string
2967 {
2969 $this->getParentType() . ":pg",
2970 $this->getId()
2971 );
2972 $files = ilObjFile::_getFilesOfObject(
2973 $this->getParentType() . ":pg",
2974 $this->getId()
2975 );
2976 $objs = array_merge($mobs, $files);
2978 }
2979
2983 public function deleteInternalLinks(): void
2984 {
2986 $this->getParentType() . ":pg",
2987 $this->getId(),
2988 $this->getLanguage()
2989 );
2990 }
2991
2992
2997 public function saveInternalLinks(DOMDocument $a_domdoc): void
2998 {
2999 $this->deleteInternalLinks();
3000 $t_type = "";
3001 // query IntLink elements
3002 $xpath = new DOMXPath($a_domdoc);
3003 $nodes = $xpath->query('//IntLink');
3004 foreach ($nodes as $node) {
3005 $link_type = $node->getAttribute("Type");
3006
3007 switch ($link_type) {
3008 case "StructureObject":
3009 $t_type = "st";
3010 break;
3011
3012 case "PageObject":
3013 $t_type = "pg";
3014 break;
3015
3016 case "GlossaryItem":
3017 $t_type = "git";
3018 break;
3019
3020 case "MediaObject":
3021 $t_type = "mob";
3022 break;
3023
3024 case "RepositoryItem":
3025 $t_type = "obj";
3026 break;
3027
3028 case "File":
3029 $t_type = "file";
3030 break;
3031
3032 case "WikiPage":
3033 $t_type = "wpage";
3034 break;
3035
3036 case "PortfolioPage":
3037 $t_type = "ppage";
3038 break;
3039
3040 case "User":
3041 $t_type = "user";
3042 break;
3043 }
3044
3045 $target = $node->getAttribute("Target");
3046 $target_arr = explode("_", $target);
3047 $t_id = $target_arr[count($target_arr) - 1];
3048
3049 // link to other internal object
3050 if (is_int(strpos($target, "__"))) {
3051 $t_inst = 0;
3052 } else { // link to unresolved object in other installation
3053 $t_inst = (int) ($target_arr[1] ?? 0);
3054 }
3055
3056 if ($t_id > 0) {
3058 $this->getParentType() . ":pg",
3059 $this->getId(),
3060 $t_type,
3061 $t_id,
3062 $t_inst,
3063 $this->getLanguage()
3064 );
3065 }
3066 }
3067 }
3068
3072 public function create(bool $a_import = false): void
3073 {
3074 $this->createFromXML();
3075 }
3076
3083 public function deleteContent(
3084 string $a_hid,
3085 bool $a_update = true,
3086 string $a_pcid = "",
3087 bool $move_operation = false
3088 ) {
3089 $curr_node = $this->getContentNode($a_hid, $a_pcid);
3090 $this->handleDeleteContent($curr_node, $move_operation);
3091 $curr_node->unlink_node($curr_node);
3092 if ($a_update) {
3093 return $this->update();
3094 }
3095 return true;
3096 }
3097
3105 public function deleteContents(
3106 array $a_hids,
3107 bool $a_update = true,
3108 bool $a_self_ass = false,
3109 bool $move_operation = false
3110 ) {
3111 if (!is_array($a_hids)) {
3112 return true;
3113 }
3114 foreach ($a_hids as $a_hid) {
3115 $a_hid = explode(":", $a_hid);
3116 //echo "-".$a_hid[0]."-".$a_hid[1]."-";
3117
3118 // @todo 1: hook
3119 // do not delete question nodes in assessment pages
3120 if (!$this->checkForTag("Question", $a_hid[0], (string) ($a_hid[1] ?? "")) || $a_self_ass) {
3121 $curr_node = $this->getContentNode((string) $a_hid[0], (string) ($a_hid[1] ?? ""));
3122 if (is_object($curr_node)) {
3123 $parent_node = $curr_node->parent_node();
3124 if ($parent_node->node_name() != "TableRow") {
3125 $this->handleDeleteContent($curr_node, $move_operation);
3126 $curr_node->unlink_node($curr_node);
3127 }
3128 }
3129 }
3130 }
3131 if ($a_update) {
3132 return $this->update();
3133 }
3134 return true;
3135 }
3136
3142 public function cutContents(array $a_hids)
3143 {
3144 $this->copyContents($a_hids);
3145 return $this->deleteContents(
3146 $a_hids,
3147 true,
3148 $this->getPageConfig()->getEnableSelfAssessment(),
3149 true
3150 );
3151 }
3152
3156 public function copyContents(array $a_hids): void
3157 {
3158 $user = $this->user;
3159
3160 $pc_id = null;
3161
3162 if (!is_array($a_hids)) {
3163 return;
3164 }
3165
3166 $time = date("Y-m-d H:i:s", time());
3167
3168 $hier_ids = array();
3169 $skip = array();
3170 foreach ($a_hids as $a_hid) {
3171 if ($a_hid == "") {
3172 continue;
3173 }
3174 $a_hid = explode(":", $a_hid);
3175
3176 // check, whether new hid is child of existing one or vice versa
3177 reset($hier_ids);
3178 foreach ($hier_ids as $h) {
3179 if ($h . "_" == substr($a_hid[0], 0, strlen($h) + 1)) {
3180 $skip[] = $a_hid[0];
3181 }
3182 if ($a_hid[0] . "_" == substr($h, 0, strlen($a_hid[0]) + 1)) {
3183 $skip[] = $h;
3184 }
3185 }
3186 $pc_id[$a_hid[0]] = $a_hid[1];
3187 if ($a_hid[0] != "") {
3188 $hier_ids[$a_hid[0]] = $a_hid[0];
3189 }
3190 }
3191 foreach ($skip as $s) {
3192 unset($hier_ids[$s]);
3193 }
3194 $hier_ids = ilPageContent::sortHierIds($hier_ids);
3195 $nr = 1;
3196 foreach ($hier_ids as $hid) {
3197 $curr_node = $this->getContentNode($hid, $pc_id[$hid]);
3198 if (is_object($curr_node)) {
3199 if ($curr_node->node_name() == "PageContent") {
3200 $content = $this->dom->dump_node($curr_node);
3201 // remove pc and hier ids
3202 $content = preg_replace('/PCID=\"[a-z0-9]*\"/i', "", $content);
3203 $content = preg_replace('/HierId=\"[a-z0-9_]*\"/i', "", $content);
3204
3205 $user->addToPCClipboard($content, $time, $nr);
3206 $nr++;
3207 }
3208 }
3209 }
3211 }
3212
3218 public function pasteContents(
3219 string $a_hier_id,
3220 bool $a_self_ass = false
3221 ) {
3222 $user = $this->user;
3223
3224 $a_hid = explode(":", $a_hier_id);
3225 $content = $user->getPCClipboardContent();
3226
3227 // we insert from last to first, because we insert all at the
3228 // same hier_id
3229 for ($i = count($content) - 1; $i >= 0; $i--) {
3230 $c = $content[$i];
3231 $temp_dom = domxml_open_mem(
3232 '<?xml version="1.0" encoding="UTF-8"?>' . $c,
3234 $error
3235 );
3236 if (empty($error)) {
3237 $this->handleCopiedContent($temp_dom, $a_self_ass);
3238 $xpc = xpath_new_context($temp_dom);
3239 $path = "//PageContent";
3240 $res = xpath_eval($xpc, $path);
3241 if (count($res->nodeset) > 0) {
3242 $new_pc_node = $res->nodeset[0];
3243 $cloned_pc_node = $new_pc_node->clone_node(true);
3244 $cloned_pc_node->unlink_node($cloned_pc_node);
3245 $this->insertContentNode(
3246 $cloned_pc_node,
3247 $a_hid[0],
3249 $a_hid[1]
3250 );
3251 }
3252 } else {
3253 //var_dump($error);
3254 }
3255 }
3256 return $this->update();
3257 }
3258
3266 public function switchEnableMultiple(
3267 array $a_hids,
3268 bool $a_update = true,
3269 bool $a_self_ass = false
3270 ) {
3271 if (!is_array($a_hids)) {
3272 return true;
3273 }
3274
3275 foreach ($a_hids as $a_hid) {
3276 $a_hid = explode(":", $a_hid);
3277 $curr_node = $this->getContentNode($a_hid[0], $a_hid[1]);
3278 if (is_object($curr_node)) {
3279 if ($curr_node->node_name() == "PageContent") {
3280 $cont_obj = $this->getContentObject($a_hid[0], $a_hid[1]);
3281 if ($cont_obj->isEnabled()) {
3282 // do not deactivate question nodes in assessment pages
3283 if (!$this->checkForTag("Question", $a_hid[0], (string) $a_hid[1]) || $a_self_ass) {
3284 $cont_obj->disable();
3285 }
3286 } else {
3287 $cont_obj->enable();
3288 }
3289 }
3290 }
3291 }
3292
3293 if ($a_update) {
3294 return $this->update();
3295 }
3296 return true;
3297 }
3298
3309 string $a_hid,
3310 bool $a_update = true
3311 ) {
3312 $hier_ids = $this->getHierIds();
3313
3314 // iterate all hierarchical ids
3315 foreach ($hier_ids as $hier_id) {
3316 // delete top level nodes only
3317 if (!is_int(strpos($hier_id, "_"))) {
3318 if ($hier_id != "pg" && $hier_id >= $a_hid) {
3319 $curr_node = $this->getContentNode($hier_id);
3320 $this->handleDeleteContent($curr_node, true);
3321 $curr_node->unlink_node($curr_node);
3322 }
3323 }
3324 }
3325 if ($a_update) {
3326 return $this->update();
3327 }
3328 return true;
3329 }
3330
3341 string $a_hid,
3342 bool $a_update = true
3343 ) {
3344 $hier_ids = $this->getHierIds();
3345
3346 // iterate all hierarchical ids
3347 foreach ($hier_ids as $hier_id) {
3348 // delete top level nodes only
3349 if (!is_int(strpos($hier_id, "_"))) {
3350 if ($hier_id != "pg" && $hier_id < $a_hid) {
3351 $curr_node = $this->getContentNode($hier_id);
3352 $this->handleDeleteContent($curr_node, true);
3353 $curr_node->unlink_node($curr_node);
3354 }
3355 }
3356 }
3357 if ($a_update) {
3358 return $this->update();
3359 }
3360 return true;
3361 }
3362
3367 public static function _moveContentAfterHierId(
3368 ilPageObject $a_source_page,
3369 ilPageObject $a_target_page,
3370 string $a_hid
3371 ): void {
3372 $hier_ids = $a_source_page->getHierIds();
3373
3374 $copy_ids = array();
3375
3376 // iterate all hierarchical ids
3377 foreach ($hier_ids as $hier_id) {
3378 // move top level nodes only
3379 if (!is_int(strpos($hier_id, "_"))) {
3380 if ($hier_id != "pg" && $hier_id >= $a_hid) {
3381 $copy_ids[] = $hier_id;
3382 }
3383 }
3384 }
3385 asort($copy_ids);
3386
3387 $parent_node = $a_target_page->getContentNode("pg");
3388 $target_dom = $a_target_page->getDom();
3389 $parent_childs = $parent_node->child_nodes();
3390 $cnt_parent_childs = count($parent_childs);
3391 //echo "-$cnt_parent_childs-";
3392 $first_child = $parent_childs[0];
3393 foreach ($copy_ids as $copy_id) {
3394 $source_node = $a_source_page->getContentNode($copy_id);
3395
3396 $new_node = $source_node->clone_node(true);
3397 $new_node->unlink_node($new_node);
3398
3399 $source_node->unlink_node($source_node);
3400
3401 if ($cnt_parent_childs == 0) {
3402 $new_node = $parent_node->append_child($new_node);
3403 } else {
3404 //$target_dom->import_node($new_node);
3405 $new_node = $first_child->insert_before($new_node, $first_child);
3406 }
3407 $parent_childs = $parent_node->child_nodes();
3408
3409 //$cnt_parent_childs++;
3410 }
3411
3412 $a_target_page->update();
3413 $a_source_page->update();
3414 }
3415
3419 public function insertContent(
3420 ilPageContent $a_cont_obj,
3421 string $a_pos,
3422 int $a_mode = IL_INSERT_AFTER,
3423 string $a_pcid = "",
3424 bool $remove_placeholder = true
3425 ): void {
3426 if ($a_pcid == "" && $a_pos == "") {
3427 $a_pos = "pg";
3428 }
3429 // move mode into container elements is always INSERT_CHILD
3430 $curr_node = $this->getContentNode($a_pos, $a_pcid);
3431 $curr_name = $curr_node->node_name();
3432
3433 // @todo: try to generalize this
3434 if (($curr_name == "TableData") || ($curr_name == "PageObject") ||
3435 ($curr_name == "ListItem") || ($curr_name == "Section")
3436 || ($curr_name == "Tab") || ($curr_name == "ContentPopup")
3437 || ($curr_name == "GridCell")) {
3438 $a_mode = IL_INSERT_CHILD;
3439 }
3440
3441 $hid = $curr_node->get_attribute("HierId");
3442 if ($hid != "") {
3443 //echo "-".$a_pos."-".$hid."-";
3444 $a_pos = $hid;
3445 }
3446
3447 if ($a_mode != IL_INSERT_CHILD) { // determine parent hierarchical id
3448 // of sibling at $a_pos
3449 $pos = explode("_", $a_pos);
3450 $target_pos = array_pop($pos);
3451 $parent_pos = implode("_", $pos);
3452 } else { // if we should insert a child, $a_pos is alreade the hierarchical id
3453 // of the parent node
3454 $parent_pos = $a_pos;
3455 }
3456
3457 // get the parent node
3458 if ($parent_pos != "") {
3459 $parent_node = $this->getContentNode($parent_pos);
3460 } else {
3461 $parent_node = $this->getNode();
3462 }
3463
3464 // count the parent children
3465 $parent_childs = $parent_node->child_nodes();
3466 $cnt_parent_childs = count($parent_childs);
3467 //echo "ZZ$a_mode";
3468 switch ($a_mode) {
3469 // insert new node after sibling at $a_pos
3470 case IL_INSERT_AFTER:
3471 $new_node = $a_cont_obj->getNode();
3472 //$a_pos = ilPageContent::incEdId($a_pos);
3473 //$curr_node = $this->getContentNode($a_pos);
3474 //echo "behind $a_pos:";
3475 if ($succ_node = $curr_node->next_sibling()) {
3476 $new_node = $succ_node->insert_before($new_node, $succ_node);
3477 } else {
3478 //echo "movin doin append_child";
3479 $new_node = $parent_node->append_child($new_node);
3480 }
3481 $a_cont_obj->setNode($new_node);
3482 break;
3483
3484 case IL_INSERT_BEFORE:
3485 //echo "INSERT_BEF";
3486 $new_node = $a_cont_obj->getNode();
3487 $succ_node = $this->getContentNode($a_pos);
3488 $new_node = $succ_node->insert_before($new_node, $succ_node);
3489 $a_cont_obj->setNode($new_node);
3490 break;
3491
3492 // insert new node as first child of parent $a_pos (= $a_parent)
3493 case IL_INSERT_CHILD:
3494 //echo "insert as child:parent_childs:$cnt_parent_childs:<br>";
3495 $new_node = $a_cont_obj->getNode();
3496 if ($cnt_parent_childs == 0) {
3497 $new_node = $parent_node->append_child($new_node);
3498 } else {
3499 $new_node = $parent_childs[0]->insert_before($new_node, $parent_childs[0]);
3500 }
3501 $a_cont_obj->setNode($new_node);
3502 //echo "PP";
3503 break;
3504 }
3505
3506 //check for PlaceHolder to remove in EditMode-keep in Layout Mode
3507 if ($remove_placeholder && !$this->getPageConfig()->getEnablePCType("PlaceHolder")) {
3508 $sub_nodes = $curr_node->child_nodes();
3509 foreach ($sub_nodes as $sub_node) {
3510 if ($sub_node->node_name() == "PlaceHolder") {
3511 $curr_node->unlink_node();
3512 }
3513 }
3514 }
3515 }
3516
3520 public function insertContentNode(
3521 php4DOMElement $a_cont_node,
3522 string $a_pos,
3523 int $a_mode = IL_INSERT_AFTER,
3524 string $a_pcid = ""
3525 ): void {
3526 // move mode into container elements is always INSERT_CHILD
3527 $curr_node = $this->getContentNode($a_pos, $a_pcid);
3528 $curr_name = $curr_node->node_name();
3529 // @todo: try to generalize
3530 if (($curr_name == "TableData") || ($curr_name == "PageObject") ||
3531 ($curr_name == "ListItem") || ($curr_name == "Section")
3532 || ($curr_name == "Tab") || ($curr_name == "ContentPopup")
3533 || ($curr_name == "GridCell")) {
3534 $a_mode = IL_INSERT_CHILD;
3535 }
3536
3537 $hid = $curr_node->get_attribute("HierId");
3538 if ($hid != "") {
3539 $a_pos = $hid;
3540 }
3541
3542 if ($a_mode != IL_INSERT_CHILD) { // determine parent hierarchical id
3543 // of sibling at $a_pos
3544 $pos = explode("_", $a_pos);
3545 $target_pos = array_pop($pos);
3546 $parent_pos = implode("_", $pos);
3547 } else { // if we should insert a child, $a_pos is alreade the hierarchical id
3548 // of the parent node
3549 $parent_pos = $a_pos;
3550 }
3551
3552 // get the parent node
3553 if ($parent_pos != "") {
3554 $parent_node = $this->getContentNode($parent_pos);
3555 } else {
3556 $parent_node = $this->getNode();
3557 }
3558
3559 // count the parent children
3560 $parent_childs = $parent_node->child_nodes();
3561 $cnt_parent_childs = count($parent_childs);
3562 switch ($a_mode) {
3563 // insert new node after sibling at $a_pos
3564 case IL_INSERT_AFTER:
3565 //$new_node = $a_cont_obj->getNode();
3566 if ($succ_node = $curr_node->next_sibling()) {
3567 $a_cont_node = $succ_node->insert_before($a_cont_node, $succ_node);
3568 } else {
3569 $a_cont_node = $parent_node->append_child($a_cont_node);
3570 }
3571 //$a_cont_obj->setNode($new_node);
3572 break;
3573
3574 case IL_INSERT_BEFORE:
3575 //$new_node = $a_cont_obj->getNode();
3576 $succ_node = $this->getContentNode($a_pos);
3577 $a_cont_node = $succ_node->insert_before($a_cont_node, $succ_node);
3578 //$a_cont_obj->setNode($new_node);
3579 break;
3580
3581 // insert new node as first child of parent $a_pos (= $a_parent)
3582 case IL_INSERT_CHILD:
3583 //$new_node = $a_cont_obj->getNode();
3584 if ($cnt_parent_childs == 0) {
3585 $a_cont_node = $parent_node->append_child($a_cont_node);
3586 } else {
3587 $a_cont_node = $parent_childs[0]->insert_before($a_cont_node, $parent_childs[0]);
3588 }
3589 //$a_cont_obj->setNode($new_node);
3590 break;
3591 }
3592 }
3593
3606 public function moveContentBefore(
3607 string $a_source,
3608 string $a_target,
3609 string $a_spcid = "",
3610 string $a_tpcid = ""
3611 ) {
3612 if ($a_source == $a_target) {
3613 return false;
3614 }
3615
3616 // clone the node
3617 $content = $this->getContentObject($a_source, $a_spcid);
3618 $source_node = $content->getNode();
3619 $clone_node = $source_node->clone_node(true);
3620
3621 // delete source node
3622 $this->deleteContent($a_source, false, $a_spcid, true);
3623
3624 // insert cloned node at target
3625 $content->setNode($clone_node);
3626 $this->insertContent($content, $a_target, IL_INSERT_BEFORE, $a_tpcid);
3627 return $this->update();
3628 }
3629
3642 public function moveContentAfter(
3643 string $a_source,
3644 string $a_target,
3645 string $a_spcid = "",
3646 string $a_tpcid = ""
3647 ) {
3648 // nothing to do...
3649 if ($a_source === $a_target) {
3650 return true;
3651 }
3652
3653 // clone the node
3654 $content = $this->getContentObject($a_source, $a_spcid);
3655 $source_node = $content->getNode();
3656 $clone_node = $source_node->clone_node(true);
3657
3658 // delete source node
3659 $this->deleteContent($a_source, false, $a_spcid, true);
3660
3661 // insert cloned node at target
3662 $content->setNode($clone_node);
3663 $this->insertContent($content, $a_target, IL_INSERT_AFTER, $a_tpcid);
3664 return $this->update();
3665 }
3666
3671 public function bbCode2XML(string &$a_content): void
3672 {
3673 $a_content = preg_replace('/\[com\]/i', "<Comment>", $a_content);
3674 $a_content = preg_replace('/\[\/com\]/i', "</Comment>", $a_content);
3675 $a_content = preg_replace('/\[emp]/i', "<Emph>", $a_content);
3676 $a_content = preg_replace('/\[\/emp\]/i', "</Emph>", $a_content);
3677 $a_content = preg_replace('/\[str]/i', "<Strong>", $a_content);
3678 $a_content = preg_replace('/\[\/str\]/i', "</Strong>", $a_content);
3679 }
3680
3687 public function insertInstIntoIDs(
3688 string $a_inst,
3689 bool $a_res_ref_to_obj_id = true
3690 ): void {
3691 // insert inst id into internal links
3692 $xpc = xpath_new_context($this->dom);
3693 $path = "//IntLink";
3694 $res = xpath_eval($xpc, $path);
3695 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
3696 $target = $res->nodeset[$i]->get_attribute("Target");
3697 $type = $res->nodeset[$i]->get_attribute("Type");
3698
3699 if (substr($target, 0, 4) == "il__") {
3700 $id = substr($target, 4, strlen($target) - 4);
3701
3702 // convert repository links obj_<ref_id> to <type>_<obj_id>
3703 // this leads to bug 6685.
3704 if ($a_res_ref_to_obj_id && $type == "RepositoryItem") {
3705 $id_arr = explode("_", $id);
3706
3707 // changed due to bug 6685
3708 $ref_id = $id_arr[1];
3709 $obj_id = ilObject::_lookupObjId($id_arr[1]);
3710
3711 $otype = ilObject::_lookupType($obj_id);
3712 if ($obj_id > 0) {
3713 // changed due to bug 6685
3714 // the ref_id should be used, if the content is
3715 // imported on the same installation
3716 // the obj_id should be used, if a different
3717 // installation imports, but has an import_id for
3718 // the object id.
3719 $id = $otype . "_" . $obj_id . "_" . $ref_id;
3720 //$id = $otype."_".$ref_id;
3721 }
3722 }
3723 $new_target = "il_" . $a_inst . "_" . $id;
3724 $res->nodeset[$i]->set_attribute("Target", $new_target);
3725 }
3726 }
3727 unset($xpc);
3728
3729 // @todo: move to media/fileitems/questions, ...
3730
3731 // insert inst id into media aliases
3732 $xpc = xpath_new_context($this->dom);
3733 $path = "//MediaAlias";
3734 $res = xpath_eval($xpc, $path);
3735 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
3736 $origin_id = $res->nodeset[$i]->get_attribute("OriginId");
3737 if (substr($origin_id, 0, 4) == "il__") {
3738 $new_id = "il_" . $a_inst . "_" . substr($origin_id, 4, strlen($origin_id) - 4);
3739 $res->nodeset[$i]->set_attribute("OriginId", $new_id);
3740 }
3741 }
3742 unset($xpc);
3743
3744 // insert inst id file item identifier entries
3745 $xpc = xpath_new_context($this->dom);
3746 $path = "//FileItem/Identifier";
3747 $res = xpath_eval($xpc, $path);
3748 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
3749 $origin_id = $res->nodeset[$i]->get_attribute("Entry");
3750 if (substr($origin_id, 0, 4) == "il__") {
3751 $new_id = "il_" . $a_inst . "_" . substr($origin_id, 4, strlen($origin_id) - 4);
3752 $res->nodeset[$i]->set_attribute("Entry", $new_id);
3753 }
3754 }
3755 unset($xpc);
3756
3757 // insert inst id into question references
3758 $xpc = xpath_new_context($this->dom);
3759 $path = "//Question";
3760 $res = xpath_eval($xpc, $path);
3761 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
3762 $qref = $res->nodeset[$i]->get_attribute("QRef");
3763 //echo "<br>setted:".$qref;
3764 if (substr($qref, 0, 4) == "il__") {
3765 $new_id = "il_" . $a_inst . "_" . substr($qref, 4, strlen($qref) - 4);
3766 //echo "<br>setting:".$new_id;
3767 $res->nodeset[$i]->set_attribute("QRef", $new_id);
3768 }
3769 }
3770 unset($xpc);
3771
3772 // insert inst id into content snippets
3773 $xpc = xpath_new_context($this->dom);
3774 $path = "//ContentInclude";
3775 $res = xpath_eval($xpc, $path);
3776 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
3777 $ci = $res->nodeset[$i]->get_attribute("InstId");
3778 if ($ci == "") {
3779 $res->nodeset[$i]->set_attribute("InstId", $a_inst);
3780 }
3781 }
3782 unset($xpc);
3783 }
3784
3788 public function checkPCIds(): bool
3789 {
3790 $this->buildDom();
3791 $mydom = $this->dom;
3792
3793 $sep = $path = "";
3794 foreach ($this->id_elements as $el) {
3795 $path .= $sep . "//" . $el . "[not(@PCID)]";
3796 $sep = " | ";
3797 $path .= $sep . "//" . $el . "[@PCID='']";
3798 }
3799
3800 $xpc = xpath_new_context($mydom);
3801 $res = xpath_eval($xpc, $path);
3802
3803 if (count($res->nodeset) > 0) {
3804 return false;
3805 }
3806 return true;
3807 }
3808
3812 public function getAllPCIds(): array
3813 {
3814 $this->buildDom();
3815 $mydom = $this->dom;
3816
3817 $pcids = array();
3818
3819 $sep = $path = "";
3820 foreach ($this->id_elements as $el) {
3821 $path .= $sep . "//" . $el . "[@PCID]";
3822 $sep = " | ";
3823 }
3824
3825 // get existing ids
3826 $xpc = xpath_new_context($mydom);
3827 $res = xpath_eval($xpc, $path);
3828
3829 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
3830 $node = $res->nodeset[$i];
3831 $pcids[] = $node->get_attribute("PCID");
3832 }
3833 return $pcids;
3834 }
3835
3836 public function hasDuplicatePCIds(): bool
3837 {
3838 $duplicates = $this->getDuplicatePCIds();
3839 return count($duplicates) > 0;
3840 }
3841
3846 public function getDuplicatePCIds(): array
3847 {
3848 $this->buildDom();
3849 $mydom = $this->dom;
3850
3851 $pcids = [];
3852 $duplicates = [];
3853
3854 $sep = $path = "";
3855 foreach ($this->id_elements as $el) {
3856 $path .= $sep . "//" . $el . "[@PCID]";
3857 $sep = " | ";
3858 }
3859
3860 // get existing ids
3861 $xpc = xpath_new_context($mydom);
3862 $res = xpath_eval($xpc, $path);
3863
3864 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
3865 $node = $res->nodeset[$i];
3866 $pc_id = $node->get_attribute("PCID");
3867 if ($pc_id != "") {
3868 if (isset($pcids[$pc_id])) {
3869 $duplicates[] = $pc_id;
3870 }
3871 $pcids[$pc_id] = $pc_id;
3872 }
3873 }
3874 return $duplicates;
3875 }
3876
3877 public function existsPCId(string $a_pc_id): bool
3878 {
3879 $this->buildDom();
3880 $mydom = $this->dom;
3881
3882 $sep = $path = "";
3883 foreach ($this->id_elements as $el) {
3884 $path .= $sep . "//" . $el . "[@PCID='" . $a_pc_id . "']";
3885 $sep = " | ";
3886 }
3887
3888 // get existing ids
3889 $xpc = xpath_new_context($mydom);
3890 $res = xpath_eval($xpc, $path);
3891 return (count($res->nodeset) > 0);
3892 }
3893
3894 public function generatePcId(): string
3895 {
3896 $id = self::randomhash();
3897 return $id;
3898 }
3899
3903 public function insertPCIds(): void
3904 {
3905 $this->buildDom();
3906 $mydom = $this->dom;
3907
3908 // add missing ones
3909 $sep = $path = "";
3910 foreach ($this->id_elements as $el) {
3911 $path .= $sep . "//" . $el . "[not(@PCID)]";
3912 $sep = " | ";
3913 $path .= $sep . "//" . $el . "[@PCID='']";
3914 $sep = " | ";
3915 }
3916 $xpc = xpath_new_context($mydom);
3917 $res = xpath_eval($xpc, $path);
3918
3919 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
3920 $id = self::randomhash();
3921 $res->nodeset[$i]->set_attribute("PCID", $id);
3922 }
3923 }
3924
3928 public function getPageContentsHashes(): array
3929 {
3930 $this->buildDom();
3931 $this->addHierIDs();
3932 $mydom = $this->dom;
3933
3934 // get existing ids
3935 $path = "//PageContent";
3936 $xpc = xpath_new_context($mydom);
3937 $res = xpath_eval($xpc, $path);
3938
3939 $hashes = array();
3940 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
3941 $hier_id = $res->nodeset[$i]->get_attribute("HierId");
3942 $pc_id = $res->nodeset[$i]->get_attribute("PCID");
3943 $dump = $mydom->dump_node($res->nodeset[$i]);
3944 if (($hpos = strpos($dump, ' HierId="' . $hier_id . '"')) > 0) {
3945 $dump = substr($dump, 0, $hpos) .
3946 substr($dump, $hpos + strlen(' HierId="' . $hier_id . '"'));
3947 }
3948
3949 $childs = $res->nodeset[$i]->child_nodes();
3950 $content = "";
3951 if ($childs[0] && $childs[0]->node_name() == "Paragraph") {
3952 $content = $mydom->dump_node($childs[0]);
3953 $content = substr(
3954 $content,
3955 strpos($content, ">") + 1,
3956 strrpos($content, "<") - (strpos($content, ">") + 1)
3957 );
3958 $content = ilPCParagraph::xml2output($content);
3959 }
3960 $hashes[$pc_id] =
3961 array("hier_id" => $hier_id, "hash" => md5($dump), "content" => $content);
3962 }
3963
3964 return $hashes;
3965 }
3966
3971 public function getQuestionIds(): array
3972 {
3973 $this->buildDom();
3974 $mydom = $this->dom;
3975
3976 // Get question IDs
3977 $path = "//Question";
3978 $xpc = xpath_new_context($mydom);
3979 $res = xpath_eval($xpc, $path);
3980
3981 $q_ids = array();
3982 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
3983 $qref = $res->nodeset[$i]->get_attribute("QRef");
3984 $inst_id = ilInternalLink::_extractInstOfTarget($qref);
3986
3987 if (!($inst_id > 0)) {
3988 if ($obj_id > 0) {
3989 $q_ids[] = $obj_id;
3990 }
3991 }
3992 }
3993 return $q_ids;
3994 }
3995
3996 // @todo: move to paragraph
3997 public function send_paragraph(
3998 string $par_id,
3999 string $filename
4000 ): void {
4001 $this->buildDom();
4002 $content = "";
4003
4004 $mydom = $this->dom;
4005
4006 $xpc = xpath_new_context($mydom);
4007 $path = "/descendant::Paragraph[position() = $par_id]";
4008
4009 $res = xpath_eval($xpc, $path);
4010
4011 if (count($res->nodeset) != 1) {
4012 die("Should not happen");
4013 }
4014
4015 $context_node = $res->nodeset[0];
4016
4017 // get plain text
4018
4019 $childs = $context_node->child_nodes();
4020
4021 for ($j = 0, $jMax = count($childs); $j < $jMax; $j++) {
4022 $content .= $mydom->dump_node($childs[$j]);
4023 }
4024
4025 $content = str_replace("<br />", "\n", $content);
4026 $content = str_replace("<br/>", "\n", $content);
4027
4028 $plain_content = html_entity_decode($content);
4029
4030 ilUtil::deliverData($plain_content, $filename);
4031 exit();
4032 }
4033
4038 public function getFO(): string
4039 {
4040 $xml = $this->getXMLFromDom(false, true, true);
4041 $xsl = file_get_contents("./Services/COPage/xsl/page_fo.xsl");
4042 $args = array('/_xml' => $xml, '/_xsl' => $xsl);
4043 $xh = xslt_create();
4044
4045 $params = array();
4046
4047 $fo = xslt_process($xh, "arg:/_xml", "arg:/_xsl", null, $args, $params);
4048 var_dump($fo);
4049 // do some replacements
4050 $fo = str_replace("\n", "", $fo);
4051 $fo = str_replace("<br/>", "<br>", $fo);
4052 $fo = str_replace("<br>", "\n", $fo);
4053
4054 xslt_free($xh);
4055
4056 //
4057 $fo = substr($fo, strpos($fo, ">") + 1);
4058 //echo "<br><b>fo:</b><br>".htmlentities($fo); flush();
4059 return $fo;
4060 }
4061
4062 public function registerOfflineHandler(object $handler): void
4063 {
4064 $this->offline_handler = $handler;
4065 }
4066
4067 public function getOfflineHandler(): ?object
4068 {
4069 return $this->offline_handler;
4070 }
4071
4076 int $a_id,
4077 string $a_parent_type,
4078 string $a_lang = "-"
4079 ): bool {
4080 global $DIC;
4081
4082 $db = $DIC->database();
4083
4084 if ($a_lang == "") {
4085 $a_lang = "-";
4086 }
4087
4088 $query = "SELECT * FROM page_object WHERE page_id = " .
4089 $db->quote($a_id, "integer") . " AND " .
4090 " parent_type = " . $db->quote($a_parent_type, "text") . " AND " .
4091 " lang = " . $db->quote($a_lang, "text") . " AND " .
4092 " inactive_elements = " . $db->quote(1, "integer");
4093 $obj_set = $db->query($query);
4094
4095 if ($obj_rec = $obj_set->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
4096 return true;
4097 }
4098
4099 return false;
4100 }
4101
4105 public function containsDeactivatedElements(string $a_content): bool
4106 {
4107 if (strpos($a_content, " Enabled=\"False\"")) {
4108 return true;
4109 }
4110 return false;
4111 }
4112
4116 public function getHistoryEntries(): array
4117 {
4118 $db = $this->db;
4119
4120 $h_query = "SELECT * FROM page_history " .
4121 " WHERE page_id = " . $db->quote($this->getId(), "integer") .
4122 " AND parent_type = " . $db->quote($this->getParentType(), "text") .
4123 " AND lang = " . $db->quote($this->getLanguage(), "text") .
4124 " ORDER BY hdate DESC";
4125
4126 $hset = $db->query($h_query);
4127 $hentries = array();
4128
4129 while ($hrec = $db->fetchAssoc($hset)) {
4130 $hrec["sortkey"] = (int) $hrec["nr"];
4131 $hrec["user"] = (int) $hrec["user_id"];
4132 $hentries[] = $hrec;
4133 }
4134 //var_dump($hentries);
4135 return $hentries;
4136 }
4137
4141 public function getHistoryEntry(int $a_old_nr): ?array
4142 {
4143 $db = $this->db;
4144
4145 $res = $db->queryF(
4146 "SELECT * FROM page_history " .
4147 " WHERE page_id = %s " .
4148 " AND parent_type = %s " .
4149 " AND nr = %s" .
4150 " AND lang = %s",
4151 array("integer", "text", "integer", "text"),
4152 array($this->getId(), $this->getParentType(), $a_old_nr, $this->getLanguage())
4153 );
4154 if ($hrec = $db->fetchAssoc($res)) {
4155 return $hrec;
4156 }
4157
4158 return null;
4159 }
4160
4166 public function getHistoryInfo(int $a_nr): array
4167 {
4168 $db = $this->db;
4169
4170 // determine previous entry
4171 $and_nr = ($a_nr > 0)
4172 ? " AND nr < " . $db->quote($a_nr, "integer")
4173 : "";
4174 $res = $db->query("SELECT MAX(nr) mnr FROM page_history " .
4175 " WHERE page_id = " . $db->quote($this->getId(), "integer") .
4176 " AND parent_type = " . $db->quote($this->getParentType(), "text") .
4177 " AND lang = " . $db->quote($this->getLanguage(), "text") .
4178 $and_nr);
4179 $row = $db->fetchAssoc($res);
4180 if ($row["mnr"] > 0) {
4181 $res = $db->query("SELECT * FROM page_history " .
4182 " WHERE page_id = " . $db->quote($this->getId(), "integer") .
4183 " AND parent_type = " . $db->quote($this->getParentType(), "text") .
4184 " AND lang = " . $db->quote($this->getLanguage(), "text") .
4185 " AND nr = " . $db->quote((int) $row["mnr"], "integer"));
4186 $row = $db->fetchAssoc($res);
4187 $ret["previous"] = $row;
4188 }
4189
4190 // determine next entry
4191 $res = $db->query("SELECT MIN(nr) mnr FROM page_history " .
4192 " WHERE page_id = " . $db->quote($this->getId(), "integer") .
4193 " AND parent_type = " . $db->quote($this->getParentType(), "text") .
4194 " AND lang = " . $db->quote($this->getLanguage(), "text") .
4195 " AND nr > " . $db->quote($a_nr, "integer"));
4196 $row = $db->fetchAssoc($res);
4197 if ($row["mnr"] > 0) {
4198 $res = $db->query("SELECT * FROM page_history " .
4199 " WHERE page_id = " . $db->quote($this->getId(), "integer") .
4200 " AND parent_type = " . $db->quote($this->getParentType(), "text") .
4201 " AND lang = " . $db->quote($this->getLanguage(), "text") .
4202 " AND nr = " . $db->quote((int) $row["mnr"], "integer"));
4203 $row = $db->fetchAssoc($res);
4204 $ret["next"] = $row;
4205 }
4206
4207 // current
4208 if ($a_nr > 0) {
4209 $res = $db->query("SELECT * FROM page_history " .
4210 " WHERE page_id = " . $db->quote($this->getId(), "integer") .
4211 " AND parent_type = " . $db->quote($this->getParentType(), "text") .
4212 " AND lang = " . $db->quote($this->getLanguage(), "text") .
4213 " AND nr = " . $db->quote($a_nr, "integer"));
4214 } else {
4215 $res = $db->query("SELECT page_id, last_change hdate, parent_type, parent_id, last_change_user user_id, content, lang FROM page_object " .
4216 " WHERE page_id = " . $db->quote($this->getId(), "integer") .
4217 " AND parent_type = " . $db->quote($this->getParentType(), "text") .
4218 " AND lang = " . $db->quote($this->getLanguage(), "text"));
4219 }
4220 $row = $db->fetchAssoc($res);
4221 $ret["current"] = $row;
4222
4223 return $ret;
4224 }
4225
4226 public function addChangeDivClasses(array $a_hashes): void
4227 {
4228 $xpc = xpath_new_context($this->dom);
4229 $path = "/*[1]";
4230 $res = xpath_eval($xpc, $path);
4231 $rnode = $res->nodeset[0];
4232
4233 foreach ($a_hashes as $h) {
4234 if (($h["change"] ?? "") != "") {
4235 $dc_node = $this->dom->create_element("DivClass");
4236 $dc_node->set_attribute("HierId", $h["hier_id"]);
4237 $dc_node->set_attribute("Class", "ilEdit" . $h["change"]);
4238 $dc_node = $rnode->append_child($dc_node);
4239 }
4240 }
4241 }
4242
4248 public function compareVersion(
4249 int $a_left,
4250 int $a_right
4251 ): array {
4252 // get page objects
4253 $l_page = ilPageObjectFactory::getInstance($this->getParentType(), $this->getId(), $a_left, $this->getLanguage());
4254 $r_page = ilPageObjectFactory::getInstance($this->getParentType(), $this->getId(), $a_right, $this->getLanguage());
4255 $this->preparePageForCompare($l_page);
4256 $this->preparePageForCompare($r_page);
4257 $l_hashes = $l_page->getPageContentsHashes();
4258 $r_hashes = $r_page->getPageContentsHashes();
4259 // determine all deleted and changed page elements
4260 foreach ($l_hashes as $pc_id => $h) {
4261 if (!isset($r_hashes[$pc_id])) {
4262 $l_hashes[$pc_id]["change"] = "Deleted";
4263 } else {
4264 if ($h["hash"] != $r_hashes[$pc_id]["hash"]) {
4265 $l_hashes[$pc_id]["change"] = "Modified";
4266 $r_hashes[$pc_id]["change"] = "Modified";
4267
4268 // if modified element is a paragraph, highlight changes
4269 if ($l_hashes[$pc_id]["content"] != "" &&
4270 $r_hashes[$pc_id]["content"] != "") {
4271 $new_left = str_replace("\n", "<br />", $l_hashes[$pc_id]["content"]);
4272 $new_right = str_replace("\n", "<br />", $r_hashes[$pc_id]["content"]);
4273 $wldiff = new WordLevelDiff(
4274 array($new_left),
4275 array($new_right)
4276 );
4277 $new_left = $wldiff->orig();
4278 $new_right = $wldiff->closing();
4279 $l_page->setParagraphContent($l_hashes[$pc_id]["hier_id"], $new_left[0]);
4280 $r_page->setParagraphContent($l_hashes[$pc_id]["hier_id"], $new_right[0]);
4281 }
4282 }
4283 }
4284 }
4285
4286 // determine all new paragraphs
4287 foreach ($r_hashes as $pc_id => $h) {
4288 if (!isset($l_hashes[$pc_id])) {
4289 $r_hashes[$pc_id]["change"] = "New";
4290 }
4291 }
4292 $l_page->addChangeDivClasses($l_hashes);
4293 $r_page->addChangeDivClasses($r_hashes);
4294
4295 return array("l_page" => $l_page,
4296 "r_page" => $r_page,
4297 "l_changes" => $l_hashes,
4298 "r_changes" => $r_hashes
4299 );
4300 }
4301
4302 protected function preparePageForCompare(ilPageObject $page): void
4303 {
4304 }
4305
4309 public function increaseViewCnt(): void
4310 {
4311 $db = $this->db;
4312
4313 $db->manipulate("UPDATE page_object " .
4314 " SET view_cnt = view_cnt + 1 " .
4315 " WHERE page_id = " . $db->quote($this->getId(), "integer") .
4316 " AND parent_type = " . $db->quote($this->getParentType(), "text") .
4317 " AND lang = " . $db->quote($this->getLanguage(), "text"));
4318 }
4319
4326 public static function getRecentChanges(
4327 string $a_parent_type,
4328 int $a_parent_id,
4329 int $a_period = 30,
4330 string $a_lang = ""
4331 ): array {
4332 global $DIC;
4333
4334 $db = $DIC->database();
4335
4336 $and_lang = "";
4337 if ($a_lang != "") {
4338 $and_lang = " AND lang = " . $db->quote($a_lang, "text");
4339 }
4340
4341 $page_changes = array();
4342 $limit_ts = date('Y-m-d H:i:s', time() - ($a_period * 24 * 60 * 60));
4343 $q = "SELECT * FROM page_object " .
4344 " WHERE parent_id = " . $db->quote($a_parent_id, "integer") .
4345 " AND parent_type = " . $db->quote($a_parent_type, "text") .
4346 " AND last_change >= " . $db->quote($limit_ts, "timestamp") . $and_lang;
4347 // " AND (TO_DAYS(now()) - TO_DAYS(last_change)) <= ".((int)$a_period);
4348 $set = $db->query($q);
4349 while ($page = $db->fetchAssoc($set)) {
4350 $page_changes[] = array(
4351 "date" => $page["last_change"],
4352 "id" => $page["page_id"],
4353 "lang" => $page["lang"],
4354 "type" => "page",
4355 "user" => $page["last_change_user"]
4356 );
4357 }
4358
4359 $and_str = "";
4360 if ($a_period > 0) {
4361 $limit_ts = date('Y-m-d H:i:s', time() - ($a_period * 24 * 60 * 60));
4362 $and_str = " AND hdate >= " . $db->quote($limit_ts, "timestamp") . " ";
4363 }
4364
4365 $q = "SELECT * FROM page_history " .
4366 " WHERE parent_id = " . $db->quote($a_parent_id, "integer") .
4367 " AND parent_type = " . $db->quote($a_parent_type, "text") .
4368 $and_str . $and_lang;
4369 $set = $db->query($q);
4370 while ($page = $db->fetchAssoc($set)) {
4371 $page_changes[] = array(
4372 "date" => $page["hdate"],
4373 "id" => $page["page_id"],
4374 "lang" => $page["lang"],
4375 "type" => "hist",
4376 "nr" => $page["nr"],
4377 "user" => $page["user_id"]
4378 );
4379 }
4380
4381 $page_changes = ilArrayUtil::sortArray($page_changes, "date", "desc");
4382
4383 return $page_changes;
4384 }
4385
4389 public static function getAllPages(
4390 string $a_parent_type,
4391 int $a_parent_id,
4392 string $a_lang = "-"
4393 ): array {
4394 global $DIC;
4395
4396 $db = $DIC->database();
4397
4398 $and_lang = "";
4399 if ($a_lang != "") {
4400 $and_lang = " AND lang = " . $db->quote($a_lang, "text");
4401 }
4402
4403 $q = "SELECT * FROM page_object " .
4404 " WHERE parent_id = " . $db->quote($a_parent_id, "integer") .
4405 " AND parent_type = " . $db->quote($a_parent_type, "text") . $and_lang;
4406 $set = $db->query($q);
4407 $pages = array();
4408 while ($page = $db->fetchAssoc($set)) {
4409 $key_add = ($a_lang == "")
4410 ? ":" . $page["lang"]
4411 : "";
4412 $pages[$page["page_id"] . $key_add] = array(
4413 "date" => $page["last_change"],
4414 "id" => $page["page_id"],
4415 "lang" => $page["lang"],
4416 "user" => $page["last_change_user"]
4417 );
4418 }
4419
4420 return $pages;
4421 }
4422
4426 public static function getNewPages(
4427 string $a_parent_type,
4428 int $a_parent_id,
4429 string $a_lang = "-"
4430 ): array {
4431 global $DIC;
4432
4433 $db = $DIC->database();
4434
4435 $and_lang = "";
4436 if ($a_lang != "") {
4437 $and_lang = " AND lang = " . $db->quote($a_lang, "text");
4438 }
4439
4440 $pages = array();
4441
4442 $q = "SELECT * FROM page_object " .
4443 " WHERE parent_id = " . $db->quote($a_parent_id, "integer") .
4444 " AND parent_type = " . $db->quote($a_parent_type, "text") . $and_lang .
4445 " ORDER BY created DESC";
4446 $set = $db->query($q);
4447 while ($page = $db->fetchAssoc($set)) {
4448 if ($page["created"] != "") {
4449 $pages[] = array(
4450 "created" => $page["created"],
4451 "id" => $page["page_id"],
4452 "lang" => $page["lang"],
4453 "user" => $page["create_user"],
4454 );
4455 }
4456 }
4457
4458 return $pages;
4459 }
4460
4466 public static function getParentObjectContributors(
4467 string $a_parent_type,
4468 int $a_parent_id,
4469 string $a_lang = "-"
4470 ): array {
4471 global $DIC;
4472
4473 $db = $DIC->database();
4474
4475 $and_lang = "";
4476 if ($a_lang != "") {
4477 $and_lang = " AND lang = " . $db->quote($a_lang, "text");
4478 }
4479
4480 $contributors = array();
4481 $set = $db->queryF(
4482 "SELECT last_change_user, lang, page_id FROM page_object " .
4483 " WHERE parent_id = %s AND parent_type = %s " .
4484 " AND last_change_user != %s" . $and_lang,
4485 array("integer", "text", "integer"),
4486 array($a_parent_id, $a_parent_type, 0)
4487 );
4488
4489 while ($page = $db->fetchAssoc($set)) {
4490 if ($a_lang == "") {
4491 $contributors[$page["last_change_user"]][$page["page_id"]][$page["lang"]] = 1;
4492 } else {
4493 $contributors[$page["last_change_user"]][$page["page_id"]] = 1;
4494 }
4495 }
4496
4497 $set = $db->queryF(
4498 "SELECT count(*) as cnt, lang, page_id, user_id FROM page_history " .
4499 " WHERE parent_id = %s AND parent_type = %s AND user_id != %s " . $and_lang .
4500 " GROUP BY page_id, user_id, lang ",
4501 array("integer", "text", "integer"),
4502 array($a_parent_id, $a_parent_type, 0)
4503 );
4504 while ($hpage = $db->fetchAssoc($set)) {
4505 if ($a_lang == "") {
4506 $contributors[$hpage["user_id"]][$hpage["page_id"]][$hpage["lang"]] =
4507 ($contributors[$hpage["user_id"]][$hpage["page_id"]][$hpage["lang"]] ?? 0) + $hpage["cnt"];
4508 } else {
4509 $contributors[$hpage["user_id"]][$hpage["page_id"]] =
4510 ($contributors[$hpage["user_id"]][$hpage["page_id"]] ?? 0) + $hpage["cnt"];
4511 }
4512 }
4513
4514 $c = array();
4515 foreach ($contributors as $k => $co) {
4516 if (ilObject::_lookupType($k) == "usr") {
4518 $c[] = array("user_id" => $k,
4519 "pages" => $co,
4520 "lastname" => $name["lastname"],
4521 "firstname" => $name["firstname"]
4522 );
4523 }
4524 }
4525
4526 return $c;
4527 }
4528
4532 public static function getPageContributors(
4533 string $a_parent_type,
4534 int $a_page_id,
4535 string $a_lang = "-"
4536 ): array {
4537 global $DIC;
4538
4539 $db = $DIC->database();
4540
4541 $and_lang = "";
4542 if ($a_lang != "") {
4543 $and_lang = " AND lang = " . $db->quote($a_lang, "text");
4544 }
4545
4546 $contributors = array();
4547 $set = $db->queryF(
4548 "SELECT last_change_user, lang FROM page_object " .
4549 " WHERE page_id = %s AND parent_type = %s " .
4550 " AND last_change_user != %s" . $and_lang,
4551 array("integer", "text", "integer"),
4552 array($a_page_id, $a_parent_type, 0)
4553 );
4554
4555 while ($page = $db->fetchAssoc($set)) {
4556 if ($a_lang == "") {
4557 $contributors[$page["last_change_user"]][$page["lang"]] = 1;
4558 } else {
4559 $contributors[$page["last_change_user"]] = 1;
4560 }
4561 }
4562
4563 $set = $db->queryF(
4564 "SELECT count(*) as cnt, lang, page_id, user_id FROM page_history " .
4565 " WHERE page_id = %s AND parent_type = %s AND user_id != %s " . $and_lang .
4566 " GROUP BY user_id, page_id, lang ",
4567 array("integer", "text", "integer"),
4568 array($a_page_id, $a_parent_type, 0)
4569 );
4570 while ($hpage = $db->fetchAssoc($set)) {
4571 if ($a_lang === "") {
4572 $contributors[$hpage["user_id"]][$page["lang"]] =
4573 ($contributors[$hpage["user_id"]][$page["lang"]] ?? 0) + $hpage["cnt"];
4574 } else {
4575 $contributors[$hpage["user_id"]] =
4576 ($contributors[$hpage["user_id"]] ?? 0) + $hpage["cnt"];
4577 }
4578 }
4579
4580 $c = array();
4581 foreach ($contributors as $k => $co) {
4583 $c[] = array("user_id" => $k,
4584 "pages" => $co,
4585 "lastname" => $name["lastname"],
4586 "firstname" => $name["firstname"]
4587 );
4588 }
4589
4590 return $c;
4591 }
4592
4596 public function writeRenderedContent(
4597 string $a_content,
4598 string $a_md5
4599 ): void {
4600 global $DIC;
4601
4602 $db = $DIC->database();
4603
4604 $db->update("page_object", array(
4605 "rendered_content" => array("clob", $a_content),
4606 "render_md5" => array("text", $a_md5),
4607 "rendered_time" => array("timestamp", ilUtil::now())
4608 ), array(
4609 "page_id" => array("integer", $this->getId()),
4610 "lang" => array("text", $this->getLanguage()),
4611 "parent_type" => array("text", $this->getParentType())
4612 ));
4613 }
4614
4618 public static function getPagesWithLinks(
4619 string $a_parent_type,
4620 int $a_parent_id,
4621 string $a_lang = "-"
4622 ): array {
4623 global $DIC;
4624
4625 $db = $DIC->database();
4626
4627 $and_lang = "";
4628 if ($a_lang != "") {
4629 $and_lang = " AND lang = " . $db->quote($a_lang, "text");
4630 }
4631
4632 $q = "SELECT * FROM page_object " .
4633 " WHERE parent_id = " . $db->quote($a_parent_id, "integer") .
4634 " AND parent_type = " . $db->quote($a_parent_type, "text") .
4635 " AND int_links = " . $db->quote(1, "integer") . $and_lang;
4636 $set = $db->query($q);
4637 $pages = array();
4638 while ($page = $db->fetchAssoc($set)) {
4639 $key_add = ($a_lang == "")
4640 ? ":" . $page["lang"]
4641 : "";
4642 $pages[$page["page_id"] . $key_add] = array(
4643 "date" => $page["last_change"],
4644 "id" => $page["page_id"],
4645 "lang" => $page["lang"],
4646 "user" => $page["last_change_user"]
4647 );
4648 }
4649
4650 return $pages;
4651 }
4652
4656 public function containsIntLinks(string $a_content): bool
4657 {
4658 if (strpos($a_content, "IntLink")) {
4659 return true;
4660 }
4661 return false;
4662 }
4663
4667 public function performAutomaticModifications(): void
4668 {
4669 }
4670
4676 string $a_type,
4677 int $a_id,
4678 string $a_target
4679 ): void {
4680 $this->buildDom();
4681 $il_node = null;
4682
4683 $link_type = "";
4684
4685 switch ($a_type) {
4686 case "media":
4687 $link_type = "MediaObject";
4688 $a_id = "il__mob_" . $a_id;
4689 break;
4690
4691 case "page":
4692 $link_type = "PageObject";
4693 $a_id = "il__pg_" . $a_id;
4694 break;
4695
4696 case "term":
4697 $link_type = "GlossaryItem";
4698 $a_id = "il__git_" . $a_id;
4699 $a_target = "Glossary";
4700 break;
4701 }
4702
4703 // if type or id missing -> delete InitOpenedContent, if existing
4704 $xpc = xpath_new_context($this->dom);
4705 $path = "//PageObject/InitOpenedContent";
4706 $res = xpath_eval($xpc, $path);
4707 if ($link_type == "" || $a_id == "") {
4708 if (count($res->nodeset) > 0) {
4709 $res->nodeset[0]->unlink_node($res->nodeset[0]);
4710 }
4711 } else {
4712 if (count($res->nodeset) > 0) {
4713 $init_node = $res->nodeset[0];
4714 $childs = $init_node->child_nodes();
4715 for ($i = 0, $iMax = count($childs); $i < $iMax; $i++) {
4716 if ($childs[$i]->node_name() == "IntLink") {
4717 $il_node = $childs[$i];
4718 }
4719 }
4720 } else {
4721 $path = "//PageObject";
4722 $res = xpath_eval($xpc, $path);
4723 $page_node = $res->nodeset[0];
4724 $init_node = $this->dom->create_element("InitOpenedContent");
4725 $init_node = $page_node->append_child($init_node);
4726 $il_node = $this->dom->create_element("IntLink");
4727 $il_node = $init_node->append_child($il_node);
4728 }
4729 $il_node->set_attribute("Target", $a_id);
4730 $il_node->set_attribute("Type", $link_type);
4731 $il_node->set_attribute("TargetFrame", $a_target);
4732 }
4733
4734 $this->update();
4735 }
4736
4740 public function getInitialOpenedContent(): array
4741 {
4742 $this->buildDom();
4743 $type = "";
4744
4745 $xpc = xpath_new_context($this->dom);
4746 $path = "//PageObject/InitOpenedContent";
4747 $res = xpath_eval($xpc, $path);
4748 $il_node = null;
4749 if (count($res->nodeset) > 0) {
4750 $init_node = $res->nodeset[0];
4751 $childs = $init_node->child_nodes();
4752 for ($i = 0, $iMax = count($childs); $i < $iMax; $i++) {
4753 if ($childs[$i]->node_name() == "IntLink") {
4754 $il_node = $childs[$i];
4755 }
4756 }
4757 }
4758 if (!is_null($il_node)) {
4759 $id = $il_node->get_attribute("Target");
4760 $link_type = $il_node->get_attribute("Type");
4761 $target = $il_node->get_attribute("TargetFrame");
4762
4763 switch ($link_type) {
4764 case "MediaObject":
4765 $type = "media";
4766 break;
4767
4768 case "PageObject":
4769 $type = "page";
4770 break;
4771
4772 case "GlossaryItem":
4773 $type = "term";
4774 break;
4775 }
4777 return array("id" => $id, "type" => $type, "target" => $target);
4778 }
4779
4780 return array();
4781 }
4782 // @todo end
4783
4790 public function beforePageContentUpdate(ilPageContent $a_page_content): void
4791 {
4792 }
4793
4802 public function copy(
4803 int $a_id,
4804 string $a_parent_type = "",
4805 int $a_new_parent_id = 0,
4806 bool $a_clone_mobs = false,
4807 int $obj_copy_id = 0
4808 ): void {
4809 if ($a_parent_type == "") {
4810 $a_parent_type = $this->getParentType();
4811 if ($a_new_parent_id == 0) {
4812 $a_new_parent_id = $this->getParentId();
4813 }
4814 }
4815
4816 foreach (self::lookupTranslations($this->getParentType(), $this->getId()) as $l) {
4817 $existed = false;
4818 $orig_page = ilPageObjectFactory::getInstance($this->getParentType(), $this->getId(), 0, $l);
4819 if (ilPageObject::_exists($a_parent_type, $a_id, $l)) {
4820 $new_page_object = ilPageObjectFactory::getInstance($a_parent_type, $a_id, 0, $l);
4821 $existed = true;
4822 } else {
4823 $new_page_object = ilPageObjectFactory::getInstance($a_parent_type, 0, 0, $l);
4824 $new_page_object->setParentId($a_new_parent_id);
4825 $new_page_object->setId($a_id);
4826 }
4827 $new_page_object->setXMLContent($orig_page->copyXMLContent($a_clone_mobs, $a_new_parent_id, $obj_copy_id));
4828 $new_page_object->setActive($orig_page->getActive());
4829 $new_page_object->setActivationStart($orig_page->getActivationStart());
4830 $new_page_object->setActivationEnd($orig_page->getActivationEnd());
4831 if ($existed) {
4832 $new_page_object->buildDom();
4833 $new_page_object->update();
4834 } else {
4835 $new_page_object->create(false);
4836 }
4837 }
4838 }
4839
4843 public static function lookupTranslations(
4844 string $a_parent_type,
4845 int $a_id
4846 ): array {
4847 global $DIC;
4848
4849 $db = $DIC->database();
4850
4851 $set = $db->query(
4852 "SELECT lang FROM page_object " .
4853 " WHERE page_id = " . $db->quote($a_id, "integer") .
4854 " AND parent_type = " . $db->quote($a_parent_type, "text")
4855 );
4856 $langs = array();
4857 while ($rec = $db->fetchAssoc($set)) {
4858 $langs[] = $rec["lang"];
4859 }
4860 return $langs;
4861 }
4862
4866 public function copyPageToTranslation(
4867 string $a_target_lang
4868 ): void {
4869 $transl_page = ilPageObjectFactory::getInstance(
4870 $this->getParentType(),
4871 0,
4872 0,
4873 $a_target_lang
4874 );
4875 $transl_page->setId($this->getId());
4876 $transl_page->setParentId($this->getParentId());
4877 $transl_page->setXMLContent($this->copyXmlContent());
4878 $transl_page->setActive($this->getActive());
4879 $transl_page->setActivationStart($this->getActivationStart());
4880 $transl_page->setActivationEnd($this->getActivationEnd());
4881 $transl_page->create(false);
4882 }
4883
4887
4891 public function getEditLock(): bool
4892 {
4893 $db = $this->db;
4894 $user = $this->user;
4895
4896 $min = $this->getEffectiveEditLockTime();
4897 if ($min > 0) {
4898 // try to set the lock for the user
4899 $ts = time();
4900 $db->manipulate(
4901 "UPDATE page_object SET " .
4902 " edit_lock_user = " . $db->quote($user->getId(), "integer") . "," .
4903 " edit_lock_ts = " . $db->quote($ts, "integer") .
4904 " WHERE (edit_lock_user = " . $db->quote($user->getId(), "integer") . " OR " .
4905 " edit_lock_ts < " . $db->quote(time() - ($min * 60), "integer") . ") " .
4906 " AND page_id = " . $db->quote($this->getId(), "integer") .
4907 " AND parent_type = " . $db->quote($this->getParentType(), "text")
4908 );
4909
4910 $set = $db->query(
4911 "SELECT edit_lock_user FROM page_object " .
4912 " WHERE page_id = " . $db->quote($this->getId(), "integer") .
4913 " AND parent_type = " . $db->quote($this->getParentType(), "text")
4914 );
4915 $rec = $db->fetchAssoc($set);
4916 if ($rec["edit_lock_user"] != $user->getId()) {
4917 return false;
4918 }
4919 }
4920
4921 return true;
4922 }
4923
4927 public function releasePageLock(): bool
4928 {
4929 $db = $this->db;
4930 $user = $this->user;
4931 $aset = new ilSetting("adve");
4932
4933 $min = (int) $aset->get("block_mode_minutes");
4934 if ($min > 0) {
4935 // try to set the lock for the user
4936 $ts = time();
4937 $db->manipulate(
4938 "UPDATE page_object SET " .
4939 " edit_lock_user = " . $db->quote($user->getId(), "integer") . "," .
4940 " edit_lock_ts = 0" .
4941 " WHERE edit_lock_user = " . $db->quote($user->getId(), "integer") .
4942 " AND page_id = " . $db->quote($this->getId(), "integer") .
4943 " AND parent_type = " . $db->quote($this->getParentType(), "text")
4944 );
4945
4946 $set = $db->query(
4947 "SELECT edit_lock_user FROM page_object " .
4948 " WHERE page_id = " . $db->quote($this->getId(), "integer") .
4949 " AND parent_type = " . $db->quote($this->getParentType(), "text")
4950 );
4951 $rec = $db->fetchAssoc($set);
4952 if ($rec["edit_lock_user"] != $user->getId()) {
4953 return false;
4954 }
4955 }
4956
4957 return true;
4958 }
4959
4963 public function getEditLockInfo(): array
4964 {
4965 $db = $this->db;
4966
4967 $aset = new ilSetting("adve");
4968 $min = (int) $aset->get("block_mode_minutes");
4969
4970 $set = $db->query(
4971 "SELECT edit_lock_user, edit_lock_ts FROM page_object " .
4972 " WHERE page_id = " . $db->quote($this->getId(), "integer") .
4973 " AND parent_type = " . $db->quote($this->getParentType(), "text")
4974 );
4975 $rec = $db->fetchAssoc($set);
4976 $rec["edit_lock_until"] = $rec["edit_lock_ts"] + $min * 60;
4977
4978 return $rec;
4979 }
4980
4985 public static function truncateHTML(
4986 string $a_text,
4987 int $a_length = 100,
4988 string $a_ending = '...',
4989 bool $a_exact = false,
4990 bool $a_consider_html = true
4991 ): string {
4992 $open_tags = [];
4993 if ($a_consider_html) {
4994 // if the plain text is shorter than the maximum length, return the whole text
4995 if (strlen(preg_replace('/<.*?>/', '', $a_text)) <= $a_length) {
4996 return $a_text;
4997 }
4998
4999 // splits all html-tags to scanable lines
5000 $total_length = strlen($a_ending);
5001 $open_tags = array();
5002 $truncate = '';
5003 preg_match_all('/(<.+?>)?([^<>]*)/s', $a_text, $lines, PREG_SET_ORDER);
5004 foreach ($lines as $line_matchings) {
5005 // if there is any html-tag in this line, handle it and add it (uncounted) to the output
5006 if (!empty($line_matchings[1])) {
5007 // if it's an "empty element" with or without xhtml-conform closing slash
5008 if (preg_match(
5009 '/^<(\s*.+?\/\s*|\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\s.+?)?)>$/is',
5010 $line_matchings[1]
5011 )) {
5012 // do nothing
5013 } // if tag is a closing tag
5014 elseif (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $line_matchings[1], $tag_matchings)) {
5015 // delete tag from $open_tags list
5016 $pos = array_search($tag_matchings[1], $open_tags);
5017 if ($pos !== false) {
5018 unset($open_tags[$pos]);
5019 }
5020 } // if tag is an opening tag
5021 elseif (preg_match('/^<\s*([^\s>!]+).*?>$/s', $line_matchings[1], $tag_matchings)) {
5022 // add tag to the beginning of $open_tags list
5023 array_unshift($open_tags, strtolower($tag_matchings[1]));
5024 }
5025 // add html-tag to $truncate'd text
5026 $truncate .= $line_matchings[1];
5027 }
5028
5029 // calculate the length of the plain text part of the line; handle entities as one character
5030 $content_length = strlen(preg_replace(
5031 '/&[0-9a-z]{2,8};|&#[0-9]{1,7};|[0-9a-f]{1,6};/i',
5032 ' ',
5033 $line_matchings[2]
5034 ));
5035 if ($total_length + $content_length > $a_length) {
5036 // the number of characters which are left
5037 $left = $a_length - $total_length;
5038 $entities_length = 0;
5039 // search for html entities
5040 if (preg_match_all(
5041 '/&[0-9a-z]{2,8};|&#[0-9]{1,7};|[0-9a-f]{1,6};/i',
5042 $line_matchings[2],
5043 $entities,
5044 PREG_OFFSET_CAPTURE
5045 )) {
5046 // calculate the real length of all entities in the legal range
5047 foreach ($entities[0] as $entity) {
5048 if ($entity[1] + 1 - $entities_length <= $left) {
5049 $left--;
5050 $entities_length += strlen($entity[0]);
5051 } else {
5052 // no more characters left
5053 break;
5054 }
5055 }
5056 }
5057
5058 // $truncate .= substr($line_matchings[2], 0, $left+$entities_length);
5059 $truncate .= ilStr::shortenText($line_matchings[2], 0, $left + $entities_length);
5060
5061 // maximum lenght is reached, so get off the loop
5062 break;
5063 } else {
5064 $truncate .= $line_matchings[2];
5065 $total_length += $content_length;
5066 }
5067
5068 // if the maximum length is reached, get off the loop
5069 if ($total_length >= $a_length) {
5070 break;
5071 }
5072 }
5073 } else {
5074 if (strlen($a_text) <= $a_length) {
5075 return $a_text;
5076 } else {
5077 // $truncate = substr($a_text, 0, $a_length - strlen($a_ending));
5078 $truncate = ilStr::shortenText($a_text, 0, $a_length - strlen($a_ending));
5079 }
5080 }
5081
5082 // THIS IS BUGGY AS IT MIGHT BREAK AN OPEN TAG AT THE END
5083 if (!count($open_tags)) {
5084 // if the words shouldn't be cut in the middle...
5085 if (!$a_exact) {
5086 // ...search the last occurance of a space...
5087 $spacepos = strrpos($truncate, ' ');
5088 if ($spacepos !== false) {
5089 // ...and cut the text in this position
5090 // $truncate = substr($truncate, 0, $spacepos);
5091 $truncate = ilStr::shortenText($truncate, 0, $spacepos);
5092 }
5093 }
5094 }
5095
5096 // add the defined ending to the text
5097 $truncate .= $a_ending;
5098
5099 if ($a_consider_html) {
5100 // close all unclosed html-tags
5101 foreach ($open_tags as $tag) {
5102 $truncate .= '</' . $tag . '>';
5103 }
5104 }
5105
5106 return $truncate;
5107 }
5108
5113 public function getContentTemplates(): array
5114 {
5115 return array();
5116 }
5117
5121 public static function getLastChangeByParent(
5122 string $a_parent_type,
5123 int $a_parent_id,
5124 string $a_lang = ""
5125 ): string {
5126 global $DIC;
5127
5128 $db = $DIC->database();
5129
5130 $and_lang = "";
5131 if ($a_lang != "") {
5132 $and_lang = " AND lang = " . $db->quote($a_lang, "text");
5133 }
5134
5135 $db->setLimit(1, 0);
5136 $q = "SELECT last_change FROM page_object " .
5137 " WHERE parent_id = " . $db->quote($a_parent_id, "integer") .
5138 " AND parent_type = " . $db->quote($a_parent_type, "text") . $and_lang .
5139 " ORDER BY last_change DESC";
5140
5141 $set = $db->query($q);
5142 $rec = $db->fetchAssoc($set);
5143
5144 return $rec["last_change"];
5145 }
5146
5148 {
5149 if ($this->getPageConfig()->getEditLockSupport() == false) {
5150 return 0;
5151 }
5152
5153 $aset = new ilSetting("adve");
5154 $min = (int) $aset->get("block_mode_minutes");
5155
5156 return $min;
5157 }
5158
5162 public function getAllFileObjIds(): array
5163 {
5164 $file_obj_ids = array();
5165
5166 // insert inst id file item identifier entries
5167 $xpc = xpath_new_context($this->dom);
5168 $path = "//FileItem/Identifier";
5169 $res = xpath_eval($xpc, $path);
5170 for ($i = 0, $iMax = count($res->nodeset); $i < $iMax; $i++) {
5171 $file_obj_ids[] = $res->nodeset[$i]->get_attribute("Entry");
5172 }
5173 unset($xpc);
5174 return $file_obj_ids;
5175 }
5176
5181 public function resolveResources(array $ref_mapping): bool
5182 {
5183 return ilPCResources::resolveResources($this, $ref_mapping);
5184 }
5185
5189 public function getRepoObjId(): ?int
5190 {
5191 return $this->getParentId();
5192 }
5193
5198 public function getPCModel(): array
5199 {
5200 $model = [];
5201 foreach ($this->getAllPCIds() as $pc_id) {
5202 $co = $this->getContentObjectForPcId($pc_id);
5203 if ($co !== null) {
5204 $co_model = $co->getModel();
5205 if ($co_model !== null) {
5206 $model[$pc_id] = $co_model;
5207 }
5208 }
5209 }
5210 return $model;
5211 }
5212
5220 public function assignCharacteristic(
5221 array $targets,
5222 string $char_par,
5223 string $char_sec,
5224 string $char_med
5225 ) {
5226 if (is_array($targets)) {
5227 foreach ($targets as $t) {
5228 $tarr = explode(":", $t);
5229 $cont_obj = $this->getContentObject($tarr[0], $tarr[1]);
5230 if (is_object($cont_obj) && $cont_obj->getType() == "par") {
5231 $cont_obj->setCharacteristic($char_par);
5232 }
5233 if (is_object($cont_obj) && $cont_obj->getType() == "sec") {
5234 $cont_obj->setCharacteristic($char_sec);
5235 }
5236 if (is_object($cont_obj) && $cont_obj->getType() == "media") {
5237 $cont_obj->setClass($char_med);
5238 }
5239 }
5240 return $this->update();
5241 }
5242 return true;
5243 }
5244}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
$filename
Definition: buildRTE.php:78
const IL_CAL_UNIX
const IL_CAL_DATETIME
const IL_MODE_OUTPUT
const IL_INSERT_BEFORE
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
const IL_INSERT_CHILD
const IL_INSERT_AFTER
static _instantiateQuestion(int $question_id)
static sortArray(array $array, string $a_array_sortby_key, string $a_array_sortorder="asc", bool $a_numeric=false, bool $a_keep_keys=false)
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...
COPage PC elements definition handler.
static getPCDefinitionByName(string $a_pc_name)
Get PC definition by name.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static formatDate(ilDateTime $date, bool $a_skip_day=false, bool $a_include_wd=false, bool $include_seconds=false)
@classDescription Date and time handling
static _after(ilDateTime $start, ilDateTime $end, string $a_compare_field='', string $a_tz='')
compare two dates and check start is after end This method does not consider tz offsets.
static _before(ilDateTime $start, ilDateTime $end, string $a_compare_field='', string $a_tz='')
compare two dates and check start is before end This method does not consider tz offsets.
static setAction(string $a_action)
static _lookupType(int $a_obj_id, int $a_lm_id=0)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
language handling
static getLogger(string $a_component_id)
Get component logger.
Component logger with individual log levels by component id.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _getMapAreasIntLinks(int $a_mob_id)
get all internal links of map areas of a mob
static _resolveMapAreaLinks(int $a_mob_id)
resolve internal links of all media items of a media object
static deleteNewsOfContext(int $a_context_obj_id, string $a_context_obj_type, int $a_context_sub_obj_id=0, string $a_context_sub_obj_type="")
Delete all news of a context.
static _getMobsOfObject(string $a_type, int $a_id, int $a_usage_hist_nr=0, string $a_lang="-")
static _deleteAllUsages(string $a_type, int $a_id, ?int $a_usage_hist_nr=0, string $a_lang="-")
User class.
addToPCClipboard(string $a_content, string $a_time, int $a_nr)
Add a page content item to PC clipboard (should go to another class)
static _lookupName(int $a_user_id)
lookup user name
getPCClipboardContent()
Add a page content item to PC clipboard (should go to another class)
parses the objects.xml it handles the xml-description of all ilias objects
static _lookupType(int $id, bool $reference=false)
static _lookupImportId(int $obj_id)
static _exists(int $id, bool $reference=false, ?string $type=null)
checks if an object exists in object_data
static _getLastUpdateOfObjects(array $obj_ids)
static _lookupObjId(int $ref_id)
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...
Class ilPCMediaObject Media content object (see ILIAS DTD)
Class ilPCParagraphGUI User Interface for Paragraph Editing.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static xml2output(string $a_text, bool $a_wysiwyg=false, bool $a_replace_lists=true, bool $unmask=true)
Converts xml from DB to output in edit textarea.
static resolveResources(ilPageObject $page, array $ref_mappings)
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...
static incEdId(string $ed_id)
Increases an hierarchical editing id at lowest level (last number)
static sortHierIds(array $a_array)
Sort an array of Hier IDS in ascending order.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getInstance(string $a_parent_type, int $a_id=0, int $a_old_nr=0, string $a_lang="-")
Get page object instance.
static getConfigInstance(string $a_parent_type)
Get page config instance.
Class ilPageObject Handles PageObjects of ILIAS Learning Modules (see ILIAS DTD)
releasePageLock()
Release page lock.
stripHierIDs()
strip all hierarchical id attributes out of the dom tree
handleRepositoryLinksOnCopy(array $a_mapping, int $a_source_ref_id)
Handle repository links on copy process.
appendXMLContent(string $a_xml)
containsDeactivatedElements(string $a_content)
Check whether content contains deactivated elements.
getEditLockInfo()
Get edit lock info.
__beforeDelete()
Before deletion handler (internal).
create(bool $a_import=false)
create new page (with current xml data)
php4DOMElement $node
switchEnableMultiple(array $a_hids, bool $a_update=true, bool $a_self_ass=false)
(De-)activate elements
read()
Read page data.
getRepoObjId()
Get object id of repository object that contains this page, return 0 if page does not belong to a rep...
deleteContentFromHierId(string $a_hid, bool $a_update=true)
delete content object with hierarchical id >= $a_hid as part of a split page operation
getFirstColumnIds()
get ids of all first table columns
deleteStyleUsages(int $a_old_nr=0)
Delete style usages.
getContentObjectForPcId(string $pcid)
Get content object for pc id.
getActive(bool $a_check_scheduled_activation=false)
newQuestionCopies(php4DOMDocument $temp_dom)
Replaces existing question content elements with new copies.
deleteContent(string $a_hid, bool $a_update=true, string $a_pcid="", bool $move_operation=false)
delete content object with hierarchical id $a_hid
addFileSizes()
add file sizes
deleteInternalLinks()
Delete internal links.
setImportMode(bool $a_val)
addChangeDivClasses(array $a_hashes)
getLanguageVariablesXML(int $style_id=0)
Get language variables as XML.
afterUpdate(DOMDocument $domdoc, string $xml)
After update.
update(bool $a_validate=true, bool $a_no_history=false)
update complete page content in db (dom xml content is used)
getPCIdsForHierIds(array $hier_ids)
Get hier ids for a set of pc ids.
setLastChange(string $a_lastchange)
newIIMCopies(php4DOMDocument $temp_dom)
Replaces media objects in interactive images with copies of the interactive images.
getEditLock()
Get page lock.
containsIntLinks(string $a_content)
Check whether content contains internal links.
resolveQuestionReferences(array $a_mapping)
Resolve all quesiont references (after import)
existsPCId(string $a_pc_id)
needsImportParsing(?bool $a_parse=null)
cutContents(array $a_hids)
Copy contents to clipboard and cut them from the page.
static getLastChangeByParent(string $a_parent_type, int $a_parent_id, string $a_lang="")
Get all pages for parent object.
setParagraphContent(string $a_hier_id, string $a_content)
Set content of paragraph.
deleteContents(array $a_hids, bool $a_update=true, bool $a_self_ass=false, bool $move_operation=false)
Delete multiple content objects.
getHierIdForPcId(string $pcid)
registerOfflineHandler(object $handler)
ILIAS COPage ReadingTime ReadingTimeManager $reading_time_manager
getInitialOpenedContent()
Get initial opened content.
getLangVarXML(string $var)
getContentObject(string $a_hier_id, string $a_pc_id="")
Get a content object of the page.
static getNewPages(string $a_parent_type, int $a_parent_id, string $a_lang="-")
Get new pages.
removeQuestions(php4DOMDocument $temp_dom)
Remove questions from document.
getDuplicatePCIds()
Get all duplicate PC Ids.
getContentNode(string $a_hier_id, string $a_pc_id="")
increaseViewCnt()
Increase view cnt.
updateFromXML()
Updates page object with current xml content This function is currently (8 beta) called by:
getPCModel()
Get page component model.
getParentContentObjectForPcId(string $pcid)
Get parent content object for pc id.
copyXmlContent(bool $a_clone_mobs=false, int $a_new_parent_id=0, int $obj_copy_id=0, bool $self_ass=true)
Copy content of page; replace page components with copies where necessary (e.g.
setLanguage(string $a_val)
Set language.
static getAllPages(string $a_parent_type, int $a_parent_id, string $a_lang="-")
Get all pages for parent object.
setActivationStart(?string $a_activationstart)
getLastUpdateOfIncludedElements()
Get last update of included elements (media objects and files).
setLastChangeUser(int $a_val)
handleCopiedContent(php4DOMDocument $a_dom, bool $a_self_ass=true, bool $a_clone_mobs=false, int $new_parent_id=0, int $obj_copy_id=0)
Handle copied content This function copies items, that must be copied, if page content is duplicated.
php4DOMDocument $dom
send_paragraph(string $par_id, string $filename)
ilObjectDefinition $obj_definition
getHistoryInfo(int $a_nr)
Get information about a history entry, its predecessor and its successor.
insertContentNode(php4DOMElement $a_cont_node, string $a_pos, int $a_mode=IL_INSERT_AFTER, string $a_pcid="")
insert a content node before/after a sibling or as first child of a parent
moveContentAfter(string $a_source, string $a_target, string $a_spcid="", string $a_tpcid="")
move content object from position $a_source before position $a_target (both hierarchical content ids)
ilDBInterface $db
pasteContents(string $a_hier_id, bool $a_self_ass=false)
Paste contents from pc clipboard.
getHierIds()
get all hierarchical ids
containsIntLink()
returns true, if page was marked as containing an intern link (via setContainsIntLink) (this method s...
deleteContentBeforeHierId(string $a_hid, bool $a_update=true)
delete content object with hierarchical id < $a_hid as part of the split page operation
setContainsQuestion(bool $a_val)
static _writeActive(int $a_id, string $a_parent_type, bool $a_active)
write activation status
static _handleImportRepositoryLinks(string $a_rep_import_id, string $a_rep_type, int $a_rep_ref_id)
Change targest of repository links.
__afterHistoryEntry(DOMDocument $a_old_domdoc, string $a_old_content, int $a_old_nr)
setRenderedTime(string $a_renderedtime)
copyPageToTranslation(string $a_target_lang)
Copy page to translation.
getMultimediaXML()
get a xml string that contains all media object elements, that are referenced by any media alias in t...
static truncateHTML(string $a_text, int $a_length=100, string $a_ending='...', bool $a_exact=false, bool $a_consider_html=true)
Truncate (html) string.
getPageContentsHashes()
Get page contents hashes.
setContainsIntLink(bool $a_contains_link)
lm parser set this flag to true, if the page contains intern links (this method should only be called...
getParagraphForPCID(string $pcid)
static _exists(string $a_parent_type, int $a_id, string $a_lang="", bool $a_no_cache=false)
Checks whether page exists.
handleImportRepositoryLink(string $a_rep_import_id, string $a_rep_type, int $a_rep_ref_id)
getContentTemplates()
Get content templates.
newMobCopies(php4DOMDocument $temp_dom)
Replaces media objects with copies.
getAllFileObjIds()
Get all file object ids.
getXMLFromDom(bool $a_incl_head=false, bool $a_append_mobs=false, bool $a_append_bib=false, string $a_append_str="", bool $a_omit_pageobject_tag=false, int $style_id=0)
get xml content of page from dom (use this, if any changes are made to the document)
static _isScheduledActivation(int $a_id, string $a_parent_type, string $a_lang="-")
Check whether page is activated by time schedule.
copy(int $a_id, string $a_parent_type="", int $a_new_parent_id=0, bool $a_clone_mobs=false, int $obj_copy_id=0)
Copy page.
getFirstRowIds()
get ids of all first table rows
setPageConfig(ilPageConfig $a_val)
setRenderMd5(string $a_rendermd5)
resolveFileItems(array $a_mapping)
Resolve file items (after import)
getQuestionIds()
Get question ids.
resolveIntLinks(array $a_link_map=null)
Resolves all internal link targets of the page, if targets are available (after import)
checkPCIds()
Check, whether (all) page content hashes are set.
getDom()
@depracated
setActive(bool $a_active)
setShowActivationInfo(bool $a_val)
getFO()
get fo page content
static _moveContentAfterHierId(ilPageObject $a_source_page, ilPageObject $a_target_page, string $a_hid)
move content of hierarchical id >= $a_hid to other page
getHistoryEntry(int $a_old_nr)
Get History Entry.
addHierIDs()
Add hierarchical ID (e.g.
setXMLContent(string $a_xml, string $a_encoding="UTF-8")
set xml content of page, start with <PageObject...>, end with </PageObject>, comply with ILIAS DTD,...
buildDom(bool $a_force=false)
preparePageForCompare(ilPageObject $page)
getXMLContent(bool $a_incl_head=false)
get xml content of page
saveInternalLinks(DOMDocument $a_domdoc)
save internal links of page
validateDom(bool $throw=false)
Validate the page content agains page DTD.
insertPCIds()
Insert Page Content IDs.
static getPagesWithLinks(string $a_parent_type, int $a_parent_id, string $a_lang="-")
Get all pages for parent object that contain internal links.
getAllPCIds()
Get all pc ids.
performAutomaticModifications()
Perform automatic modifications (may be overwritten by sub classes)
saveInitialOpenedContent(string $a_type, int $a_id, string $a_target)
Save initial opened content.
__afterUpdate(DOMDocument $a_domdoc, string $a_xml, bool $a_creation=false, bool $a_empty=false)
After update event handler (internal).
resolveIIMMediaAliases(array $a_mapping)
Resolve iim media aliases (in ilContObjParse)
assignCharacteristic(array $targets, string $char_par, string $char_sec, string $char_med)
Assign characteristic.
getFileItemIds()
get ids of all file items
saveStyleUsage(DOMDocument $a_domdoc, int $a_old_nr=0)
Save all style class/template usages.
bbCode2XML(string &$a_content)
transforms bbCode to corresponding xml
getHistoryEntries()
Get History Entries.
compareVersion(int $a_left, int $a_right)
Compares to revisions of the page.
static _lookupActive(int $a_id, string $a_parent_type, bool $a_check_scheduled_activation=false, string $a_lang="-")
lookup activation status
setParentId(int $a_id)
static _writeParentId(string $a_parent_type, int $a_pg_id, int $a_par_id)
writeRenderedContent(string $a_content, string $a_md5)
Write rendered content.
static _lookupActivationData(int $a_id, string $a_parent_type, string $a_lang="-")
Lookup activation data.
getPCIdForHierId(string $hier_id)
static lookupParentId(int $a_id, string $a_type)
getListItemIds()
get ids of all list items
ilPageConfig $page_config
static getParentObjectContributors(string $a_parent_type, int $a_parent_id, string $a_lang="-")
Get all contributors for parent object.
moveContentBefore(string $a_source, string $a_target, string $a_spcid="", string $a_tpcid="")
move content object from position $a_source before position $a_target (both hierarchical content ids)
createFromXML()
Create new page object with current xml content.
copyContents(array $a_hids)
Copy contents to clipboard.
static _existsAndNotEmpty(string $a_parent_type, int $a_id, string $a_lang="-")
Checks whether page exists and is not empty (may return true on some empty pages)
static array $activation_data
resolveResources(array $ref_mapping)
Resolve resources.
checkForTag(string $a_content_tag, string $a_hier_id, string $a_pc_id="")
Get content node from dom.
insertInstIntoIDs(string $a_inst, bool $a_res_ref_to_obj_id=true)
inserts installation id into ids (e.g.
static _lookupContainsDeactivatedElements(int $a_id, string $a_parent_type, string $a_lang="-")
lookup whether page contains deactivated elements
insertContent(ilPageContent $a_cont_obj, string $a_pos, int $a_mode=IL_INSERT_AFTER, string $a_pcid="", bool $remove_placeholder=true)
insert a content node before/after a sibling or as first child of a parent
getMediaAliasElement(int $a_mob_id, int $a_nr=1)
get complete media object (alias) element
static lookupTranslations(string $a_parent_type, int $a_id)
Lookup translations.
static preloadActivationDataByParentId(int $a_parent_id)
Preload activation data by Parent Id.
collectMediaObjects(bool $a_inline_only=true)
get all media objects, that are referenced and used within the page
moveIntLinks(array $a_from_to)
Move internal links from one destination to another.
getHierIdsForPCIds(array $a_pc_ids)
Get hier ids for a set of pc ids.
getInternalLinks(bool $a_cnt_multiple=false)
get all internal links that are used within the page
resolveMediaAliases(array $a_mapping, bool $a_reuse_existing_by_import=false)
Resolve media aliases (after import)
setActivationEnd(?string $a_activationend)
Set Activation End.
static getPageContributors(string $a_parent_type, int $a_page_id, string $a_lang="-")
Get all contributors for parent object.
static getRecentChanges(string $a_parent_type, int $a_parent_id, int $a_period=30, string $a_lang="")
Get recent pages changes for parent object.
setConcreteLang(string $a_val)
addUpdateListener(object $a_object, string $a_method, $a_parameters="")
beforePageContentUpdate(ilPageContent $a_page_content)
Before page content update Note: This one is "work in progress", currently only text paragraphs call ...
static array $exists
setRenderedContent(string $a_renderedcontent)
__construct(int $a_id=0, int $a_old_nr=0, string $a_lang="-")
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...
static subStr(string $a_str, int $a_start, ?int $a_length=null)
Definition: class.ilStr.php:24
static shortenText(string $a_string, int $a_start_pos, int $a_num_bytes, string $a_encoding='UTF-8')
Shorten text to the given number of bytes.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
isGrandChild(int $a_startnode_id, int $a_querynode_id)
checks if a node is in the path of an other node
static getNamePresentation( $a_user_id, bool $a_user_image=false, bool $a_profile_link=false, string $a_profile_back_link="", bool $a_force_first_lastname=false, bool $a_omit_login=false, bool $a_sortable=true, bool $a_return_data_array=false, $a_ctrl_path="ilpublicuserprofilegui")
Default behaviour is:
static now()
Return current timestamp in Y-m-d H:i:s format.
static deliverData(string $a_data, string $a_filename, string $mime="application/octet-stream")
node_name($a_local=false)
insert_before($newnode, $refnode)
append_child($newnode)
$c
Definition: cli.php:38
if(!file_exists(getcwd() . '/ilias.ini.php'))
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: confirmReg.php:20
const CLIENT_ID
Definition: constants.php:41
const IL_INST_ID
Definition: constants.php:40
return['3gp', '7z', 'ai', 'aif', 'aifc', 'aiff', 'au', 'arw', 'avi', 'backup', 'bak', 'bas', 'bpmn', 'bpmn2', 'bmp', 'bib', 'bibtex', 'bz', 'bz2', 'c', 'c++', 'cc', 'cct', 'cdf', 'cer', 'class', 'cls', 'conf', 'cpp', 'crt', 'crs', 'crw', 'cr2', 'css', 'cst', 'csv', 'cur', 'db', 'dcr', 'des', 'dng', 'doc', 'docx', 'dot', 'dotx', 'dtd', 'dvi', 'el', 'eps', 'epub', 'f', 'f77', 'f90', 'flv', 'for', 'g3', 'gif', 'gl', 'gan', 'ggb', 'gsd', 'gsm', 'gtar', 'gz', 'gzip', 'h', 'hpp', 'htm', 'html', 'htmls', 'ibooks', 'ico', 'ics', 'ini', 'ipynb', 'java', 'jbf', 'jpeg', 'jpg', 'js', 'jsf', 'jso', 'json', 'latex', 'lang', 'less', 'log', 'lsp', 'ltx', 'm1v', 'm2a', 'm2v', 'm3u', 'm4a', 'm4v', 'markdown', 'm', 'mat', 'md', 'mdl', 'mdown', 'mid', 'min', 'midi', 'mobi', 'mod', 'mov', 'movie', 'mp2', 'mp3', 'mp4', 'mpa', 'mpeg', 'mpg', 'mph', 'mpga', 'mpp', 'mpt', 'mpv', 'mpx', 'mv', 'mw', 'mv4', 'nb', 'nbp', 'nef', 'nif', 'niff', 'obj', 'obm', 'odt', 'ods', 'odp', 'odg', 'odf', 'oga', 'ogg', 'ogv', 'old', 'p', 'pas', 'pbm', 'pcl', 'pct', 'pcx', 'pdf', 'pgm', 'pic', 'pict', 'png', 'por', 'pov', 'project', 'properties', 'ppa', 'ppm', 'pps', 'ppsx', 'ppt', 'pptx', 'ppz', 'ps', 'psd', 'pwz', 'qt', 'qtc', 'qti', 'qtif', 'r', 'ra', 'ram', 'rar', 'rast', 'rda', 'rev', 'rexx', 'ris', 'rf', 'rgb', 'rm', 'rmd', 'rmi', 'rmm', 'rmp', 'rt', 'rtf', 'rtx', 'rv', 's', 's3m', 'sav', 'sbs', 'sec', 'sdml', 'sgm', 'sgml', 'smi', 'smil', 'srt', 'sps', 'spv', 'stl', 'svg', 'swa', 'swf', 'swz', 'tar', 'tex', 'texi', 'texinfo', 'text', 'tgz', 'tif', 'tiff', 'ttf', 'txt', 'tmp', 'uvproj', 'vdf', 'vimeo', 'viv', 'vivo', 'vrml', 'vsdx', 'wav', 'webm', 'wmv', 'wmx', 'wmz', 'woff', 'wwd', 'xhtml', 'xif', 'xls', 'xlsx', 'xmind', 'xml', 'xsl', 'xsd', 'zip']
$txt
Definition: error.php:13
global $DIC
Definition: feed.php:28
$target_arr
Definition: goto.php:50
$mobs
Definition: imgupload.php:70
$errors
Definition: imgupload.php:65
const ILIAS_VERSION_NUMERIC
domxml_open_mem($str, $mode=0, &$error=null)
xpath_new_context($dom_document)
const DOMXML_LOAD_PARSING
xpath_eval(php4DOMXPath $xpath_context, string $eval_str, $contextnode=null)
xslt_free(&$proc)
xslt_create()
Interface ilDBInterface.
update(string $table_name, array $values, array $where)
@description $where MUST contain existing columns only.
setLimit(int $limit, int $offset=0)
quote($value, string $type)
manipulate(string $query)
Run a (write) Query on the database.
manipulateF(string $query, array $types, array $values)
query(string $query)
Run a (read-only) Query on the database.
fetchAssoc(ilDBStatement $statement)
queryF(string $query, array $types, array $values)
exit
Definition: login.php:28
$ref_id
Definition: ltiauth.php:67
if(! $DIC->user() ->getId()||!ilLTIConsumerAccess::hasCustomProviderCreationAccess()) $params
Definition: ltiregstart.php:33
$path
Definition: ltiservices.php:32
$res
Definition: ltiservices.php:69
if($clientAssertionType !='urn:ietf:params:oauth:client-assertion-type:jwt-bearer'|| $grantType !='client_credentials') $parts
Definition: ltitoken.php:64
if($format !==null) $name
Definition: metadata.php:247
$i
Definition: metadata.php:41
$source
Definition: metadata.php:93
$xml
Definition: metadata.php:351
string $key
Consumer key/client ID value.
Definition: System.php:193
int $updated
Timestamp for when the object was last updated.
Definition: System.php:158
$query
$type
$url
$lm_set