ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
class.ilPageObject.php
Go to the documentation of this file.
1 <?php
2 
19 define("IL_INSERT_BEFORE", 0);
20 define("IL_INSERT_AFTER", 1);
21 define("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 
50 abstract 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  {
191  return $this->concrete_lang;
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  {
222  return $this->renderedcontent;
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 {
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 . "\">" .
911  $this->xml;
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 {
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 
1497  $med_links = ilMediaItem::_getMapAreasIntLinks($id);
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 
1519  $med_links = ilMediaItem::_getMapAreasIntLinks($id);
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."-";
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");
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);
2977  return ilObject::_getLastUpdateOfObjects($objs);
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 
3308  public function deleteContentFromHierId(
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 
3340  public function deleteContentBeforeHierId(
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);
3985  $obj_id = ilInternalLink::_extractObjIdOfTarget($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 
4075  public static function _lookupContainsDeactivatedElements(
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 
4675  public function saveInitialOpenedContent(
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 
5147  public function getEffectiveEditLockTime(): int
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 }
assignCharacteristic(array $targets, string $char_par, string $char_sec, string $char_med)
Assign characteristic.
xpath_eval(php4DOMXPath $xpath_context, string $eval_str, $contextnode=null)
getLastUpdateOfIncludedElements()
Get last update of included elements (media objects and files).
xslt_create()
setRenderedContent(string $a_renderedcontent)
performAutomaticModifications()
Perform automatic modifications (may be overwritten by sub classes)
getActive(bool $a_check_scheduled_activation=false)
stripHierIDs()
strip all hierarchical id attributes out of the dom tree
addChangeDivClasses(array $a_hashes)
static _lookupActive(int $a_id, string $a_parent_type, bool $a_check_scheduled_activation=false, string $a_lang="-")
lookup activation status
checkPCIds()
Check, whether (all) page content hashes are set.
static resolveResources(ilPageObject $page, array $ref_mappings)
static setAction(string $a_action)
cutContents(array $a_hids)
Copy contents to clipboard and cut them from the page.
increaseViewCnt()
Increase view cnt.
copyPageToTranslation(string $a_target_lang)
Copy page to translation.
getAllPCIds()
Get all pc ids.
getLangVarXML(string $var)
$target_arr
Definition: goto.php:50
$res
Definition: ltiservices.php:69
getPCModel()
Get page component model.
updateFromXML()
Updates page object with current xml content This function is currently (8 beta) called by: ...
getHistoryInfo(int $a_nr)
Get information about a history entry, its predecessor and its successor.
__beforeDelete()
Before deletion handler (internal).
releasePageLock()
Release page lock.
addFileSizes()
add file sizes
validateDom(bool $throw=false)
Validate the page content agains page DTD.
setContainsQuestion(bool $a_val)
resolveFileItems(array $a_mapping)
Resolve file items (after import)
setPageConfig(ilPageConfig $a_val)
exit
Definition: login.php:28
newQuestionCopies(php4DOMDocument $temp_dom)
Replaces existing question content elements with new copies.
getDuplicatePCIds()
Get all duplicate PC Ids.
append_child($newnode)
appendXMLContent(string $a_xml)
append xml content to page setXMLContent must be called before and the same encoding must be us...
buildDom(bool $a_force=false)
static getParentObjectContributors(string $a_parent_type, int $a_parent_id, string $a_lang="-")
Get all contributors for parent object.
create(bool $a_import=false)
create new page (with current xml data)
const IL_INST_ID
Definition: constants.php:40
const IL_CAL_DATETIME
getFO()
get fo page content
manipulateF(string $query, array $types, array $values)
$c
Definition: cli.php:38
setImportMode(bool $a_val)
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 getLogger(string $a_component_id)
Get component logger.
getHierIdForPcId(string $pcid)
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:
setParentId(int $a_id)
static getPagesWithLinks(string $a_parent_type, int $a_parent_id, string $a_lang="-")
Get all pages for parent object that contain internal links.
deleteContentBeforeHierId(string $a_hid, bool $a_update=true)
delete content object with hierarchical id < $a_hid as part of the split page operation ...
static _isScheduledActivation(int $a_id, string $a_parent_type, string $a_lang="-")
Check whether page is activated by time schedule.
const ILIAS_VERSION_NUMERIC
$mobs
Definition: imgupload.php:70
newMobCopies(php4DOMDocument $temp_dom)
Replaces media objects with copies.
$errors
Definition: imgupload.php:65
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$type
ILIAS COPage ReadingTime ReadingTimeManager $reading_time_manager
if($clientAssertionType !='urn:ietf:params:oauth:client-assertion-type:jwt-bearer'|| $grantType !='client_credentials') $parts
Definition: ltitoken.php:64
fetchAssoc(ilDBStatement $statement)
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.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getContentNode(string $a_hier_id, string $a_pc_id="")
if(! $DIC->user() ->getId()||!ilLTIConsumerAccess::hasCustomProviderCreationAccess()) $params
Definition: ltiregstart.php:33
getFirstColumnIds()
get ids of all first table columns
addToPCClipboard(string $a_content, string $a_time, int $a_nr)
Add a page content item to PC clipboard (should go to another class)
xslt_free(&$proc)
resolveIntLinks(array $a_link_map=null)
Resolves all internal link targets of the page, if targets are available (after import) ...
update(bool $a_validate=true, bool $a_no_history=false)
update complete page content in db (dom xml content is used)
update(string $table_name, array $values, array $where)
$where MUST contain existing columns only.
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
static array $activation_data
php4DOMDocument $dom
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 _writeParentId(string $a_parent_type, int $a_pg_id, int $a_par_id)
getPCIdForHierId(string $hier_id)
static formatDate(ilDateTime $date, bool $a_skip_day=false, bool $a_include_wd=false, bool $include_seconds=false)
static _lookupName(int $a_user_id)
lookup user name
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.
removeQuestions(php4DOMDocument $temp_dom)
Remove questions from document.
static getPCDefinitionByName(string $a_pc_name)
Get PC definition by name.
saveStyleUsage(DOMDocument $a_domdoc, int $a_old_nr=0)
Save all style class/template usages.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
handleImportRepositoryLink(string $a_rep_import_id, string $a_rep_type, int $a_rep_ref_id)
read()
Read page data.
getContentObjectForPcId(string $pcid)
Get content object for pc id.
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.
getQuestionIds()
Get question ids.
static getPageContributors(string $a_parent_type, int $a_page_id, string $a_lang="-")
Get all contributors for parent object.
static _lookupActivationData(int $a_id, string $a_parent_type, string $a_lang="-")
Lookup activation data.
getHistoryEntries()
Get History Entries.
quote($value, string $type)
newIIMCopies(php4DOMDocument $temp_dom)
Replaces media objects in interactive images with copies of the interactive images.
ilPageConfig $page_config
static subStr(string $a_str, int $a_start, ?int $a_length=null)
Definition: class.ilStr.php:24
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
beforePageContentUpdate(ilPageContent $a_page_content)
Before page content update Note: This one is "work in progress", currently only text paragraphs call ...
preparePageForCompare(ilPageObject $page)
static deliverData(string $a_data, string $a_filename, string $mime="application/octet-stream")
static lookupTranslations(string $a_parent_type, int $a_id)
Lookup translations.
resolveIIMMediaAliases(array $a_mapping)
Resolve iim media aliases (in ilContObjParse)
const IL_CAL_UNIX
static now()
Return current timestamp in Y-m-d H:i:s format.
getPageContentsHashes()
Get page contents hashes.
setLimit(int $limit, int $offset=0)
getInternalLinks(bool $a_cnt_multiple=false)
get all internal links that are used within the page
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
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)...
handleRepositoryLinksOnCopy(array $a_mapping, int $a_source_ref_id)
Handle repository links on copy process.
getFileItemIds()
get ids of all file items
$path
Definition: ltiservices.php:32
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
deleteContent(string $a_hid, bool $a_update=true, string $a_pcid="", bool $move_operation=false)
delete content object with hierarchical id $a_hid
static _lookupObjId(int $ref_id)
isGrandChild(int $a_startnode_id, int $a_querynode_id)
checks if a node is in the path of an other node
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setXMLContent(string $a_xml, string $a_encoding="UTF-8")
set xml content of page, start with <PageObject...>, end with </PageObject>, comply with ILIAS DTD...
deleteStyleUsages(int $a_old_nr=0)
Delete style usages.
global $DIC
Definition: feed.php:28
static _lookupContainsDeactivatedElements(int $a_id, string $a_parent_type, string $a_lang="-")
lookup whether page contains deactivated elements
if($format !==null) $name
Definition: metadata.php:247
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.
xpath_new_context($dom_document)
parses the objects.xml it handles the xml-description of all ilias objects
resolveResources(array $ref_mapping)
Resolve resources.
static _lookupImportId(int $obj_id)
getHierIdsForPCIds(array $a_pc_ids)
Get hier ids for a set of pc ids.
saveInternalLinks(DOMDocument $a_domdoc)
save internal links of page
int $updated
Timestamp for when the object was last updated.
Definition: System.php:158
deleteContentFromHierId(string $a_hid, bool $a_update=true)
delete content object with hierarchical id >= $a_hid as part of a split page operation ...
static _exists(int $id, bool $reference=false, ?string $type=null)
checks if an object exists in object_data
static truncateHTML(string $a_text, int $a_length=100, string $a_ending='...', bool $a_exact=false, bool $a_consider_html=true)
Truncate (html) string.
getParentContentObjectForPcId(string $pcid)
Get parent content object for pc id.
static getNewPages(string $a_parent_type, int $a_parent_id, string $a_lang="-")
Get new pages.
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) ...
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.
$ref_id
Definition: ltiauth.php:67
php4DomElement
containsIntLinks(string $a_content)
Check whether content contains internal links.
saveInitialOpenedContent(string $a_type, int $a_id, string $a_target)
Save initial opened content.
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) ...
insertInstIntoIDs(string $a_inst, bool $a_res_ref_to_obj_id=true)
inserts installation id into ids (e.g.
domxml_open_mem($str, $mode=0, &$error=null)
setActivationStart(?string $a_activationstart)
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.
writeRenderedContent(string $a_content, string $a_md5)
Write rendered content.
deleteInternalLinks()
Delete internal links.
static getRecentChanges(string $a_parent_type, int $a_parent_id, int $a_period=30, string $a_lang="")
Get recent pages changes for parent object.
static _exists(string $a_parent_type, int $a_id, string $a_lang="", bool $a_no_cache=false)
Checks whether page exists.
checkForTag(string $a_content_tag, string $a_hier_id, string $a_pc_id="")
Get content node from dom.
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
getListItemIds()
get ids of all list items
resolveMediaAliases(array $a_mapping, bool $a_reuse_existing_by_import=false)
Resolve media aliases (after import)
getInitialOpenedContent()
Get initial opened content.
setActivationEnd(?string $a_activationend)
Set Activation End.
Class ilPageObject Handles PageObjects of ILIAS Learning Modules (see ILIAS DTD)
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)...
const CLIENT_ID
Definition: constants.php:41
getHistoryEntry(int $a_old_nr)
Get History Entry.
bbCode2XML(string &$a_content)
transforms bbCode to corresponding xml
deleteContents(array $a_hids, bool $a_update=true, bool $a_self_ass=false, bool $move_operation=false)
Delete multiple content objects.
containsIntLink()
returns true, if page was marked as containing an intern link (via setContainsIntLink) (this method s...
string $key
Consumer key/client ID value.
Definition: System.php:193
createFromXML()
Create new page object with current xml content.
static sortHierIds(array $a_array)
Sort an array of Hier IDS in ascending order.
static _getMapAreasIntLinks(int $a_mob_id)
get all internal links of map areas of a mob
query(string $query)
Run a (read-only) Query on the database.
setLanguage(string $a_val)
Set language.
insert_before($newnode, $refnode)
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)
$query
ilObjectDefinition $obj_definition
switchEnableMultiple(array $a_hids, bool $a_update=true, bool $a_self_ass=false)
(De-)activate elements
const IL_INSERT_AFTER
$txt
Definition: error.php:13
static lookupParentId(int $a_id, string $a_type)
static _getMobsOfObject(string $a_type, int $a_id, int $a_usage_hist_nr=0, string $a_lang="-")
setConcreteLang(string $a_val)
getContentObject(string $a_hier_id, string $a_pc_id="")
Get a content object of the page.
static getLastChangeByParent(string $a_parent_type, int $a_parent_id, string $a_lang="")
Get all pages for parent object.
queryF(string $query, array $types, array $values)
$filename
Definition: buildRTE.php:78
getPCClipboardContent()
Add a page content item to PC clipboard (should go to another class)
getContentTemplates()
Get content templates.
insertPCIds()
Insert Page Content IDs.
static _resolveMapAreaLinks(int $a_mob_id)
resolve internal links of all media items of a media object
static _writeActive(int $a_id, string $a_parent_type, bool $a_active)
write activation status
node_name($a_local=false)
getFirstRowIds()
get ids of all first table rows
addHierIDs()
Add hierarchical ID (e.g.
afterUpdate(DOMDocument $domdoc, string $xml)
After update.
setContainsIntLink(bool $a_contains_link)
lm parser set this flag to true, if the page contains intern links (this method should only be called...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static preloadActivationDataByParentId(int $a_parent_id)
Preload activation data by Parent Id.
setLastChange(string $a_lastchange)
static _deleteAllUsages(string $a_type, int $a_id, ?int $a_usage_hist_nr=0, string $a_lang="-")
static array $exists
static _instantiateQuestion(int $question_id)
getEditLockInfo()
Get edit lock info.
moveIntLinks(array $a_from_to)
Move internal links from one destination to another.
$lm_set
php4DOMElement $node
registerOfflineHandler(object $handler)
ilDBInterface $db
copyContents(array $a_hids)
Copy contents to clipboard.
__afterUpdate(DOMDocument $a_domdoc, string $a_xml, bool $a_creation=false, bool $a_empty=false)
After update event handler (internal).
__afterHistoryEntry(DOMDocument $a_old_domdoc, string $a_old_content, int $a_old_nr)
addUpdateListener(object $a_object, string $a_method, $a_parameters="")
getLanguageVariablesXML(int $style_id=0)
Get language variables as XML.
static _lookupType(int $a_obj_id, int $a_lm_id=0)
getMultimediaXML()
get a xml string that contains all media object elements, that are referenced by any media alias in t...
static getConfigInstance(string $a_parent_type)
Get page config instance.
collectMediaObjects(bool $a_inline_only=true)
get all media objects, that are referenced and used within the page
getEditLock()
Get page lock.
needsImportParsing(?bool $a_parse=null)
const IL_MODE_OUTPUT
containsDeactivatedElements(string $a_content)
Check whether content contains deactivated elements.
__construct(int $a_id=0, int $a_old_nr=0, string $a_lang="-")
getXMLContent(bool $a_incl_head=false)
get xml content of page
compareVersion(int $a_left, int $a_right)
Compares to revisions of the page.
$url
setParagraphContent(string $a_hier_id, string $a_content)
Set content of paragraph.
getHierIds()
get all hierarchical ids
Class ilPCMediaObject Media content object (see ILIAS DTD)
getParagraphForPCID(string $pcid)
getPCIdsForHierIds(array $hier_ids)
Get hier ids for a set of pc ids.
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...
manipulate(string $query)
Run a (write) Query on the database.
static _lookupType(int $id, bool $reference=false)
getRepoObjId()
Get object id of repository object that contains this page, return 0 if page does not belong to a rep...
setNode(php4DOMElement $a_node)
Set xml node of page content.
getMediaAliasElement(int $a_mob_id, int $a_nr=1)
get complete media object (alias) element
setActive(bool $a_active)
static _handleImportRepositoryLinks(string $a_rep_import_id, string $a_rep_type, int $a_rep_ref_id)
Change targest of repository links.
$source
Definition: metadata.php:93
existsPCId(string $a_pc_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
send_paragraph(string $par_id, string $filename)
getAllFileObjIds()
Get all file object ids.
static _getLastUpdateOfObjects(array $obj_ids)
static getInstance(string $a_parent_type, int $a_id=0, int $a_old_nr=0, string $a_lang="-")
Get page object instance.
setRenderMd5(string $a_rendermd5)
const IL_INSERT_CHILD
const DOMXML_LOAD_PARSING
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
pasteContents(string $a_hier_id, bool $a_self_ass=false)
Paste contents from pc clipboard.
setShowActivationInfo(bool $a_val)
const IL_INSERT_BEFORE
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$i
Definition: metadata.php:41
setRenderedTime(string $a_renderedtime)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getAllPages(string $a_parent_type, int $a_parent_id, string $a_lang="-")
Get all pages for parent object.
static sortArray(array $array, string $a_array_sortby_key, string $a_array_sortorder="asc", bool $a_numeric=false, bool $a_keep_keys=false)
Class ilPCParagraphGUI User Interface for Paragraph Editing.
setLastChangeUser(int $a_val)
resolveQuestionReferences(array $a_mapping)
Resolve all quesiont references (after import)
static _moveContentAfterHierId(ilPageObject $a_source_page, ilPageObject $a_target_page, string $a_hid)
move content of hierarchical id >= $a_hid to other page