ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.ilObjStyleSheet.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
22 
29 {
31  protected \ILIAS\Style\Content\InternalRepoService $repo;
32  protected bool $is_3_10_skin = false;
33  protected string $export_sub_dir = "";
34  protected array $chars_by_type = [];
35  protected array $end_styles = [];
36  protected array $chars = [];
37  protected bool $up_to_date = false;
38  public array $style = [];
39  protected array $hidden_chars = [];
40  protected array $style_class = [];
41 
42  protected int $scope = 0;
43 
44  public static array $num_unit = array("px", "em", "rem", "ex", "%", "pt", "pc", "in", "mm", "cm");
45  public static array $num_unit_no_perc = array("px", "em", "rem", "ex", "pt", "pc", "in", "mm", "cm");
46 
47  // css parameters and their attribute values, input type and group
48  public static array $parameter = array(
49  "font-size" => array(
50  "values" => array("xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large", "smaller", "larger"),
51  "input" => "fontsize",
52  "group" => "text"),
53  "font-family" => array(
54  "values" => array(),
55  "input" => "text",
56  "group" => "text"),
57  "font-style" => array(
58  "values" => array("italic", "oblique", "normal"),
59  "input" => "select",
60  "group" => "text"),
61  "font-weight" => array(
62  "values" => array("bold", "normal", "bolder", "lighter"),
63  "input" => "select",
64  "group" => "text"),
65  "font-variant" => array(
66  "values" => array("small-caps", "normal"),
67  "input" => "select",
68  "group" => "text"),
69  "word-spacing" => array(
70  "values" => array(),
71  "input" => "numeric_no_perc",
72  "group" => "text"),
73  "letter-spacing" => array(
74  "values" => array(),
75  "input" => "numeric_no_perc",
76  "group" => "text"),
77  "text-decoration" => array(
78  "values" => array("underline", "overline", "line-through", "blink", "none"),
79  "input" => "select",
80  "group" => "text"),
81  "text-transform" => array(
82  "values" => array("capitalize", "uppercase", "lowercase", "none"),
83  "input" => "select",
84  "group" => "text"),
85  "color" => array(
86  "values" => array(),
87  "input" => "color",
88  "group" => "text"),
89  "text-indent" => array(
90  "values" => array(),
91  "input" => "numeric",
92  "group" => "text"),
93  "line-height" => array(
94  "values" => array(),
95  "input" => "numeric",
96  "group" => "text"),
97  "vertical-align" => array(
98  "values" => array("top", "middle", "bottom", "baseline", "sub", "super",
99  "text-top", "text-bottom"),
100  "input" => "select",
101  "group" => "text"),
102  "text-align" => array(
103  "values" => array("left", "center", "right", "justify"),
104  "input" => "select",
105  "group" => "text"),
106  "white-space" => array(
107  "values" => array("normal", "pre", "nowrap"),
108  "input" => "select",
109  "group" => "text"),
110  "margin" => array(
111  "values" => array(),
112  "input" => "trbl_numeric",
113  "subpar" => array("margin", "margin-top", "margin-right",
114  "margin-bottom", "margin-left"),
115  "group" => "margin_and_padding"),
116  "padding" => array(
117  "values" => array(),
118  "input" => "trbl_numeric",
119  "subpar" => array("padding", "padding-top", "padding-right",
120  "padding-bottom", "padding-left"),
121  "group" => "margin_and_padding"),
122  "border-width" => array(
123  "values" => array("thin", "medium", "thick"),
124  "input" => "border_width",
125  "subpar" => array("border-width", "border-top-width", "border-right-width",
126  "border-bottom-width", "border-left-width"),
127  "group" => "border"),
128  "border-color" => array(
129  "values" => array(),
130  "input" => "trbl_color",
131  "subpar" => array("border-color", "border-top-color", "border-right-color",
132  "border-bottom-color", "border-left-color"),
133  "group" => "border"),
134  "border-style" => array(
135  "values" => array("none", "hidden", "dotted", "dashed", "solid", "double",
136  "groove", "ridge", "inset", "outset"),
137  "input" => "border_style",
138  "subpar" => array("border-style", "border-top-style", "border-right-style",
139  "border-bottom-style", "border-left-style"),
140  "group" => "border"),
141 
142  "background-color" => array(
143  "values" => array(),
144  "input" => "color",
145  "group" => "background"),
146  "background-image" => array(
147  "values" => array(),
148  "input" => "background_image",
149  "group" => "background"),
150  "background-repeat" => array(
151  "values" => array("repeat", "repeat-x", "repeat-y", "no-repeat"),
152  "input" => "select",
153  "group" => "background"),
154  "background-attachment" => array(
155  "values" => array("fixed", "scroll"),
156  "input" => "select",
157  "group" => "background"),
158  "background-position" => array(
159  "values" => array("horizontal" => array("left", "center", "right"),
160  "vertical" => array("top", "center", "bottom")),
161  "input" => "background_position",
162  "group" => "background"),
163 
164  "position" => array(
165  "values" => array("absolute", "fixed", "relative", "static"),
166  "input" => "select",
167  "group" => "positioning"),
168  "top" => array(
169  "values" => array(),
170  "input" => "numeric",
171  "group" => "positioning"),
172  "bottom" => array(
173  "values" => array(),
174  "input" => "numeric",
175  "group" => "positioning"),
176  "left" => array(
177  "values" => array(),
178  "input" => "numeric",
179  "group" => "positioning"),
180  "right" => array(
181  "values" => array(),
182  "input" => "numeric",
183  "group" => "positioning"),
184  "width" => array(
185  "values" => array(),
186  "input" => "numeric",
187  "group" => "positioning"),
188  "height" => array(
189  "values" => array(),
190  "input" => "numeric",
191  "group" => "positioning"),
192  "min-height" => array(
193  "values" => array(),
194  "input" => "numeric",
195  "group" => "positioning"),
196  "float" => array(
197  "values" => array("left", "right", "none"),
198  "input" => "select",
199  "group" => "positioning"),
200  "overflow" => array(
201  "values" => array("visible", "hidden", "scroll", "auto"),
202  "input" => "select",
203  "group" => "positioning"),
204  "opacity" => array(
205  "values" => array(),
206  "input" => "percentage",
207  "group" => "special"),
208  "transform" => array(
209  "values" => array("rotate(90deg)", "rotate(180deg)", "rotate(270deg)"),
210  "input" => "select",
211  "group" => "special"),
212  "transform-origin" => array(
213  "values" => array( "horizontal" => array("left", "center", "right"),
214  "vertical" => array("top", "center", "bottom")),
215  "input" => "background_position",
216  "group" => "special"),
217  "cursor" => array(
218  "values" => array("auto", "default", "crosshair", "pointer", "move",
219  "n-resize", "ne-resize", "e-resize", "se-resize", "s-resize", "sw-resize",
220  "w-resize", "nw-resize", "text", "wait", "help"),
221  "input" => "select",
222  "group" => "special"),
223  "clear" => array(
224  "values" => array("both","left","right","none"),
225  "input" => "select",
226  "group" => "special"),
227 
228  "list-style-type.ol" => array(
229  "values" => array("decimal","lower-roman","upper-roman",
230  "lower-alpha", "upper-alpha", "lower-greek", "hebrew",
231  "decimal-leading-zero", "cjk-ideographic", "hiragana",
232  "katakana", "hiragana-iroha", "katakana-iroha", "none"),
233  "input" => "select",
234  "group" => "ol"),
235  "list-style-type.ul" => array(
236  "values" => array("disc","circle","square",
237  "none"),
238  "input" => "select",
239  "group" => "ul"),
240  "list-style-image.ul" => array(
241  "values" => array(),
242  "input" => "background_image",
243  "group" => "ul"),
244  "list-style-position.ol" => array(
245  "values" => array("inside","outside"),
246  "input" => "select",
247  "group" => "ol"),
248  "list-style-position.ul" => array(
249  "values" => array("inside","outside"),
250  "input" => "select",
251  "group" => "ul"
252  ),
253  "border-collapse" => array(
254  "values" => array("collapse","separate"),
255  "input" => "select",
256  "group" => "table"
257  ),
258  "caption-side" => array(
259  "values" => array("top","bottom","left","right"),
260  "input" => "select",
261  "group" => "table"
262  )
263  );
264 
265  // filter groups of properties that should only be
266  // displayed with matching tag (group -> tags)
267  public static array $filtered_groups =
268  array("ol" => array("ol"), "ul" => array("ul"),
269  "table" => array("table"), "positioning" => array("h1", "h2", "h3", "div", "img", "table", "a", "figure", "li", "p"));
270 
271  // style types and their super type
272  public static array $style_super_types = array(
273  "text_block" => array("text_block", "heading1", "heading2", "heading3", "code_block"),
274  "text_inline" => array("text_inline", "sub", "sup", "code_inline", "strong", "em"),
275  "section" => array("section"),
276  "link" => array("link"),
277  "table" => array("table", "table_cell", "table_caption"),
278  "list" => array("list_o", "list_u", "list_item"),
279  "flist" => array("flist_cont", "flist_head", "flist", "flist_li", "flist_a"),
280  "media" => array("media_cont", "media_caption", "iim", "marker"),
281  "tabs" => array("va_cntr", "va_icntr", "va_ihead", "va_iheada", "va_ihcap", "va_icont",
282  "ha_cntr", "ha_icntr", "ha_ihead", "ha_iheada", "ha_ihcap", "ha_icont", "ca_cntr", "ca_icntr", "ca_ihead", "ca_icont"),
283  "question" => array("question", "qtitle", "qanswer", "qinput", "qlinput", "qsubmit", "qfeedr", "qfeedw",
284  "qimg", "qordul", "qordli", "qimgd", "qetitem", "qetcorr", "qover"),
285  "page" => array("page_cont", "page_title", "page_fn")
286  );
287 
288  // these types are expandable, i.e. the user can define new style classes
289  public static array $expandable_types = array(
290  "text_block",
291  "text_inline", "section", "media_cont", "media_caption", "table", "table_cell", "flist_li", "table_caption",
292  "list_o", "list_u", "list_item",
293  "va_cntr", "va_icntr", "va_ihead", "va_iheada", "va_ihcap", "va_icont",
294  "ha_cntr", "ha_icntr", "ha_ihead", "ha_iheada", "ha_ihcap", "ha_icont",
295  "ca_cntr", "ca_icntr", "ca_ihead", "ca_icont"
296  );
297 
298  // these types can be hidden in the content editor
299  public static array $hideable_types = array(
300  "table", "table_cell"
301  );
302 
303  // tag that are used by style types
304  public static array $assigned_tags = array(
305  "text_block" => "p",
306  "heading1" => "h1",
307  "heading2" => "h2",
308  "heading3" => "h3",
309  "code_block" => "pre",
310  "em" => "em",
311  "text_inline" => "span",
312  "code_inline" => "code",
313  "sup" => "sup",
314  "sub" => "sub",
315  "strong" => "strong",
316  "section" => "div",
317  "link" => "a",
318  "table" => "table",
319  "table_cell" => "td",
320  "table_caption" => "caption",
321  "media_cont" => "figure",
322  "media_caption" => "div",
323  "iim" => "div",
324  "marker" => "a",
325  "list_o" => "ol",
326  "list_u" => "ul",
327  "list_item" => "li",
328  "flist_cont" => "div",
329  "flist_head" => "div",
330  "flist" => "ul",
331  "flist_li" => "li",
332  "flist_a" => "a",
333  "question" => "div",
334  "qtitle" => "div",
335  "qanswer" => "div",
336  "qimg" => "img",
337  "qimgd" => "a",
338  "qordul" => "ul",
339  "qordli" => "li",
340  "qetitem" => "a",
341  "qetcorr" => "span",
342  "qinput" => "input",
343  "qlinput" => "textarea",
344  "qsubmit" => "input",
345  "qfeedr" => "div",
346  "qfeedw" => "div",
347  "qover" => "div",
348  "page_cont" => "div",
349  "page_fn" => "div",
350  "page" => "div",
351  "page_title" => "h1",
352  "va_cntr" => "div",
353  "va_icntr" => "div",
354  "va_icont" => "div",
355  "va_ihead" => "div",
356  "va_iheada" => "div",
357  "va_ihcap" => "div",
358  "ha_cntr" => "div",
359  "ha_icntr" => "div",
360  "ha_icont" => "div",
361  "ha_iheada" => "div",
362  "ha_ihcap" => "div",
363  "ha_ihead" => "div",
364  "ca_cntr" => "div",
365  "ca_icntr" => "div",
366  "ca_ihead" => "div",
367  "ca_icont" => "div"
368  );
369 
370  // pseudo classes
371  public static array $pseudo_classes =
372  [
373  "a" => ["hover"],
374  "div" => ["hover", "before"],
375  "img" => ["hover"],
376  "li" => ["before"]
377  ];
378 
379  // core styles these styles MUST exists -> see also basic_style/style.xml
380  public static array $core_styles = array(
381  array("type" => "text_block", "class" => "Standard"),
382  array("type" => "text_block", "class" => "List"),
383  array("type" => "text_block", "class" => "TableContent"),
384  array("type" => "code_block", "class" => "Code"),
385  array("type" => "heading1", "class" => "Headline1"),
386  array("type" => "heading2", "class" => "Headline2"),
387  array("type" => "heading3", "class" => "Headline3"),
388  array("type" => "text_inline", "class" => "Comment"),
389  array("type" => "em", "class" => "Emph"),
390  array("type" => "text_inline", "class" => "Quotation"),
391  array("type" => "strong", "class" => "Strong"),
392  array("type" => "text_inline", "class" => "Accent"),
393  array("type" => "text_inline", "class" => "Important"),
394  array("type" => "code_inline", "class" => "CodeInline"),
395  array("type" => "sup", "class" => "Sup"),
396  array("type" => "sub", "class" => "Sub"),
397  array("type" => "link", "class" => "IntLink"),
398  array("type" => "link", "class" => "ExtLink"),
399  array("type" => "link", "class" => "FootnoteLink"),
400  array("type" => "link", "class" => "FileLink"),
401  array("type" => "link", "class" => "GlossaryLink"),
402  array("type" => "media_cont", "class" => "MediaContainer"),
403  array("type" => "media_cont", "class" => "MediaContainerMax50"),
404  array("type" => "media_cont", "class" => "MediaContainerFull100"),
405  array("type" => "table", "class" => "StandardTable"),
406  array("type" => "media_caption", "class" => "MediaCaption"),
407  array("type" => "iim", "class" => "ContentPopup"),
408  array("type" => "marker", "class" => "Marker"),
409  array("type" => "page_cont", "class" => "PageContainer"),
410  array("type" => "page", "class" => "Page"),
411  array("type" => "page_fn", "class" => "Footnote"),
412  array("type" => "page_title", "class" => "PageTitle"),
413  array("type" => "list_o", "class" => "NumberedList"),
414  array("type" => "list_u", "class" => "BulletedList"),
415  array("type" => "list_item", "class" => "StandardListItem"),
416  array("type" => "question", "class" => "Standard"),
417  array("type" => "question", "class" => "SingleChoice"),
418  array("type" => "question", "class" => "MultipleChoice"),
419  array("type" => "question", "class" => "TextQuestion"),
420  array("type" => "question", "class" => "OrderingQuestion"),
421  array("type" => "question", "class" => "MatchingQuestion"),
422  array("type" => "question", "class" => "ImagemapQuestion"),
423  array("type" => "question", "class" => "ErrorText"),
424  array("type" => "question", "class" => "TextSubset"),
425  array("type" => "question", "class" => "ClozeTest"),
426  array("type" => "qtitle", "class" => "Title"),
427  array("type" => "qanswer", "class" => "Answer"),
428  array("type" => "qimg", "class" => "QuestionImage"),
429  array("type" => "qimgd", "class" => "ImageDetailsLink"),
430  array("type" => "qordul", "class" => "OrderList"),
431  array("type" => "qordli", "class" => "OrderListItem"),
432  array("type" => "qordul", "class" => "OrderListHorizontal"),
433  array("type" => "qordli", "class" => "OrderListItemHorizontal"),
434  array("type" => "qetitem", "class" => "ErrorTextItem"),
435  array("type" => "qetitem", "class" => "ErrorTextSelected"),
436  array("type" => "qetcorr", "class" => "ErrorTextCorrected"),
437  array("type" => "qinput", "class" => "TextInput"),
438  array("type" => "qlinput", "class" => "LongTextInput"),
439  array("type" => "qsubmit", "class" => "Submit"),
440  array("type" => "qfeedr", "class" => "FeedbackRight"),
441  array("type" => "qfeedw", "class" => "FeedbackWrong"),
442  array("type" => "qover", "class" => "Correct"),
443  array("type" => "qover", "class" => "Inorrect"),
444  array("type" => "qover", "class" => "StatusMessage"),
445  array("type" => "qover", "class" => "WrongAnswersMessage"),
446  array("type" => "flist_cont", "class" => "FileListContainer"),
447  array("type" => "flist_head", "class" => "FileListHeading"),
448  array("type" => "flist", "class" => "FileList"),
449  array("type" => "flist_li", "class" => "FileListItem"),
450  array("type" => "flist_a", "class" => "FileListItemLink")
451  );
452 
453  public static array $templates = array(
454  "table" => array(
455  "table" => "table",
456  "caption" => "table_caption",
457  "row_head" => "table_cell",
458  "row_foot" => "table_cell",
459  "col_head" => "table_cell",
460  "col_foot" => "table_cell",
461  "odd_row" => "table_cell",
462  "even_row" => "table_cell",
463  "odd_col" => "table_cell",
464  "even_col" => "table_cell"),
465  "vaccordion" => array(
466  "va_cntr" => "va_cntr",
467  "va_icntr" => "va_icntr",
468  "va_ihead" => "va_ihead",
469  "va_iheada" => "va_iheada",
470  "va_ihcap" => "va_ihcap",
471  "va_icont" => "va_icont"
472  ),
473  "haccordion" => array(
474  "ha_cntr" => "ha_cntr",
475  "ha_icntr" => "ha_icntr",
476  "ha_ihead" => "ha_ihead",
477  "ha_iheada" => "ha_iheada",
478  "ha_ihcap" => "ha_ihcap",
479  "ha_icont" => "ha_icont"
480  ),
481  "carousel" => array(
482  "ca_cntr" => "ca_cntr",
483  "ca_icntr" => "ca_icntr",
484  "ca_ihead" => "ca_ihead",
485  "ca_icont" => "ca_icont"
486  )
487  );
488 
489  // basic style xml file, image directory and dom
490  protected static string $basic_style_file = "./libs/ilias/Style/basic_style/style.xml";
491  protected static string $basic_style_zip = "./libs/ilias/Style/basic_style/style.zip";
492  protected static string $basic_style_image_dir = "./libs/ilias/Style/basic_style/images";
493  protected static ?DOMDocument $basic_style_dom = null;
494 
495  public function __construct(
496  int $a_id = 0,
497  bool $a_call_by_reference = false
498  ) {
499  global $DIC;
500 
501  $this->db = $DIC->database();
502  $this->lng = $DIC->language();
503  $this->type = "sty";
504  $this->style = array();
505  $this->ilias = $DIC["ilias"];
506  $this->domain = $DIC->contentStyle()->internal()->domain();
507 
508  if ($a_call_by_reference) {
509  $this->ilias->raiseError("Can't instantiate style object via reference id.", $this->ilias->error_obj->FATAL);
510  }
511  parent::__construct($a_id, false);
512  $this->repo = $DIC->contentStyle()->internal()->repo();
513  }
514 
515  public static function getBasicZipPath(): string
516  {
517  return self::$basic_style_zip;
518  }
519 
523  public function setUpToDate(bool $a_up_to_date = true): void
524  {
525  $this->up_to_date = $a_up_to_date;
526  }
527 
528  public function getUpToDate(): bool
529  {
530  return $this->up_to_date;
531  }
532 
533  public function setScope(int $a_scope): void
534  {
535  $this->scope = $a_scope;
536  }
537 
538  public function getScope(): int
539  {
540  return $this->scope;
541  }
542 
543  public static function _writeUpToDate(
544  int $a_id,
545  bool $a_up_to_date
546  ): void {
547  global $DIC;
548 
549  $ilDB = $DIC->database();
550 
551  $q = "UPDATE style_data SET uptodate = " .
552  $ilDB->quote((int) $a_up_to_date, "integer") .
553  " WHERE id = " . $ilDB->quote($a_id, "integer");
554  $ilDB->manipulate($q);
555  }
556 
557  public static function writeOwner($obj_id, $style_id)
558  {
559  global $DIC;
560  $ilDB = $DIC->database();
561 
562  $q = "UPDATE style_data SET owner_obj = " .
563  $ilDB->quote((int) $obj_id, "integer") .
564  " WHERE id = " . $ilDB->quote($style_id, "integer");
565  $ilDB->manipulate($q);
566  }
567 
568  public static function _lookupUpToDate(int $a_id): bool
569  {
570  global $DIC;
571 
572  $ilDB = $DIC->database();
573 
574  $q = "SELECT uptodate FROM style_data " .
575  " WHERE id = " . $ilDB->quote($a_id, "integer");
576  $res = $ilDB->query($q);
577  $sty = $ilDB->fetchAssoc($res);
578 
579  return (bool) $sty["uptodate"];
580  }
581 
585  public static function _writeStandard(
586  int $a_id,
587  bool $a_std
588  ): void {
589  global $DIC;
590 
591  $ilDB = $DIC->database();
592 
593  $q = "UPDATE style_data SET standard = " .
594  $ilDB->quote((int) $a_std, "integer") .
595  " WHERE id = " . $ilDB->quote($a_id, "integer");
596  $ilDB->manipulate($q);
597  }
598 
599  public static function _writeScope(int $a_id, int $a_scope): void
600  {
601  global $DIC;
602 
603  $ilDB = $DIC->database();
604 
605  $q = "UPDATE style_data SET category = " .
606  $ilDB->quote($a_scope, "integer") .
607  " WHERE id = " . $ilDB->quote($a_id, "integer");
608  $ilDB->manipulate($q);
609  }
610 
614  public static function _lookupStandard(int $a_id): bool
615  {
616  global $DIC;
617 
618  $ilDB = $DIC->database();
619 
620  $q = "SELECT * FROM style_data " .
621  " WHERE id = " . $ilDB->quote($a_id, "integer");
622  $res = $ilDB->query($q);
623  $sty = $ilDB->fetchAssoc($res);
624 
625  return (bool) ($sty["standard"] ?? false);
626  }
627 
628  public static function _writeActive(int $a_id, bool $a_active): void
629  {
630  global $DIC;
631 
632  $ilDB = $DIC->database();
633 
634  $q = "UPDATE style_data SET active = " .
635  $ilDB->quote((int) $a_active, "integer") .
636  " WHERE id = " . $ilDB->quote($a_id, "integer");
637  $ilDB->manipulate($q);
638  }
639 
643  public static function _lookupActive(int $a_id): bool
644  {
645  global $DIC;
646 
647  $ilDB = $DIC->database();
648 
649  $q = "SELECT * FROM style_data " .
650  " WHERE id = " . $ilDB->quote($a_id, "integer");
651  $res = $ilDB->query($q);
652  $sty = $ilDB->fetchAssoc($res);
653 
654  return (bool) $sty["active"];
655  }
656 
661  public static function _getStandardStyles(
662  bool $a_exclude_default_style = false,
663  bool $a_include_deactivated = false,
664  int $a_scope = 0
665  ): array {
666  global $DIC;
667 
668  $ilDB = $DIC->database();
669  $ilSetting = $DIC->settings();
670  $tree = $DIC->repositoryTree();
671 
672  $default_style = $ilSetting->get("default_content_style_id");
673 
674  $and_str = "";
675  if (!$a_include_deactivated) {
676  $and_str = " AND active = 1";
677  }
678 
679  $q = "SELECT * FROM style_data " .
680  " WHERE standard = 1" . $and_str;
681  $res = $ilDB->query($q);
682  $styles = array();
683  while ($sty = $ilDB->fetchAssoc($res)) {
684  if (!$a_exclude_default_style || $default_style != $sty["id"]) {
685  // check scope
686  if ($a_scope > 0 && $sty["category"] > 0) {
687  if ($tree->isInTree((int) $sty["category"]) &&
688  $tree->isInTree($a_scope)) {
689  $path = $tree->getPathId($a_scope);
690  if (!in_array((int) $sty["category"], $path)) {
691  continue;
692  }
693  }
694  }
695  $styles[(int) $sty["id"]] = ilObject::_lookupTitle((int) $sty["id"]);
696  }
697  }
698 
699  return $styles;
700  }
701 
702 
708  public static function _getClonableContentStyles(): array
709  {
710  global $DIC;
711 
712  $ilAccess = $DIC->access();
713  $ilDB = $DIC->database();
714 
715  $clonable_styles = array();
716 
717  $q = "SELECT * FROM style_data";
718  $style_set = $ilDB->query($q);
719  while ($style_rec = $ilDB->fetchAssoc($style_set)) {
720  $clonable = false;
721  if ($style_rec["standard"] == 1) {
722  if ($style_rec["active"] == 1) {
723  $clonable = true;
724  }
725  } else {
726  $obj_ids = ilObjContentObject::_lookupContObjIdByStyleId((int) $style_rec["id"]);
727  if (count($obj_ids) == 0) {
728  $obj_ids = self::lookupObjectForStyle((int) $style_rec["id"]);
729  }
730  foreach ($obj_ids as $id) {
731  $ref = ilObject::_getAllReferences((int) $id);
732  foreach ($ref as $ref_id) {
733  if ($ilAccess->checkAccess("write", "", $ref_id)) {
734  $clonable = true;
735  }
736  }
737  }
738  }
739  if ($clonable) {
740  $clonable_styles[(int) $style_rec["id"]] =
741  ilObject::_lookupTitle((int) $style_rec["id"]);
742  }
743  }
744 
745  asort($clonable_styles);
746 
747  return $clonable_styles;
748  }
749 
750  public static function _getBasicStyleDom(): DOMDocument
751  {
752  if (!is_object(self::$basic_style_dom)) {
753  self::$basic_style_dom = new DOMDocument();
754  self::$basic_style_dom->load(self::$basic_style_file);
755  }
756 
757  return self::$basic_style_dom;
758  }
759 
760  public static function getBasicImageDir(): string
761  {
762  return self::$basic_style_image_dir;
763  }
764 
765 
769  public function create(
770  int $a_from_style = 0,
771  bool $a_import_mode = false
772  ): int {
773  global $DIC;
774 
775  $ilDB = $this->db;
776 
777  $id = parent::create();
778 
779  $service = $DIC->contentStyle()
780  ->internal();
781  $access_manager = $service->domain()->access(
782  0,
783  $DIC->user()->getId()
784  );
785  $access_manager->enableWrite(true);
786  $color_manager = $service->domain()->color($this->getId(), $access_manager);
787 
788  if ($a_from_style == 0) {
789  if (!$a_import_mode) {
790  // copy styles from basic style
791  $this->createFromXMLFile(self::$basic_style_file, true);
792 
793  // copy images from basic style
794  $this->createImagesDirectory();
795 
796  // cross filesystem (lib -> web) rCopy
798  self::$basic_style_image_dir,
799  $this->getImagesDirectory()
800  );
801  } else {
802  // add style_data record
803  $q = "INSERT INTO style_data (id, uptodate, category) VALUES " .
804  "(" . $ilDB->quote($this->getId(), "integer") . ", 0," .
805  $ilDB->quote($this->getScope(), "integer") . ")";
806  $ilDB->manipulate($q);
808  }
809  } else {
810  // get style parameter records
811  $def = array();
812  $q = "SELECT * FROM style_parameter WHERE style_id = " .
813  $ilDB->quote($a_from_style, "integer");
814  $par_set = $ilDB->query($q);
815  while ($par_rec = $ilDB->fetchAssoc($par_set)) {
816  $def[] = array("tag" => $par_rec["tag"], "class" => $par_rec["class"],
817  "parameter" => $par_rec["parameter"], "value" => $par_rec["value"],
818  "type" => $par_rec["type"], "mq_id" => $par_rec["mq_id"], "custom" => $par_rec["custom"]);
819  }
820 
821  $char_repo = $this->repo->characteristic();
822  $char_repo->cloneAllFromStyle($a_from_style, $this->getId());
823 
824 
825  // copy media queries
826  $from_style = new ilObjStyleSheet($a_from_style);
827  $mqs = $from_style->getMediaQueries();
828  $mq_mapping = array();
829  foreach ($mqs as $mq) {
830  $nid = $this->addMediaQuery($mq["mquery"]);
831  $mq_mapping[$mq["id"]] = $nid;
832  }
833 
834  // default style settings
835  foreach ($def as $sty) {
836  $id = $ilDB->nextId("style_parameter");
837  $q = "INSERT INTO style_parameter (id, style_id, tag, class, parameter, value, type, mq_id, custom) VALUES " .
838  "(" .
839  $ilDB->quote($id, "integer") . "," .
840  $ilDB->quote($this->getId(), "integer") . "," .
841  $ilDB->quote($sty["tag"], "text") . "," .
842  $ilDB->quote($sty["class"], "text") . "," .
843  $ilDB->quote($sty["parameter"], "text") . "," .
844  $ilDB->quote($sty["value"], "text") . "," .
845  $ilDB->quote($sty["type"], "text") . "," .
846  $ilDB->quote((int) ($mq_mapping[$sty["mq_id"]] ?? 0), "integer") . "," .
847  $ilDB->quote($sty["custom"], "integer") .
848  ")";
849  $ilDB->manipulate($q);
850  }
851 
852  // add style_data record
853  $q = "INSERT INTO style_data (id, uptodate, category) VALUES " .
854  "(" . $ilDB->quote($this->getId(), "integer") . ", 0," .
855  $ilDB->quote($this->getScope(), "integer") . ")";
856  $ilDB->manipulate($q);
857 
858  // copy images
859  $this->createImagesDirectory();
861  $from_style->getImagesDirectory(),
862  $this->getImagesDirectory()
863  );
864 
865  // copy colors
866  $colors = $from_style->getColors();
867  foreach ($colors as $c) {
868  $color_manager->addColor($c["name"], $c["code"]);
869  }
870 
871  // copy templates
873  foreach ($tcts as $tct => $v) {
874  $templates = $from_style->getTemplates($tct);
875  foreach ($templates as $t) {
876  $this->addTemplate($tct, $t["name"], $t["classes"]);
877  }
878  }
879  }
880 
881  $this->read();
882  if (!$a_import_mode) {
883  $this->writeCSSFile();
884  }
885 
886  return $id;
887  }
888 
892  public function characteristicExists(
893  string $a_char,
894  string $a_style_type
895  ): bool {
896  $ilDB = $this->db;
897 
898  $set = $ilDB->queryF(
899  "SELECT style_id FROM style_char WHERE style_id = %s AND characteristic = %s AND type = %s",
900  array("integer", "text", "text"),
901  array($this->getId(), $a_char, $a_style_type)
902  );
903  if ($ilDB->fetchAssoc($set)) {
904  return true;
905  }
906  return false;
907  }
908 
909  public function addCharacteristic(
910  string $a_type,
911  string $a_char,
912  bool $a_hidden = false,
913  int $order_nr = 0,
914  bool $outdated = false
915  ): void {
916  $ilDB = $this->db;
917 
918  // delete characteristic record
919  $ilDB->insert("style_char", [
920  "style_id" => ["integer", $this->getId()],
921  "type" => ["text", $a_type],
922  "characteristic" => ["text", $a_char],
923  "hide" => ["integer", (int) $a_hidden],
924  "outdated" => ["integer", (int) $outdated],
925  "order_nr" => ["integer", $order_nr]
926  ]);
927 
928  $this->setUpToDate(false);
929  $this->_writeUpToDate($this->getId(), false);
930  }
931 
935  public function getCharacteristics(
936  string $a_type = "",
937  bool $a_no_hidden = false,
938  bool $a_include_core = true
939  ): array {
940  $chars = array();
941 
942  if ($a_type == "") {
943  $chars = $this->chars;
944  }
945  if (isset($this->chars_by_type[$a_type])) {
946  foreach ($this->chars_by_type[$a_type] as $c) {
947  if ($a_include_core || !self::isCoreStyle($a_type, $c)) {
948  $chars[] = $c;
949  }
950  }
951  }
952 
953  if ($a_no_hidden) {
954  foreach ($chars as $k => $char) {
955  if ($a_type == "" && $this->hidden_chars[$char["type"] . ":" . $char["class"]]) {
956  unset($chars[$k]);
957  } elseif ($this->hidden_chars[$a_type . ":" . $char] ?? false) {
958  unset($chars[$k]);
959  }
960  }
961  }
962 
963  return $chars;
964  }
965 
966  public function setCharacteristics(array $a_chars): void
967  {
968  $this->chars = $a_chars;
969  }
970 
974  public function saveHideStatus(
975  string $a_type,
976  string $a_char,
977  bool $a_hide
978  ): void {
979  $ilDB = $this->db;
980 
981  $ilDB->manipulate(
982  "UPDATE style_char SET " .
983  " hide = " . $ilDB->quote((int) $a_hide, "integer") .
984  " WHERE style_id = " . $ilDB->quote($this->getId(), "integer") . " AND " .
985  " type = " . $ilDB->quote($a_type, "text") . " AND " .
986  " characteristic = " . $ilDB->quote($a_char, "text")
987  );
988  }
989 
993  public function getHideStatus(
994  string $a_type,
995  string $a_char
996  ): bool {
997  $ilDB = $this->db;
998 
999  $set = $ilDB->query(
1000  "SELECT hide FROM style_char " .
1001  " WHERE style_id = " . $ilDB->quote($this->getId(), "integer") . " AND " .
1002  " type = " . $ilDB->quote($a_type, "text") . " AND " .
1003  " characteristic = " . $ilDB->quote($a_char, "text")
1004  );
1005  $rec = $ilDB->fetchAssoc($set);
1006 
1007  return (bool) ($rec["hide"] ?? false);
1008  }
1009 
1013  public function ilClone(): int
1014  {
1015  $lng = $this->lng;
1016 
1017  $lng->loadLanguageModule("style");
1018 
1019  $new_obj = new ilObjStyleSheet();
1020  $new_obj->setTitle($this->getTitle() . " (" . $lng->txt("sty_acopy") . ")");
1021  $new_obj->setType($this->getType());
1022  $new_obj->setDescription($this->getDescription());
1023  $new_obj->create($this->getId());
1024 
1025  $new_obj->writeStyleSetting(
1026  "disable_auto_margins",
1027  $this->lookupStyleSetting("disable_auto_margins")
1028  );
1029 
1030  return $new_obj->getId();
1031  }
1032 
1036  public function copyImagesToDir(string $a_target): void
1037  {
1038  ilFileUtils::rCopy($this->getImagesDirectory(), $a_target);
1039  }
1040 
1050  public function addParameter(
1051  string $a_tag,
1052  string $a_par,
1053  string $a_type,
1054  int $a_mq_id = 0,
1055  bool $a_custom = false
1056  ): void {
1057  $ilDB = $this->db;
1058 
1059  $avail_params = $this->getAvailableParameters();
1060  $tag = explode(".", $a_tag);
1061  $value = $avail_params[$a_par][0];
1062  $id = $ilDB->nextId("style_parameter");
1063  $q = "INSERT INTO style_parameter (id,style_id, type, tag, class, parameter, value, mq_id, custom) VALUES " .
1064  "(" .
1065  $ilDB->quote($id, "integer") . "," .
1066  $ilDB->quote($this->getId(), "integer") . "," .
1067  $ilDB->quote($a_type, "text") . "," .
1068  $ilDB->quote($tag[0], "text") . "," .
1069  $ilDB->quote($tag[1], "text") . "," .
1070  $ilDB->quote($a_par, "text") . "," .
1071  $ilDB->quote($value, "text") . "," .
1072  $ilDB->quote($a_mq_id, "integer") . "," .
1073  $ilDB->quote($a_custom, "integer") .
1074  ")";
1075  $ilDB->manipulate($q);
1076  $this->read();
1077  $this->writeCSSFile();
1078  }
1079 
1084  public function createImagesDirectory(): void
1085  {
1087  }
1088 
1093  public static function _createImagesDirectory(
1094  int $a_style_id
1095  ): void {
1096  global $DIC;
1097 
1098  $ilErr = $DIC["ilErr"];
1099 
1100  $sty_data_dir = ilFileUtils::getWebspaceDir() . "/sty";
1101  if (!is_dir($sty_data_dir)) {
1102  ilFileUtils::makeDir($sty_data_dir);
1103  }
1104  if (!is_writable($sty_data_dir)) {
1105  $ilErr->raiseError("Style data directory (" . $sty_data_dir
1106  . ") not writeable.", $ilErr->FATAL);
1107  }
1108 
1109  $style_dir = $sty_data_dir . "/sty_" . $a_style_id;
1110  if (!is_dir($style_dir)) {
1111  ilFileUtils::makeDir($style_dir);
1112  }
1113  if (!is_dir($style_dir)) {
1114  $ilErr->raiseError("Creation of style directory failed (" .
1115  $style_dir . ").", $ilErr->FATAL);
1116  }
1117 
1118  // create images subdirectory
1119  $im_dir = $style_dir . "/images";
1120  if (!is_dir($im_dir)) {
1121  ilFileUtils::makeDir($im_dir);
1122  }
1123  if (!is_dir($im_dir)) {
1124  $ilErr->raiseError("Creation of Import Directory failed (" .
1125  $im_dir . ").", $ilErr->FATAL);
1126  }
1127 
1128  // create thumbnails directory
1129  $thumb_dir = $style_dir . "/images/thumbnails";
1130  ilFileUtils::makeDir($thumb_dir);
1131  if (!is_dir($thumb_dir)) {
1132  $ilErr->raiseError("Creation of Import Directory failed (" .
1133  $thumb_dir . ").", $ilErr->FATAL);
1134  }
1135  }
1136 
1137  public function getImagesDirectory(): string
1138  {
1140  }
1141 
1142  public static function _getImagesDirectory(int $a_style_id): string
1143  {
1144  return ilFileUtils::getWebspaceDir() . "/sty/sty_" . $a_style_id .
1145  "/images";
1146  }
1147 
1148  public function getThumbnailsDirectory(): string
1149  {
1150  return $this->getImagesDirectory() .
1151  "/thumbnails";
1152  }
1153 
1157  public function deleteParameter(int $a_id): void
1158  {
1159  $ilDB = $this->db;
1160 
1161  $q = "DELETE FROM style_parameter WHERE id = " .
1162  $ilDB->quote($a_id, "integer");
1163  $ilDB->query($q);
1164  }
1165 
1166 
1174  public function deleteCustomStylePars(
1175  string $a_tag,
1176  string $a_class,
1177  string $a_type,
1178  int $a_mq_id = 0
1179  ): void {
1180  $ilDB = $this->db;
1181 
1182  $q = "DELETE FROM style_parameter WHERE " .
1183  " style_id = " . $ilDB->quote($this->getId(), "integer") . " AND " .
1184  " tag = " . $ilDB->quote($a_tag, "text") . " AND " .
1185  " class = " . $ilDB->quote($a_class, "text") . " AND " .
1186  " mq_id = " . $ilDB->quote($a_mq_id, "integer") . " AND " .
1187  " custom = " . $ilDB->quote(1, "integer") . " AND " .
1188  " " . $ilDB->equals("type", $a_type, "text", true);
1189 
1190  $ilDB->manipulate($q);
1191  }
1192 
1196  public function deleteStyleParOfChar(
1197  string $a_type,
1198  string $a_class
1199  ): void {
1200  $ilDB = $this->db;
1201 
1202  $q = "DELETE FROM style_parameter WHERE " .
1203  " style_id = " . $ilDB->quote($this->getId(), "integer") . " AND " .
1204  " class = " . $ilDB->quote($a_class, "text") . " AND " .
1205  " " . $ilDB->equals("type", $a_type, "text", true);
1206 
1207  $ilDB->manipulate($q);
1208  }
1209 
1210 
1211  public function delete(): bool
1212  {
1213  $ilDB = $this->db;
1214 
1215  // delete object
1216  parent::delete();
1217 
1218  // check whether this style is global default
1219  $def_style = $this->ilias->getSetting("default_content_style_id");
1220  if ($def_style == $this->getId()) {
1221  $this->ilias->deleteSetting("default_content_style_id");
1222  }
1223 
1224  // check whether this style is global fixed
1225  $fixed_style = $this->ilias->getSetting("fixed_content_style_id");
1226  if ($fixed_style == $this->getId()) {
1227  $this->ilias->deleteSetting("fixed_content_style_id");
1228  }
1229 
1230  // delete style parameter
1231  $q = "DELETE FROM style_parameter WHERE style_id = " .
1232  $ilDB->quote($this->getId(), "integer");
1233  $ilDB->manipulate($q);
1234 
1235  // delete style file
1236  $css_file_name = ilFileUtils::getWebspaceDir() . "/css/style_" . $this->getId() . ".css";
1237  if (is_file($css_file_name)) {
1238  unlink($css_file_name);
1239  }
1240 
1241  // delete media queries
1242  $ilDB->manipulate(
1243  "DELETE FROM sty_media_query WHERE " .
1244  " style_id = " . $ilDB->quote($this->getId(), "integer")
1245  );
1246 
1247  // delete entries in learning modules
1249 
1250  // delete style data record
1251  $q = "DELETE FROM style_data WHERE id = " .
1252  $ilDB->quote($this->getId(), "integer");
1253  $ilDB->manipulate($q);
1254 
1255  return true;
1256  }
1257 
1258 
1262  public function read(): void
1263  {
1264  $ilDB = $this->db;
1265 
1266  parent::read();
1267 
1268  $q = "SELECT * FROM style_parameter WHERE style_id = " .
1269  $ilDB->quote($this->getId(), "integer") . " ORDER BY tag, class, type, mq_id ";
1270  $style_set = $ilDB->query($q);
1271  $ctag = "";
1272  $cclass = "";
1273  $ctype = "";
1274  $cmq_id = 0;
1275  $this->style = array();
1276  // workaround for bug #17586, see also http://stackoverflow.com/questions/3066356/multiple-css-classes-properties-overlapping-based-on-the-order-defined
1277  // e.g. ha_iheada must be written after ha_ihead, since they are acting on the same dom node
1278  // styles that must be added at the end
1279  $this->end_styles = array();
1280  $tag = null;
1281  while ($style_rec = $ilDB->fetchAssoc($style_set)) {
1282  if ($style_rec["tag"] != $ctag || $style_rec["class"] != $cclass
1283  || $style_rec["type"] != $ctype || $style_rec["mq_id"] != $cmq_id) {
1284  // add current tag array to style array
1285  if (is_array($tag)) {
1286  if (in_array($ctype, array("ha_iheada", "va_iheada"))) {
1287  $this->end_styles[] = $tag;
1288  } else {
1289  $this->style[] = $tag;
1290  }
1291  }
1292  $tag = array();
1293  }
1294  $ctag = $style_rec["tag"];
1295  $cclass = $style_rec["class"];
1296  $ctype = $style_rec["type"];
1297  $cmq_id = $style_rec["mq_id"];
1298  $tag[] = $style_rec;
1299  // added $cmq_id
1300  $this->style_class[$ctype][$cclass][$cmq_id][$style_rec["parameter"]] = $style_rec["value"];
1301  }
1302  if (is_array($tag)) {
1303  $this->style[] = $tag;
1304  }
1305  foreach ($this->end_styles as $s) {
1306  $this->style[] = $s;
1307  }
1308  //var_dump($this->style_class);
1309  $q = "SELECT * FROM style_data WHERE id = " .
1310  $ilDB->quote($this->getId(), "integer");
1311  $res = $ilDB->query($q);
1312  $sty = $ilDB->fetchAssoc($res);
1313  $this->setUpToDate((bool) $sty["uptodate"]);
1314  $this->setScope((int) $sty["category"]);
1315 
1316  // get style characteristics records
1317  $this->chars = array();
1318  $this->chars_by_type = array();
1319  $q = "SELECT * FROM style_char WHERE style_id = " .
1320  $ilDB->quote($this->getId(), "integer") .
1321  " ORDER BY type ASC, characteristic ASC";
1322  $par_set = $ilDB->query($q);
1323  while ($par_rec = $ilDB->fetchAssoc($par_set)) {
1324  $this->chars[] = array("type" => $par_rec["type"], "class" => $par_rec["characteristic"], "hide" => $par_rec["hide"]);
1325  $this->chars_by_type[$par_rec["type"]][] = $par_rec["characteristic"];
1326  if ($par_rec["hide"]) {
1327  $this->hidden_chars[$par_rec["type"] . ":" . $par_rec["characteristic"]] = true;
1328  }
1329  }
1330  // var_dump($this->style); exit;
1331  }
1332 
1336  public function writeCSSFile(
1337  string $a_target_file = "",
1338  string $a_image_dir = ""
1339  ): void {
1340  $style = $this->getStyle();
1341 
1342  if (!is_dir(ilFileUtils::getWebspaceDir() . "/css")) {
1344  }
1345 
1346  if ($a_target_file == "") {
1347  $css_file_name = ilFileUtils::getWebspaceDir() . "/css/style_" . $this->getId() . ".css";
1348  } else {
1349  $css_file_name = $a_target_file;
1350  }
1351  $css_file = fopen($css_file_name, 'wb');
1352 
1353  $page_background = "";
1354 
1355  $mqs = array(array("mquery" => "", "id" => 0));
1356  foreach ($this->getMediaQueries() as $mq) {
1357  $mqs[] = $mq;
1358  }
1359 
1360  // iterate all media queries
1361  foreach ($mqs as $mq) {
1362  if ($mq["id"] > 0) {
1363  fwrite($css_file, "@media " . $mq["mquery"] . " {\n");
1364  }
1365  reset($style);
1366  foreach ($style as $tag) {
1367  if ($tag[0]["mq_id"] != $mq["id"]) {
1368  continue;
1369  }
1370  if (is_int(strpos($tag[0]["class"], "before")) && !is_int(strpos($tag[0]["class"], "::before"))) {
1371  $tag[0]["class"] = str_replace(":before", "::before", $tag[0]["class"]);
1372  }
1373  fwrite($css_file, $tag[0]["tag"] . ".ilc_" . $tag[0]["type"] . "_" . $tag[0]["class"] . "\n");
1374  // echo "<br>";
1375  // var_dump($tag[0]["type"]);
1376  if ($tag[0]["tag"] == "td") {
1377  fwrite($css_file, ",th" . ".ilc_" . $tag[0]["type"] . "_" . $tag[0]["class"] . "\n");
1378  }
1379  if (in_array($tag[0]["tag"], array("h1", "h2", "h3"))) {
1380  fwrite($css_file, ",div.ilc_text_block_" . $tag[0]["class"] . "\n");
1381  fwrite($css_file, ",html.il-no-tiny-bg body#tinymce.ilc_text_block_" . $tag[0]["class"] . " > p \n");
1382  }
1383  if ($tag[0]["type"] == "section") { // sections can use a tags, if links are used
1384  fwrite($css_file, ",div a.ilc_" . $tag[0]["type"] . "_" . $tag[0]["class"] . "\n");
1385  }
1386  if ($tag[0]["type"] == "strong") {
1387  fwrite($css_file, ",span.ilc_text_inline_" . $tag[0]["class"] . "\n");
1388  }
1389  if ($tag[0]["type"] == "em") {
1390  fwrite($css_file, ",span.ilc_text_inline_" . $tag[0]["class"] . "\n");
1391  }
1392  if ($tag[0]["type"] == "text_block") {
1393  fwrite($css_file, ",html.il-no-tiny-bg body#tinymce.ilc_text_block_" . $tag[0]["class"] . " > p, #copg-editor-slate-content p.ilc_text_block_" . $tag[0]["class"] . "\n");
1394  }
1395  if ($tag[0]["class"] == "VAccordCntr") {
1396  fwrite($css_file, ",div.ilc_va_cntr_AccordCntr\n");
1397  }
1398  if ($tag[0]["class"] == "VAccordICntr") {
1399  fwrite($css_file, ",div.ilc_va_icntr_AccordICntr\n");
1400  }
1401  if ($tag[0]["class"] == "VAccordICont") {
1402  fwrite($css_file, ",div.ilc_va_icont_AccordICont\n");
1403  }
1404  if ($tag[0]["class"] == "VAccordIHead") {
1405  fwrite($css_file, ",div.ilc_va_ihead_AccordIHead\n");
1406  }
1407  if ($tag[0]["class"] == "VAccordIHead:hover") {
1408  fwrite($css_file, ",div.ilc_va_ihead_AccordIHead:hover\n");
1409  }
1410  if ($tag[0]["class"] == "VAccordIHeadActive") {
1411  fwrite($css_file, ",div.ilc_va_iheada_AccordIHeadActive\n");
1412  }
1413  if ($tag[0]["class"] == "VAccordIHeadActive:hover") {
1414  fwrite($css_file, ",div.ilc_va_iheada_AccordIHeadActive:hover\n");
1415  }
1416  fwrite($css_file, "{\n");
1417 
1418  // collect table border attributes
1419  $t_border = array();
1420 
1421  foreach ($tag as $par) {
1422  $cur_par = $par["parameter"] ?? '';
1423  $cur_val = $par["value"] ?? '';
1424 
1425  // replace named colors
1426  if (is_int(strpos($cur_par, "color")) && substr(trim($cur_val), 0, 1) == "!") {
1427  $cur_val = $this->getColorCodeForName(substr($cur_val, 1));
1428  }
1429 
1430  if ($tag[0]["type"] == "table" && is_int(strpos($par["parameter"], "border"))) {
1431  $t_border[$cur_par] = $cur_val;
1432  }
1433 
1434  if (in_array($cur_par, array("background-image", "list-style-image"))) {
1435  if (is_int(strpos($cur_val, "/"))) { // external
1436  $cur_val = "url('" . $cur_val . "')";
1437  } else { // internal
1438  if ($a_image_dir == "") {
1439  $cur_val = "url('../sty/sty_" . $this->getId() . "/images/" . $cur_val . "')";
1440  } else {
1441  $cur_val = "url('" . $a_image_dir . "/" . $cur_val . "')";
1442  }
1443  }
1444  }
1445 
1446  if ($cur_par == "opacity") {
1447  $cur_val = ((int) $cur_val) / 100;
1448  }
1449 
1450  fwrite($css_file, "\t" . $cur_par . ": " . $cur_val . ";\n");
1451 
1452  // IE6 fix for minimum height
1453  /*
1454  if ($cur_par == "min-height") {
1455  fwrite($css_file, "\t" . "height" . ": " . "auto !important" . ";\n");
1456  fwrite($css_file, "\t" . "height" . ": " . $cur_val . ";\n");
1457  }*/
1458 
1459  // opacity fix
1460  if ($cur_par == "opacity") {
1461  fwrite($css_file, "\t" . '-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=' . ($cur_val * 100) . ')"' . ";\n");
1462  fwrite($css_file, "\t" . 'filter: alpha(opacity=' . ($cur_val * 100) . ')' . ";\n");
1463  fwrite($css_file, "\t" . '-moz-opacity: ' . $cur_val . ";\n");
1464  }
1465 
1466  // transform fix
1467  if ($cur_par == "transform") {
1468  fwrite($css_file, "\t" . '-webkit-transform: ' . $cur_val . ";\n");
1469  fwrite($css_file, "\t" . '-moz-transform: ' . $cur_val . ";\n");
1470  fwrite($css_file, "\t" . '-ms-transform: ' . $cur_val . ";\n");
1471  }
1472 
1473  // transform-origin fix
1474  if ($cur_par == "transform-origin") {
1475  fwrite($css_file, "\t" . '-webkit-transform-origin: ' . $cur_val . ";\n");
1476  fwrite($css_file, "\t" . '-moz-transform-origin: ' . $cur_val . ";\n");
1477  fwrite($css_file, "\t" . '-ms-transform-origin: ' . $cur_val . ";\n");
1478  }
1479 
1480  // save page background
1481  if ($tag[0]["tag"] == "div" && $tag[0]["class"] == "Page"
1482  && $cur_par == "background-color") {
1483  $page_background = $cur_val;
1484  }
1485  }
1486  fwrite($css_file, "}\n");
1487  fwrite($css_file, "\n");
1488 
1489  // use table border attributes for th td as well
1490  /* if ($tag[0]["type"] == "table")
1491  {
1492  if (count($t_border) > 0)
1493  {
1494  fwrite ($css_file, $tag[0]["tag"].".ilc_".$tag[0]["type"]."_".$tag[0]["class"]." th,".
1495  $tag[0]["tag"].".ilc_".$tag[0]["type"]."_".$tag[0]["class"]." td\n");
1496  fwrite ($css_file, "{\n");
1497  foreach ($t_border as $p => $v)
1498  {
1499  // fwrite ($css_file, "\t".$p.": ".$v.";\n");
1500  }
1501  fwrite ($css_file, "}\n");
1502  fwrite ($css_file, "\n");
1503  }
1504  }*/
1505  }
1506 
1507  if ($page_background != "") {
1508  fwrite($css_file, "td.ilc_Page\n");
1509  fwrite($css_file, "{\n");
1510  fwrite($css_file, "\t" . "background-color: " . $page_background . ";\n");
1511  fwrite($css_file, "}\n");
1512  }
1513  if ($mq["id"] > 0) {
1514  fwrite($css_file, "}\n");
1515  }
1516  }
1517  fclose($css_file);
1518  // exit;
1519  $this->setUpToDate();
1520  $this->_writeUpToDate($this->getId(), true);
1521  }
1522 
1529  public static function getEffectiveContentStyleId(
1530  int $a_style_id
1531  ): int {
1532  global $DIC;
1533 
1534  $ilSetting = $DIC->settings();
1535 
1536  // check global fixed content style
1537  $fixed_style = $ilSetting->get("fixed_content_style_id");
1538  if ($fixed_style > 0) {
1539  $a_style_id = (int) $fixed_style;
1540  }
1541 
1542  // check global default style
1543  if ($a_style_id <= 0) {
1544  $a_style_id = (int) $ilSetting->get("default_content_style_id");
1545  }
1546 
1547  if ($a_style_id > 0 && ilObject::_lookupType($a_style_id) === "sty") {
1548  return $a_style_id;
1549  }
1550 
1551  return 0;
1552  }
1553 
1554  public function getParametersOfClass(
1555  string $a_type,
1556  string $a_class,
1557  int $a_mq_id = 0
1558  ): array {
1559  if (is_array($this->style_class[$a_type][$a_class][$a_mq_id])) {
1560  return $this->style_class[$a_type][$a_class][$a_mq_id];
1561  }
1562  return array();
1563  }
1564 
1569  public static function getContentStylePath(
1570  int $a_style_id,
1571  bool $add_random = true,
1572  bool $add_token = true
1573  ): string {
1574  global $DIC;
1575 
1576  $ilSetting = $DIC->settings();
1577 
1578  $random = new \ilRandom();
1579  $rand = $random->int(1, 999999);
1580 
1581 
1582  // check global fixed content style
1583  $fixed_style = $ilSetting->get("fixed_content_style_id");
1584  if ($fixed_style > 0) {
1585  $a_style_id = (int) $fixed_style;
1586  }
1587 
1588  // check global default style
1589  if ($a_style_id <= 0) {
1590  $a_style_id = (int) $ilSetting->get("default_content_style_id");
1591  }
1592 
1593  if ($a_style_id > 0 && ilObject::_exists($a_style_id)) {
1594  // check whether file is up to date
1595  if (!ilObjStyleSheet::_lookupUpToDate($a_style_id)) {
1596  $style = new ilObjStyleSheet($a_style_id);
1597  $style->writeCSSFile();
1598  }
1599 
1600  $path = ilFileUtils::getWebspaceDir("output") . "/css/style_" . $a_style_id . ".css";
1601  if ($add_random) {
1602  $path .= "?dummy=$rand";
1603  }
1604  if ($add_token) {
1606  }
1607 
1608  return $path;
1609  } else { // todo: work this out
1610  return "./Services/COPage/css/content.css";
1611  }
1612  }
1613 
1614  public static function getContentPrintStyle(): string
1615  {
1616  return "./Services/COPage/css/print_content.css";
1617  }
1618 
1619  public static function getSyntaxStylePath(): string
1620  {
1621  return "./Services/COPage/css/syntaxhighlight.css";
1622  }
1623 
1624  public static function getBaseContentStylePath(): string
1625  {
1626  return "./Services/COPage/css/content_base.css";
1627  }
1628 
1629  public function update(): bool
1630  {
1631  $ilDB = $this->db;
1632 
1633  parent::update();
1634  $this->read(); // this could be done better
1635  $this->writeCSSFile();
1636 
1637  $q = "UPDATE style_data " .
1638  "SET category = " . $ilDB->quote($this->getScope(), "integer") .
1639  " WHERE id = " . $ilDB->quote($this->getId(), "integer");
1640  $ilDB->manipulate($q);
1641 
1642  return true;
1643  }
1644 
1648  public function updateStyleParameter(
1649  int $a_id,
1650  string $a_value
1651  ): void {
1652  $ilDB = $this->db;
1653 
1654  $q = "UPDATE style_parameter SET VALUE = " .
1655  $ilDB->quote($a_value, "text") . " WHERE id = " .
1656  $ilDB->quote($a_id, "integer");
1657  $style_set = $ilDB->manipulate($q);
1658  }
1659 
1660  public function getStyle(): array
1661  {
1662  return $this->style;
1663  }
1664 
1665  public function setStyle(array $a_style): void
1666  {
1667  $this->style = $a_style;
1668  }
1669 
1670  public function handleXmlString(string $a_str): string
1671  {
1672  return str_replace("&", "&amp;", $a_str);
1673  }
1674 
1679  public function getXML(): string
1680  {
1681  $xml = "<StyleSheet>\n";
1682 
1683  // title and description
1684  $xml .= "<Title>" . $this->handleXmlString($this->getTitle()) . "</Title>";
1685  $xml .= "<Description>" . $this->handleXmlString($this->getDescription()) . "</Description>\n";
1686 
1687  // style classes
1688  foreach ($this->chars as $char) {
1689  $xml .= "<Style Tag=\"" . ilObjStyleSheet::_determineTag($char["type"]) .
1690  "\" Type=\"" . $char["type"] . "\" Class=\"" . $char["class"] . "\">\n";
1691  foreach ($this->style as $style) {
1692  if ($style[0]["type"] == $char["type"] && $style[0]["class"] == $char["class"]) {
1693  foreach ($style as $tag) {
1694  $xml .= "<StyleParameter Name=\"" . $tag["parameter"] . "\" Value=\"" . $tag["value"] . "\" Custom=\"" . $tag["custom"] . "\" />\n";
1695  }
1696  }
1697  }
1698  $xml .= "</Style>\n";
1699  }
1700 
1701  // colors
1702  foreach ($this->getColors() as $color) {
1703  $xml .= "<StyleColor Name=\"" . $color["name"] . "\" Code=\"" . $color["code"] . "\"/>\n";
1704  }
1705 
1706  // templates
1708  foreach ($tcts as $tct => $v) {
1709  $ts = $this->getTemplates($tct);
1710 
1711  foreach ($ts as $t) {
1712  $xml .= "<StyleTemplate Type=\"" . $tct . "\" Name=\"" . $t["name"] . "\">\n";
1713  foreach ($t["classes"] as $ct => $c) {
1714  if ($c != "") {
1715  $xml .= "<StyleTemplateClass ClassType=\"" . $ct . "\" Class=\"" . $c . "\"/>\n";
1716  }
1717  }
1718  $xml .= "</StyleTemplate>\n";
1719  }
1720  }
1721 
1722 
1723  $xml .= "</StyleSheet>";
1724  //echo "<pre>".htmlentities($xml)."</pre>"; exit;
1725  return $xml;
1726  }
1727 
1728  public function createExportDirectory(): string
1729  {
1730  $sty_data_dir = ilFileUtils::getDataDir() . "/sty";
1731  ilFileUtils::makeDir($sty_data_dir);
1732  if (!is_writable($sty_data_dir)) {
1733  $this->ilias->raiseError("Style data directory (" . $sty_data_dir
1734  . ") not writeable.", $this->ilias->error_obj->FATAL);
1735  }
1736 
1737  $style_dir = $sty_data_dir . "/sty_" . $this->getId();
1738  ilFileUtils::makeDir($style_dir);
1739  if (!is_dir($style_dir)) {
1740  $this->ilias->raiseError("Creation of style directory failed (" .
1741  $style_dir . ").", $this->ilias->error_obj->FATAL);
1742  }
1743 
1744  // create export subdirectory
1745  $ex_dir = $style_dir . "/export";
1746  ilFileUtils::makeDir($ex_dir);
1747  if (!is_dir($ex_dir)) {
1748  $this->ilias->raiseError("Creation of Import Directory failed (" .
1749  $ex_dir . ").", $this->ilias->error_obj->FATAL);
1750  }
1751 
1752  return $ex_dir;
1753  }
1754 
1755  public function cleanExportDirectory(): void
1756  {
1757  $sty_data_dir = ilFileUtils::getDataDir() . "/sty";
1758  $style_dir = $sty_data_dir . "/sty_" . $this->getId();
1759  // create export subdirectory
1760  $ex_dir = $style_dir . "/export";
1761 
1762  if (is_dir($ex_dir)) {
1763  ilFileUtils::delDir($ex_dir, true);
1764  }
1765  }
1766 
1767  public function createExportSubDirectory()
1768  {
1769  $ex_dir = $this->createExportDirectory();
1770  $ex_sub_dir = $ex_dir . "/" . $this->getExportSubDir();
1771  ilFileUtils::makeDir($ex_sub_dir);
1772  if (!is_writable($ex_sub_dir)) {
1773  $this->ilias->raiseError("Style data directory (" . $ex_sub_dir
1774  . ") not writeable.", $this->ilias->error_obj->FATAL);
1775  }
1776  $ex_sub_images_dir = $ex_sub_dir . "/images";
1777  ilFileUtils::makeDir($ex_sub_images_dir);
1778  if (!is_writable($ex_sub_images_dir)) {
1779  $this->ilias->raiseError("Style data directory (" . $ex_sub_images_dir
1780  . ") not writeable.", $this->ilias->error_obj->FATAL);
1781  }
1782  }
1783 
1787  public function setExportSubDir(string $a_dir): void
1788  {
1789  $this->export_sub_dir = $a_dir;
1790  }
1791 
1795  public function getExportSubDir(): string
1796  {
1797  if ($this->export_sub_dir == "") {
1798  return "sty_" . $this->getId();
1799  } else {
1800  return $this->export_sub_dir;
1801  }
1802  }
1803 
1810  public function export(): string
1811  {
1812  $this->cleanExportDirectory();
1813  $ex_dir = $this->createExportDirectory();
1814  $this->createExportSubDirectory();
1815  $this->exportXML($ex_dir . "/" . $this->getExportSubDir());
1816  //echo "-".$this->getImagesDirectory()."-".$ex_dir."/".$this->getExportSubDir()."/images"."-";
1818  $this->getImagesDirectory(),
1819  $ex_dir . "/" . $this->getExportSubDir() . "/images"
1820  );
1821  if (is_file($ex_dir . "/" . $this->getExportSubDir() . ".zip")) {
1822  unlink($ex_dir . "/" . $this->getExportSubDir() . ".zip");
1823  }
1825  $ex_dir . "/" . $this->getExportSubDir(),
1826  $ex_dir . "/" . $this->getExportSubDir() . ".zip"
1827  );
1828 
1829  return $ex_dir . "/" . $this->getExportSubDir() . ".zip";
1830  }
1831 
1835  public function exportXML(string $a_dir): void
1836  {
1837  $file = $a_dir . "/style.xml";
1838 
1839  // open file
1840  if (!($fp = fopen($file, 'wb'))) {
1841  die("<strong>Error</strong>: Could not open \"" . $file . "\" for writing" .
1842  " in <strong>" . __FILE__ . "</strong> on line <strong>" . __LINE__ . "</strong><br />");
1843  }
1844 
1845  // set file permissions
1846  chmod($file, 0770);
1847 
1848  // write xml data into the file
1849  fwrite($fp, $this->getXML());
1850 
1851  // close file
1852  fclose($fp);
1853  }
1854 
1855  public function createImportDirectory(): string
1856  {
1857  $sty_data_dir = ilFileUtils::getDataDir() . "/sty";
1858  ilFileUtils::makeDir($sty_data_dir);
1859  if (!is_writable($sty_data_dir)) {
1860  $this->ilias->raiseError("Style data directory (" . $sty_data_dir
1861  . ") not writeable.", $this->ilias->error_obj->FATAL);
1862  }
1863 
1864  $style_dir = $sty_data_dir . "/sty_" . $this->getId();
1865  ilFileUtils::makeDir($style_dir);
1866  if (!is_dir($style_dir)) {
1867  $this->ilias->raiseError("Creation of style directory failed (" .
1868  $style_dir . ").", $this->ilias->error_obj->FATAL);
1869  }
1870 
1871  // create import subdirectory
1872  $im_dir = $style_dir . "/import";
1873  ilFileUtils::makeDir($im_dir);
1874  if (!is_dir($im_dir)) {
1875  $this->ilias->raiseError("Creation of Import Directory failed (" .
1876  $im_dir . ").", $this->ilias->error_obj->FATAL);
1877  }
1878 
1879  return $im_dir;
1880  }
1881 
1889  public function import($a_file): void
1890  {
1891  parent::create();
1892 
1893  $subdir = "";
1894  $im_dir = $this->createImportDirectory();
1895 
1896  // handle uploaded files
1897  if (is_array($a_file)) {
1899  $a_file["tmp_name"],
1900  $a_file["name"],
1901  $im_dir . "/" . $a_file["name"]
1902  );
1903  $file_name = $a_file["name"];
1904  } else { // handle not directly uploaded files
1905  $pi = pathinfo($a_file);
1906  $file_name = $pi["basename"];
1907  copy($a_file, $im_dir . "/" . $file_name);
1908  }
1909  $file = pathinfo($file_name);
1910 
1911  // unzip file
1912  if (strtolower($file["extension"]) == "zip") {
1913  $this->domain->resources()->zip()->unzipFile($im_dir . "/" . $file_name);
1914  $subdir = basename($file["basename"], "." . $file["extension"]);
1915  if (!is_dir($im_dir . "/" . $subdir)) {
1916  $subdir = "style"; // check style subdir
1917  }
1918  $xml_file = $im_dir . "/" . $subdir . "/style.xml";
1919  } else { // handle xml file directly (old style)
1920  $xml_file = $im_dir . "/" . $file_name;
1921  }
1922 
1923  // load information from xml file
1924  //echo "-$xml_file-";
1925  $this->createFromXMLFile($xml_file, true);
1926 
1927  // copy images
1928  $this->createImagesDirectory();
1929  if (is_dir($im_dir . "/" . $subdir . "/images")) {
1931  $im_dir . "/" . $subdir . "/images",
1932  $this->getImagesDirectory()
1933  );
1934  }
1935 
1937  $this->read();
1938  $this->writeCSSFile();
1939  }
1940 
1945  public function createFromXMLFile(
1946  string $a_file,
1947  bool $a_skip_parent_create = false
1948  ): void {
1949  $ilDB = $this->db;
1950 
1951  $this->is_3_10_skin = false;
1952 
1953  if (!$a_skip_parent_create) {
1954  parent::create();
1955  }
1956  $importParser = new ilStyleImportParser($a_file, $this);
1957  $importParser->startParsing();
1958 
1959  // store style parameter
1960  foreach ($this->style as $style) {
1961  foreach ($style as $tag) {
1962  $id = $ilDB->nextId("style_parameter");
1963 
1964  // migrate old table PageFrame/PageContainer to div
1965  if (in_array($tag["class"], array("PageFrame", "PageContainer")) &&
1966  $tag["tag"] == "table") {
1967  $tag["tag"] = "div";
1968  if ($tag["parameter"] == "width" && $tag["value"] == "100%") {
1969  continue;
1970  }
1971  }
1972 
1973  if ($tag["type"] === "text_block" && $tag["tag"] === "div") {
1974  $tag["tag"] = "p";
1975  }
1976 
1977  $q = "INSERT INTO style_parameter (id,style_id, tag, class, parameter, type, value, custom) VALUES " .
1978  "(" .
1979  $ilDB->quote($id, "integer") . "," .
1980  $ilDB->quote($this->getId(), "integer") . "," .
1981  $ilDB->quote($tag["tag"], "text") . "," .
1982  $ilDB->quote($tag["class"], "text") . "," .
1983  $ilDB->quote($tag["parameter"], "text") . "," .
1984  $ilDB->quote($tag["type"], "text") . "," .
1985  $ilDB->quote($tag["value"], "text") . "," .
1986  $ilDB->quote((bool) $tag["custom"], "integer") .
1987  ")";
1988  $ilDB->manipulate($q);
1989  }
1990  }
1991 
1992  // store characteristics
1993  $this->is_3_10_skin = true;
1994  foreach ($this->chars as $char) {
1995  if ($char["type"] != "") {
1996  $s = substr($char["class"], strlen($char["class"]) - 6);
1997  if ($s != ":hover") {
1998  $ilDB->replace(
1999  "style_char",
2000  array(
2001  "style_id" => array("integer", $this->getId()),
2002  "type" => array("text", $char["type"]),
2003  "characteristic" => array("text", ilStr::subStr($char["class"], 0, 30))),
2004  array("hide" => array("integer", 0))
2005  );
2006  $this->is_3_10_skin = false;
2007  }
2008  }
2009  }
2010 
2011  // add style_data record
2012  $q = "INSERT INTO style_data (id, uptodate) VALUES " .
2013  "(" . $ilDB->quote($this->getId(), "integer") . ", 0)";
2014  $ilDB->manipulate($q);
2015 
2016  $this->update();
2017  $this->read();
2018 
2019  if ($this->is_3_10_skin) {
2020  $this->do_3_10_Migration();
2021  }
2022  //$this->writeCSSFile();
2023  }
2024 
2028  public function getStyleParameterGroups(): array
2029  {
2030  $groups = array();
2031 
2032  foreach (self::$parameter as $parameter => $props) {
2033  $groups[$props["group"]][] = $parameter;
2034  }
2035  return $groups;
2036  }
2037 
2038  public static function _getStyleParameterInputType(string $par): string
2039  {
2040  $input = self::$parameter[$par]["input"];
2041  return $input;
2042  }
2043 
2044  public static function _getStyleParameterSubPar(string $par): string
2045  {
2046  $subpar = self::$parameter[$par]["subpar"];
2047  return $subpar;
2048  }
2049 
2050  public static function _getStyleParameters(
2051  string $a_tag = ""
2052  ): array {
2053  if ($a_tag == "") {
2054  return self::$parameter;
2055  }
2056  $par = array();
2057  foreach (self::$parameter as $k => $v) {
2058  if (isset(self::$filtered_groups[$v["group"]]) &&
2059  !in_array($a_tag, self::$filtered_groups[$v["group"]])) {
2060  continue;
2061  }
2062  $par[$k] = $v;
2063  }
2064  return $par;
2065  }
2066 
2067  public static function _getFilteredGroups(): array
2068  {
2069  return self::$filtered_groups;
2070  }
2071 
2072  public static function _getStyleParameterNumericUnits(
2073  bool $a_no_percentage = false
2074  ): array {
2075  if ($a_no_percentage) {
2076  return self::$num_unit_no_perc;
2077  }
2078  return self::$num_unit;
2079  }
2080 
2081  public static function _getStyleParameterValues(
2082  string $par
2083  ): array {
2084  return self::$parameter[$par]["values"];
2085  }
2086 
2087  public static function _getStyleSuperTypes(): array
2088  {
2089  return self::$style_super_types;
2090  }
2091 
2092  public static function _isExpandable(string $a_type): bool
2093  {
2094  return in_array($a_type, self::$expandable_types);
2095  }
2096 
2097  public static function _isHideable(string $a_type): bool
2098  {
2099  return in_array($a_type, self::$hideable_types);
2100  }
2101 
2102  public static function _getStyleSuperTypeForType(
2103  string $a_type
2104  ): string {
2105  foreach (self::$style_super_types as $s => $t) {
2106  if (in_array($a_type, $t)) {
2107  return $s;
2108  }
2109  if ($a_type == $s) {
2110  return $s;
2111  }
2112  }
2113  return "";
2114  }
2115 
2116  public static function _getCoreStyles(): array
2117  {
2118  $c_styles = array();
2119  foreach (self::$core_styles as $cstyle) {
2120  $c_styles[$cstyle["type"] . "." . ilObjStyleSheet::_determineTag($cstyle["type"]) . "." . $cstyle["class"]]
2121  = array("type" => $cstyle["type"],
2122  "tag" => ilObjStyleSheet::_determineTag($cstyle["type"]),
2123  "class" => $cstyle["class"]);
2124  }
2125  return $c_styles;
2126  }
2127 
2128  public static function isCoreStyle(
2129  string $a_type,
2130  string $a_class
2131  ): bool {
2132  foreach (self::$core_styles as $s) {
2133  if ($s["type"] == $a_type && $s["class"] == $a_class) {
2134  return true;
2135  }
2136  }
2137  return false;
2138  }
2139 
2143  public static function _getTemplateClassTypes(
2144  string $a_template_type = ""
2145  ): array {
2146  if ($a_template_type == "") {
2147  return self::$templates;
2148  }
2149  return self::$templates[$a_template_type];
2150  }
2151 
2152  public static function _getPseudoClasses(string $tag): array
2153  {
2154  return self::$pseudo_classes[$tag] ?? [];
2155  }
2156 
2157  // e.g. table, row_head > table_cell
2159  string $t,
2160  string $k
2161  ): string {
2162  return self::$templates[$t][$k];
2163  }
2164 
2165  public static function _determineTag(string $a_type): string
2166  {
2167  return self::$assigned_tags[$a_type];
2168  }
2169 
2170  public static function getAvailableParameters(): array
2171  {
2172  $pars = array();
2173  foreach (self::$parameter as $p => $v) {
2174  $pars[$p] = $v["values"];
2175  }
2176 
2177  return $pars;
2178  }
2179 
2183  public static function _addMissingStyleClassesToStyle(
2184  int $a_id
2185  ): void {
2186  $styles = array(array("id" => $a_id));
2188  }
2189 
2194  public static function _addMissingStyleClassesToAllStyles(
2195  ?array $a_styles = null
2196  ): void {
2197  global $DIC;
2198 
2199  $ilDB = $DIC->database();
2200 
2201  if (is_null($a_styles)) {
2202  $styles = ilObject::_getObjectsDataForType("sty");
2203  } else {
2204  $styles = $a_styles;
2205  }
2206  $core_styles = ilObjStyleSheet::_getCoreStyles();
2208 
2209  // get all core image files
2210  $core_images = array();
2211  $core_dir = self::$basic_style_image_dir;
2212  if (is_dir($core_dir)) {
2213  $dir = opendir($core_dir);
2214  while ($file = readdir($dir)) {
2215  if (substr($file, 0, 1) != "." && is_file($core_dir . "/" . $file)) {
2216  $core_images[] = $file;
2217  }
2218  }
2219  }
2220 
2221  foreach ($styles as $style) {
2222  $id = $style["id"];
2223 
2224  foreach ($core_styles as $cs) {
2225  // check, whether core style class exists
2226  $set = $ilDB->queryF(
2227  "SELECT * FROM style_char WHERE style_id = %s " .
2228  "AND type = %s AND characteristic = %s",
2229  array("integer", "text", "text"),
2230  array($id, $cs["type"], $cs["class"])
2231  );
2232 
2233  // if not, add core style class
2234  if (!($rec = $ilDB->fetchAssoc($set))) {
2235  $ilDB->manipulateF(
2236  "INSERT INTO style_char (style_id, type, characteristic) " .
2237  " VALUES (%s,%s,%s) ",
2238  array("integer", "text", "text"),
2239  array($id, $cs["type"], $cs["class"])
2240  );
2241 
2242  $xpath = new DOMXPath($bdom);
2243  $par_nodes = $xpath->query("/StyleSheet/Style[@Tag = '" . $cs["tag"] . "' and @Type='" .
2244  $cs["type"] . "' and @Class='" . $cs["class"] . "']/StyleParameter");
2245  foreach ($par_nodes as $par_node) {
2246  // check whether style parameter exists
2247  $set = $ilDB->queryF(
2248  "SELECT * FROM style_parameter WHERE style_id = %s " .
2249  "AND type = %s AND class = %s AND tag = %s AND parameter = %s",
2250  array("integer", "text", "text", "text", "text"),
2251  array($id, $cs["type"], $cs["class"],
2252  $cs["tag"], $par_node->getAttribute("Name"))
2253  );
2254 
2255  // if not, create style parameter
2256  if (!($ilDB->fetchAssoc($set))) {
2257  $spid = $ilDB->nextId("style_parameter");
2258  $st = $ilDB->manipulateF(
2259  "INSERT INTO style_parameter (id, style_id, type, class, tag, parameter, value) " .
2260  " VALUES (%s,%s,%s,%s,%s,%s,%s)",
2261  array("integer", "integer", "text", "text", "text", "text", "text"),
2262  array($spid, $id, $cs["type"], $cs["class"], $cs["tag"],
2263  $par_node->getAttribute("Name"), $par_node->getAttribute("Value"))
2264  );
2265  }
2266  }
2267  }
2268  }
2269 
2270  // now check, whether some core image files are missing
2273  foreach ($core_images as $cim) {
2274  if (!is_file($imdir . "/" . $cim)) {
2275  copy($core_dir . "/" . $cim, $imdir . "/" . $cim);
2276  }
2277  }
2278  }
2279  }
2280 
2281  //
2282  // Color management
2283  //
2284 
2288  public function do_3_10_Migration(): void
2289  {
2290  $ilDB = $this->db;
2291 
2292  $this->do_3_9_Migration($this->getId());
2293 
2294  $this->do_3_10_CharMigration($this->getId());
2295 
2296  // style_char: type for characteristic
2297  $st = $ilDB->prepareManip("UPDATE style_char SET type = ? WHERE characteristic = ?" .
2298  " AND style_id = ? ", array("text", "text", "integer"));
2299  $ilDB->execute($st, array("media_cont", "Media", $this->getId()));
2300  $ilDB->execute($st, array("media_caption", "MediaCaption", $this->getId()));
2301  $ilDB->execute($st, array("page_fn", "Footnote", $this->getId()));
2302  $ilDB->execute($st, array("page_nav", "LMNavigation", $this->getId()));
2303  $ilDB->execute($st, array("page_title", "PageTitle", $this->getId()));
2304  $ilDB->execute($st, array("page_cont", "Page", $this->getId()));
2305 
2306  // style_parameter: type for class
2307  $st = $ilDB->prepareManip("UPDATE style_parameter SET type = ? WHERE class = ?" .
2308  " AND style_id = ? ", array("text", "text", "integer"));
2309  $ilDB->execute($st, array("media_cont", "Media", $this->getId()));
2310  $ilDB->execute($st, array("media_caption", "MediaCaption", $this->getId()));
2311  $ilDB->execute($st, array("page_fn", "Footnote", $this->getId()));
2312  $ilDB->execute($st, array("page_nav", "LMNavigation", $this->getId()));
2313  $ilDB->execute($st, array("page_title", "PageTitle", $this->getId()));
2314  $ilDB->execute($st, array("table", "Page", $this->getId()));
2315 
2316  $st = $ilDB->prepareManip("UPDATE style_parameter SET tag = ? WHERE class = ?" .
2317  " AND style_id = ? ", array("text", "text", "integer"));
2318  $ilDB->execute($st, array("div", "MediaCaption", $this->getId()));
2319 
2320  // style_char: characteristic for characteristic
2321  $st = $ilDB->prepareManip("UPDATE style_char SET characteristic = ? WHERE characteristic = ?" .
2322  " AND style_id = ? ", array("text", "text", "integer"));
2323  $ilDB->execute($st, array("MediaContainer", "Media", $this->getId()));
2324  $ilDB->execute($st, array("PageContainer", "Page", $this->getId()));
2325 
2326  // style_parameter: class for class
2327  $st = $ilDB->prepareManip("UPDATE style_parameter SET class = ? WHERE class = ?" .
2328  " AND style_id = ? ", array("text", "text", "integer"));
2329  $ilDB->execute($st, array("MediaContainer", "Media", $this->getId()));
2330  $ilDB->execute($st, array("PageContainer", "Page", $this->getId()));
2331 
2332  // force rewriting of container style
2333  $st = $ilDB->prepareManip("DELETE FROM style_char WHERE type = ?" .
2334  " AND style_id = ? ", array("text", "integer"));
2335  $ilDB->execute($st, array("page_cont", $this->getId()));
2336  $st = $ilDB->prepareManip("DELETE FROM style_parameter WHERE type = ?" .
2337  " AND style_id = ? ", array("text", "integer"));
2338  $ilDB->execute($st, array("page_cont", $this->getId()));
2339  }
2340 
2346  public function do_3_10_CharMigration(int $a_id = 0): void
2347  {
2348  $ilDB = $this->db;
2349 
2350  $add_str = "";
2351  if ($a_id > 0) {
2352  $add_str = " AND style_id = " . $ilDB->quote($a_id, "integer");
2353  }
2354 
2355  $set = $ilDB->query($q = "SELECT DISTINCT style_id, tag, class FROM style_parameter WHERE " .
2356  $ilDB->equals("type", "", "text", true) . " " . $add_str);
2357 
2358  while ($rec = $ilDB->fetchAssoc($set)) {
2359  // derive types from tag
2360  $types = array();
2361  switch ($rec["tag"]) {
2362  case "div":
2363  case "p":
2364  if (in_array($rec["class"], array("Headline3", "Headline1",
2365  "Headline2", "TableContent", "List", "Standard", "Remark",
2366  "Additional", "Mnemonic", "Citation", "Example"))) {
2367  $types[] = "text_block";
2368  }
2369  if (in_array($rec["class"], array("Block", "Remark",
2370  "Additional", "Mnemonic", "Example", "Excursus", "Special"))) {
2371  $types[] = "section";
2372  }
2373  if (in_array($rec["class"], array("Page", "Footnote", "PageTitle", "LMNavigation"))) {
2374  $types[] = "page";
2375  }
2376  break;
2377 
2378  case "td":
2379  $types[] = "table_cell";
2380  break;
2381 
2382  case "a":
2383  if (in_array($rec["class"], array("ExtLink", "IntLink", "FootnoteLink"))) {
2384  $types[] = "link";
2385  }
2386  break;
2387 
2388  case "span":
2389  $types[] = "text_inline";
2390  break;
2391 
2392  case "table":
2393  $types[] = "table";
2394  break;
2395  }
2396 
2397  // check if style_char set exists
2398  foreach ($types as $t) {
2399  // check if second type already exists
2400  $set4 = $ilDB->queryF(
2401  "SELECT * FROM style_char " .
2402  " WHERE style_id = %s AND type = %s AND characteristic = %s",
2403  array("integer", "text", "text"),
2404  array($rec["style_id"], $t, $rec["class"])
2405  );
2406  if ($rec4 = $ilDB->fetchAssoc($set4)) {
2407  // ok
2408  } else {
2409  //echo "<br>1-".$rec["style_id"]."-".$t."-".$rec["class"]."-";
2410  $ilDB->manipulateF(
2411  "INSERT INTO style_char " .
2412  " (style_id, type, characteristic) VALUES " .
2413  " (%s,%s,%s) ",
2414  array("integer", "text", "text"),
2415  array($rec["style_id"], $t, $rec["class"])
2416  );
2417  }
2418  }
2419 
2420  // update types
2421  if ($rec["type"] == "") {
2422  if (count($types) > 0) {
2423  $ilDB->manipulateF(
2424  "UPDATE style_parameter SET type = %s " .
2425  " WHERE style_id = %s AND class = %s AND " . $ilDB->equals("type", "", "text", true),
2426  array("text", "integer", "text"),
2427  array($types[0], $rec["style_id"], $rec["class"])
2428  );
2429  //echo "<br>3-".$types[0]."-".$rec["style_id"]."-".$rec["class"]."-";
2430 
2431  // links extra handling
2432  if ($types[0] == "link") {
2433  $ilDB->manipulateF(
2434  "UPDATE style_parameter SET type = %s " .
2435  " WHERE style_id = %s AND (class = %s OR class = %s) AND " . $ilDB->equals("type", "", "text", true),
2436  array("text", "integer", "text", "text"),
2437  array($types[0], $rec["style_id"], $rec["class"] . ":visited",
2438  $rec["class"] . ":hover")
2439  );
2440  }
2441  }
2442 
2443  if (count($types) == 2) {
2444  // select all records of first type and add second type
2445  // records if necessary.
2446  $set2 = $ilDB->queryF(
2447  "SELECT * FROM style_parameter " .
2448  " WHERE style_id = %s AND class = %s AND type = %s",
2449  array("integer", "text", "text"),
2450  array($rec["style_id"], $rec["class"], $types[0])
2451  );
2452  while ($rec2 = $ilDB->fetchAssoc($set2)) {
2453  // check if second type already exists
2454  $set3 = $ilDB->queryF(
2455  "SELECT * FROM style_parameter " .
2456  " WHERE style_id = %s AND tag = %s AND class = %s AND type = %s AND parameter = %s",
2457  array("integer", "text", "text", "text", "text"),
2458  array($rec["style_id"], $rec["tag"], $rec["class"], $types[1], $rec["parameter"])
2459  );
2460  if ($rec3 = $ilDB->fetchAssoc($set3)) {
2461  // ok
2462  } else {
2463  $nid = $ilDB->nextId("style_parameter");
2464  $ilDB->manipulateF(
2465  "INSERT INTO style_parameter " .
2466  " (id, style_id, tag, class, parameter, value, type) VALUES " .
2467  " (%s, %s,%s,%s,%s,%s,%s) ",
2468  array("integer", "integer", "text", "text", "text", "text", "text"),
2469  array($nid, $rec2["style_id"], $rec2["tag"], $rec2["class"],
2470  $rec2["parameter"], $rec2["value"], $types[1])
2471  );
2472  }
2473  }
2474  }
2475  }
2476  }
2477  }
2478 
2482  public function do_3_9_Migration(int $a_id): void
2483  {
2484  $ilDB = $this->db;
2485 
2486  $classes = array("Example", "Additional", "Citation", "Mnemonic", "Remark");
2487  $pars = array("margin-top", "margin-bottom");
2488 
2489  foreach ($classes as $curr_class) {
2490  foreach ($pars as $curr_par) {
2491  $res2 = $ilDB->queryF(
2492  "SELECT id FROM style_parameter WHERE style_id = %s" .
2493  " AND tag = %s AND class= %s AND parameter = %s",
2494  array("integer", "text", "text", "text"),
2495  array($a_id, "p", $curr_class, $curr_par)
2496  );
2497  if ($row2 = $ilDB->fetchAssoc($res2)) {
2498  $ilDB->manipulateF(
2499  "UPDATE style_parameter SET value= %s WHERE id = %s",
2500  array("text", "integer"),
2501  array("10px", $row2["id"])
2502  );
2503  } else {
2504  $nid = $ilDB->nextId("style_parameter");
2505  $ilDB->manipulateF(
2506  "INSERT INTO style_parameter " .
2507  "(id, style_id, tag, class, parameter,value) VALUES (%s,%s,%s,%s,%s,%s)",
2508  array("integer", "integer", "text", "text", "text", "text"),
2509  array($nid, $a_id, "div", $curr_class, $curr_par, "10px")
2510  );
2511  }
2512  }
2513  }
2514 
2515  $ilDB->manipulateF(
2516  "UPDATE style_parameter SET tag = %s WHERE tag = %s and style_id = %s",
2517  array("text", "text", "integer"),
2518  array("div", "p", $a_id)
2519  );
2520  }
2521 
2525 
2529  public function getColors(): array
2530  {
2531  $ilDB = $this->db;
2532 
2533  $set = $ilDB->query("SELECT * FROM style_color WHERE " .
2534  "style_id = " . $ilDB->quote($this->getId(), "integer") . " " .
2535  "ORDER BY color_name");
2536 
2537  $colors = array();
2538  while ($rec = $ilDB->fetchAssoc($set)) {
2539  $colors[] = array(
2540  "name" => $rec["color_name"],
2541  "code" => $rec["color_code"]
2542  );
2543  }
2544 
2545  return $colors;
2546  }
2547 
2551  public function removeColor(string $a_name): void
2552  {
2553  $ilDB = $this->db;
2554 
2555  $ilDB->manipulate("DELETE FROM style_color WHERE " .
2556  " style_id = " . $ilDB->quote($this->getId(), "integer") . " AND " .
2557  " color_name = " . $ilDB->quote($a_name, "text"));
2558  }
2559 
2560  public function getColorCodeForName(string $a_name): string
2561  {
2562  $ilDB = $this->db;
2563  $a_i = "";
2564  $pos = strpos($a_name, "(");
2565  if ($pos > 0) {
2566  $a_i = substr($a_name, $pos + 1);
2567  $a_i = str_replace(")", "", $a_i);
2568  $a_name = substr($a_name, 0, $pos);
2569  }
2570 
2571  $set = $ilDB->query("SELECT color_code FROM style_color WHERE " .
2572  " style_id = " . $ilDB->quote($this->getId(), "integer") . " AND " .
2573  " color_name = " . $ilDB->quote($a_name, "text"));
2574  if ($rec = $ilDB->fetchAssoc($set)) {
2575  if ($a_i == "") {
2576  return "#" . $rec["color_code"];
2577  } else {
2578  return "#" . ilObjStyleSheet::_getColorFlavor(
2579  $rec["color_code"],
2580  (int) $a_i
2581  );
2582  }
2583  }
2584  return "";
2585  }
2586 
2590  public static function _getColorFlavor(
2591  string $a_rgb,
2592  int $a_i
2593  ): string {
2594  $rgb = ilObjStyleSheet::_explodeRGB($a_rgb, true);
2595  $hls = ilObjStyleSheet::_RGBToHLS($rgb);
2596 
2597  if ($a_i > 0) {
2598  $hls["l"] = $hls["l"] + ((255 - $hls["l"]) * ($a_i / 100));
2599  }
2600  if ($a_i < 0) {
2601  $hls["l"] = $hls["l"] - (($hls["l"]) * (-$a_i / 100));
2602  }
2603 
2604  $rgb = ilObjStyleSheet::_HLSToRGB($hls);
2605 
2606  foreach ($rgb as $k => $v) {
2607  $rgb[$k] = str_pad(dechex((int) $v), 2, "0", STR_PAD_LEFT);
2608  }
2609 
2610  return $rgb["r"] . $rgb["g"] . $rgb["b"];
2611  }
2612 
2616  public static function _explodeRGB(
2617  string $a_rgb,
2618  bool $as_dec = false
2619  ): array {
2620  $r["r"] = substr($a_rgb, 0, 2);
2621  $r["g"] = substr($a_rgb, 2, 2);
2622  $r["b"] = substr($a_rgb, 4, 2);
2623  if ($as_dec) {
2624  $r["r"] = self::hexdec($r["r"]);
2625  $r["g"] = self::hexdec($r["g"]);
2626  $r["b"] = self::hexdec($r["b"]);
2627  }
2628 
2629  return $r;
2630  }
2631 
2632  protected static function hexdec(string $hex): int
2633  {
2634  $hex = preg_replace("/[^a-fA-F0-9]+/", "", $hex);
2635  if ($hex === "") {
2636  $hex = "0";
2637  }
2638  return (int) hexdec($hex);
2639  }
2640 
2644  public static function _RGBToHLS(array $a_rgb): array
2645  {
2646  $r = $a_rgb["r"] / 255;
2647  $g = $a_rgb["g"] / 255;
2648  $b = $a_rgb["b"] / 255;
2649  $h = 0;
2650 
2651  // max / min
2652  $max = max($r, $g, $b);
2653  $min = min($r, $g, $b);
2654 
2655  //lightness
2656  $l = ($max + $min) / 2;
2657 
2658  if ($max == $min) {
2659  $s = 0;
2660  } else {
2661  if ($l < 0.5) {
2662  $s = ($max - $min) / ($max + $min);
2663  } else {
2664  $s = ($max - $min) / (2.0 - $max - $min);
2665  }
2666 
2667  if ($r == $max) {
2668  $h = ($g - $b) / ($max - $min);
2669  } elseif ($g == $max) {
2670  $h = 2.0 + ($b - $r) / ($max - $min);
2671  } elseif ($b == $max) {
2672  $h = 4.0 + ($r - $g) / ($max - $min);
2673  }
2674  }
2675 
2676  $hls["h"] = round(($h / 6) * 255);
2677  $hls["l"] = round($l * 255);
2678  $hls["s"] = round($s * 255);
2679 
2680  return $hls;
2681  }
2682 
2686  public static function _HLSToRGB(array $a_hls): array
2687  {
2688  $h = $a_hls["h"] / 255;
2689  $l = $a_hls["l"] / 255;
2690  $s = $a_hls["s"] / 255;
2691  $temp3 = 0;
2692 
2693  $rgb["r"] = $rgb["g"] = $rgb["b"] = 0;
2694 
2695  // If S=0, define R, G, and B all to L
2696  if ($s == 0) {
2697  $rgb["r"] = $rgb["g"] = $rgb["b"] = $l;
2698  } else {
2699  if ($l < 0.5) {
2700  $temp2 = $l * (1.0 + $s);
2701  } else {
2702  $temp2 = $l + $s - $l * $s;
2703  }
2704 
2705  $temp1 = 2.0 * $l - $temp2;
2706 
2707 
2708  # For each of R, G, B, compute another temporary value, temp3, as follows:
2709  foreach ($rgb as $k => $v) {
2710  switch ($k) {
2711  case "r":
2712  $temp3 = $h + 1.0 / 3.0;
2713  break;
2714 
2715  case "g":
2716  $temp3 = $h;
2717  break;
2718 
2719  case "b":
2720  $temp3 = $h - 1.0 / 3.0;
2721  break;
2722  }
2723  if ($temp3 < 0) {
2724  $temp3 = $temp3 + 1.0;
2725  }
2726  if ($temp3 > 1) {
2727  $temp3 = $temp3 - 1.0;
2728  }
2729 
2730  if (6.0 * $temp3 < 1) {
2731  $rgb[$k] = $temp1 + ($temp2 - $temp1) * 6.0 * $temp3;
2732  } elseif (2.0 * $temp3 < 1) {
2733  $rgb[$k] = $temp2;
2734  } elseif (3.0 * $temp3 < 2) {
2735  $rgb[$k] = $temp1 + ($temp2 - $temp1) * ((2.0 / 3.0) - $temp3) * 6.0;
2736  } else {
2737  $rgb[$k] = $temp1;
2738  }
2739  }
2740  }
2741 
2742  $rgb["r"] = round($rgb["r"] * 255);
2743  $rgb["g"] = round($rgb["g"] * 255);
2744  $rgb["b"] = round($rgb["b"] * 255);
2745 
2746  return $rgb;
2747  }
2748 
2749  //
2750  // Media queries
2751  //
2752 
2756 
2757  public function getMediaQueries(): array
2758  {
2759  $ilDB = $this->db;
2760 
2761  $set = $ilDB->query("SELECT * FROM sty_media_query WHERE " .
2762  "style_id = " . $ilDB->quote($this->getId(), "integer") . " " .
2763  "ORDER BY order_nr");
2764 
2765  $mq = array();
2766  while ($rec = $ilDB->fetchAssoc($set)) {
2767  $mq[] = $rec;
2768  }
2769 
2770  return $mq;
2771  }
2772 
2773  public function addMediaQuery(
2774  string $a_mquery,
2775  int $order_nr = 0
2776  ): int {
2777  $ilDB = $this->db;
2778 
2779  $id = $ilDB->nextId("sty_media_query");
2780  if ($order_nr == 0) {
2781  $order_nr = $this->getMaxMQueryOrderNr() + 10;
2782  }
2783 
2784  $ilDB->manipulate("INSERT INTO sty_media_query (id, style_id, mquery, order_nr)" .
2785  " VALUES (" .
2786  $ilDB->quote($id, "integer") . "," .
2787  $ilDB->quote($this->getId(), "integer") . "," .
2788  $ilDB->quote($a_mquery, "text") . "," .
2789  $ilDB->quote($order_nr, "integer") .
2790  ")");
2791 
2792  return $id;
2793  }
2794 
2798  public function getMaxMQueryOrderNr(): int
2799  {
2800  $ilDB = $this->db;
2801 
2802  $set = $ilDB->query(
2803  "SELECT max(order_nr) mnr FROM sty_media_query " .
2804  " WHERE style_id = " . $ilDB->quote($this->getId(), "integer")
2805  );
2806  $rec = $ilDB->fetchAssoc($set);
2807 
2808  return (int) $rec["mnr"];
2809  }
2810 
2811  public function updateMediaQuery(
2812  int $a_id,
2813  string $a_mquery
2814  ): void {
2815  $ilDB = $this->db;
2816 
2817  $ilDB->manipulate(
2818  "UPDATE sty_media_query SET " .
2819  " mquery = " . $ilDB->quote($a_mquery, "text") .
2820  " WHERE id = " . $ilDB->quote($a_id, "integer")
2821  );
2822  }
2823 
2830  public function getMediaQueryForId(
2831  int $a_id
2832  ): array {
2833  $ilDB = $this->db;
2834 
2835  $set = $ilDB->query(
2836  "SELECT * FROM sty_media_query " .
2837  " WHERE id = " . $ilDB->quote($a_id, "integer")
2838  );
2839  return $ilDB->fetchAssoc($set);
2840  }
2841 
2847  public function deleteMediaQuery(
2848  int $a_id
2849  ): void {
2850  $ilDB = $this->db;
2851 
2852  $ilDB->manipulate(
2853  "DELETE FROM sty_media_query WHERE " .
2854  " style_id = " . $ilDB->quote($this->getId(), "integer") .
2855  " AND id = " . $ilDB->quote($a_id, "integer")
2856  );
2857  $this->saveMediaQueryOrder();
2858  }
2859 
2863  public function saveMediaQueryOrder(
2864  array $a_order_nr = null
2865  ): void {
2866  $ilDB = $this->db;
2867 
2868  $mqueries = $this->getMediaQueries();
2869  if (is_array($a_order_nr)) {
2870  foreach ($mqueries as $k => $mq) {
2871  $mqueries[$k]["order_nr"] = $a_order_nr[$mq["id"]];
2872  }
2873  $mqueries = ilArrayUtil::sortArray($mqueries, "order_nr", "", true);
2874  }
2875  $cnt = 10;
2876  foreach ($mqueries as $mq) {
2877  $ilDB->manipulate(
2878  "UPDATE sty_media_query SET " .
2879  " order_nr = " . $ilDB->quote($cnt, "integer") .
2880  " WHERE id = " . $ilDB->quote($mq["id"], "integer")
2881  );
2882  $cnt += 10;
2883  }
2884  }
2885 
2886 
2887  //
2888  // Table template management
2889  //
2890 
2894  public function getTemplates(
2895  string $a_type
2896  ): array {
2897  $ilDB = $this->db;
2898 
2899  $set = $ilDB->query("SELECT * FROM style_template WHERE " .
2900  "style_id = " . $ilDB->quote($this->getId(), "integer") . " AND " .
2901  "temp_type = " . $ilDB->quote($a_type, "text") . " " .
2902  "ORDER BY name");
2903 
2904  $templates = array();
2905  while ($rec = $ilDB->fetchAssoc($set)) {
2906  $rec["classes"] = $this->getTemplateClasses((int) $rec["id"]);
2907  $templates[] = $rec;
2908  }
2909 
2910  return $templates;
2911  }
2912 
2916  public function getTemplateClasses(
2917  int $a_tid
2918  ): array {
2919  $ilDB = $this->db;
2920  $set = $ilDB->query("SELECT * FROM style_template_class WHERE " .
2921  "template_id = " . $ilDB->quote($a_tid, "integer"));
2922 
2923  $class = array();
2924  while ($rec = $ilDB->fetchAssoc($set)) {
2925  $key = $rec["class_type"];
2926  $class[$key] = $rec["class"];
2927  }
2928 
2929  return $class;
2930  }
2931 
2932 
2936  public function addTemplate(
2937  string $a_type,
2938  string $a_name,
2939  array $a_classes
2940  ): int {
2941  $ilDB = $this->db;
2942 
2943  $tid = $ilDB->nextId("style_template");
2944  $ilDB->manipulate("INSERT INTO style_template " .
2945  "(id, style_id, name, temp_type)" .
2946  " VALUES (" .
2947  $ilDB->quote($tid, "integer") . "," .
2948  $ilDB->quote($this->getId(), "integer") . "," .
2949  $ilDB->quote($a_name, "text") . "," .
2950  $ilDB->quote($a_type, "text") .
2951  ")");
2952 
2953  foreach ($a_classes as $t => $c) {
2954  $ilDB->manipulate("INSERT INTO style_template_class " .
2955  "(template_id, class_type, class)" .
2956  " VALUES (" .
2957  $ilDB->quote($tid, "integer") . "," .
2958  $ilDB->quote($t, "text") . "," .
2959  $ilDB->quote($c, "text") .
2960  ")");
2961  }
2962 
2963  $this->writeTemplatePreview(
2964  $tid,
2965  ilObjStyleSheetGUI::_getTemplatePreview($this, $a_type, $tid, true)
2966  );
2967 
2968  return $tid;
2969  }
2970 
2974  public function updateTemplate(
2975  int $a_t_id,
2976  string $a_name,
2977  array $a_classes
2978  ): void {
2979  $ilDB = $this->db;
2980 
2981  $ilDB->manipulate("UPDATE style_template SET " .
2982  "name = " . $ilDB->quote($a_name, "text") .
2983  " WHERE id = " . $ilDB->quote($a_t_id, "integer"));
2984 
2985  $ilDB->manipulate(
2986  "DELETE FROM style_template_class WHERE " .
2987  "template_id = " . $ilDB->quote($a_t_id, "integer")
2988  );
2989  foreach ($a_classes as $t => $c) {
2990  $ilDB->manipulate("INSERT INTO style_template_class " .
2991  "(template_id, class_type, class)" .
2992  " VALUES (" .
2993  $ilDB->quote($a_t_id, "integer") . "," .
2994  $ilDB->quote($t, "text") . "," .
2995  $ilDB->quote($c, "text") .
2996  ")");
2997  }
2998  }
2999 
3000  public function addTemplateClass(
3001  int $a_t_id,
3002  string $a_type,
3003  string $a_class
3004  ): void {
3005  $ilDB = $this->db;
3006 
3007  $ilDB->manipulate("INSERT INTO style_template_class " .
3008  "(template_id, class_type, class)" .
3009  " VALUES (" .
3010  $ilDB->quote($a_t_id, "integer") . "," .
3011  $ilDB->quote($a_type, "text") . "," .
3012  $ilDB->quote($a_class, "text") .
3013  ")");
3014  }
3015 
3019  public function templateExists(
3020  string $a_template_name
3021  ): bool {
3022  $ilDB = $this->db;
3023 
3024  $set = $ilDB->query("SELECT * FROM style_template WHERE " .
3025  "style_id = " . $ilDB->quote($this->getId(), "integer") . " AND " .
3026  "name = " . $ilDB->quote($a_template_name, "text"));
3027  if ($ilDB->fetchAssoc($set)) {
3028  return true;
3029  }
3030  return false;
3031  }
3032 
3036  public function getTemplate(int $a_t_id): array
3037  {
3038  $ilDB = $this->db;
3039 
3040  $set = $ilDB->query("SELECT * FROM style_template WHERE " .
3041  "style_id = " . $ilDB->quote($this->getId(), "integer") . " " .
3042  " AND id = " . $ilDB->quote($a_t_id, "integer"));
3043 
3044  if ($rec = $ilDB->fetchAssoc($set)) {
3045  $rec["classes"] = $this->getTemplateClasses((int) $rec["id"]);
3046 
3047  $template = $rec;
3048  return $template;
3049  }
3050  return array();
3051  }
3052 
3056  public function lookupTemplateName(int $a_t_id): string
3057  {
3058  return self::_lookupTemplateName($a_t_id);
3059  }
3060 
3064  public static function _lookupTemplateName(int $a_t_id): ?string
3065  {
3066  global $DIC;
3067 
3068  $ilDB = $DIC->database();
3069 
3070  $set = $ilDB->query("SELECT name FROM style_template WHERE " .
3071  " id = " . $ilDB->quote($a_t_id, "integer"));
3072 
3073  if ($rec = $ilDB->fetchAssoc($set)) {
3074  return $rec["name"];
3075  }
3076 
3077  return null;
3078  }
3079 
3083  public function getTemplateXML(): string
3084  {
3085  $ilDB = $this->db;
3086 
3087  $tag = "<StyleTemplates>";
3088 
3089  $ttypes = array("table", "vaccordion", "haccordion", "carousel");
3090 
3091  foreach ($ttypes as $ttype) {
3092  $ts = $this->getTemplates($ttype);
3093 
3094  foreach ($ts as $t) {
3096  /*$atts = array("table" => "TableClass",
3097  "caption" => "CaptionClass",
3098  "row_head" => "RowHeadClass",
3099  "row_foot" => "RowFootClass",
3100  "col_head" => "ColHeadClass",
3101  "col_foot" => "ColFootClass",
3102  "odd_row" => "OddRowClass",
3103  "even_row" => "EvenRowClass",
3104  "odd_col" => "OddColClass",
3105  "even_col" => "EvenColClass");*/
3106  $c = $t["classes"];
3107 
3108  $tag .= '<StyleTemplate Name="' . $t["name"] . '">';
3109 
3110  foreach ($atts as $type => $t2) {
3111  if (($c[$type] ?? "") != "") {
3112  $tag .= '<StyleClass Type="' . $type . '" Value="' . $c[$type] . '" />';
3113  }
3114  }
3115 
3116  $tag .= "</StyleTemplate>";
3117  }
3118  }
3119 
3120  $tag .= "</StyleTemplates>";
3121 
3122  //echo htmlentities($tag);
3123  return $tag;
3124  }
3125 
3129  public function writeTemplatePreview(
3130  int $a_t_id,
3131  string $a_preview_html
3132  ): void {
3133  $ilDB = $this->db;
3134  $a_preview_html = str_replace(' width=""', "", $a_preview_html);
3135  $a_preview_html = str_replace(' valign="top"', "", $a_preview_html);
3136  $a_preview_html = str_replace('<div class="ilc_text_block_TableContent">', "<div>", $a_preview_html);
3137  //echo "1-".strlen($a_preview_html)."-";
3138  //echo htmlentities($a_preview_html);
3139  if (strlen($a_preview_html) > 4000) {
3140  //echo "2";
3141  $a_preview_html = "";
3142  }
3143  $ilDB->manipulate("UPDATE style_template SET " .
3144  "preview = " . $ilDB->quote($a_preview_html, "text") .
3145  " WHERE id = " . $ilDB->quote($a_t_id, "integer"));
3146  }
3147 
3151  public function lookupTemplatePreview(int $a_t_id): string
3152  {
3153  $ilDB = $this->db;
3154 
3155  $set = $ilDB->query("SELECT preview FROM style_template " .
3156  " WHERE id = " . $ilDB->quote($a_t_id, "integer"));
3157  if ($rec = $ilDB->fetchAssoc($set)) {
3158  return $rec["preview"] ?? "";
3159  }
3160 
3161  return "";
3162  }
3163 
3167  public static function _lookupTemplateIdByName(
3168  int $a_style_id,
3169  string $a_name
3170  ): ?int {
3171  global $DIC;
3172 
3173  $ilDB = $DIC->database();
3174 
3175  $set = $ilDB->query("SELECT id FROM style_template " .
3176  " WHERE style_id = " . $ilDB->quote($a_style_id, "integer") .
3177  " AND name = " . $ilDB->quote($a_name, "text"));
3178  if ($rec = $ilDB->fetchAssoc($set)) {
3179  return (int) $rec["id"];
3180  }
3181 
3182  return null;
3183  }
3184 
3188  public function removeTemplate(int $a_t_id): void
3189  {
3190  $ilDB = $this->db;
3191 
3192  $ilDB->manipulate("DELETE FROM style_template WHERE " .
3193  " style_id = " . $ilDB->quote($this->getId(), "integer") . " AND " .
3194  " id = " . $ilDB->quote($a_t_id, "integer"));
3195 
3196  $ilDB->manipulate(
3197  "DELETE FROM style_template_class WHERE " .
3198  "template_id = " . $ilDB->quote($a_t_id, "integer")
3199  );
3200  }
3201 
3202  public function writeStyleSetting(
3203  string $a_name,
3204  string $a_value
3205  ): void {
3206  $ilDB = $this->db;
3207 
3208  $ilDB->manipulate(
3209  "DELETE FROM style_setting WHERE " .
3210  " style_id = " . $ilDB->quote($this->getId(), "integer") .
3211  " AND name = " . $ilDB->quote($a_name, "text")
3212  );
3213 
3214  $ilDB->manipulate("INSERT INTO style_setting " .
3215  "(style_id, name, value) VALUES (" .
3216  $ilDB->quote($this->getId(), "integer") . "," .
3217  $ilDB->quote($a_name, "text") . "," .
3218  $ilDB->quote($a_value, "text") .
3219  ")");
3220  }
3221 
3225  public function lookupStyleSetting(string $a_name): string
3226  {
3227  $ilDB = $this->db;
3228 
3229  $set = $ilDB->query(
3230  "SELECT value FROM style_setting " .
3231  " WHERE style_id = " . $ilDB->quote($this->getId(), "integer") .
3232  " AND name = " . $ilDB->quote($a_name, "text")
3233  );
3234  $rec = $ilDB->fetchAssoc($set);
3235 
3236  return $rec["value"] ?? '';
3237  }
3238 
3242  public static function writeStyleUsage(
3243  int $a_obj_id,
3244  int $a_style_id
3245  ): void {
3246  global $DIC;
3247 
3248  $ilDB = $DIC->database();
3249 
3250  $ilDB->replace(
3251  "style_usage",
3252  array(
3253  "obj_id" => array("integer", $a_obj_id)),
3254  array(
3255  "style_id" => array("integer", $a_style_id))
3256  );
3257  }
3258 
3262  public static function lookupObjectStyle(
3263  int $a_obj_id
3264  ): int {
3265  global $DIC;
3266 
3267  $ilDB = $DIC->database();
3268 
3269  $set = $ilDB->query(
3270  "SELECT style_id FROM style_usage " .
3271  " WHERE obj_id = " . $ilDB->quote($a_obj_id, "integer")
3272  );
3273  $rec = $ilDB->fetchAssoc($set);
3274  if (!is_array($rec)) {
3275  return 0;
3276  }
3277 
3278  if (ilObject::_lookupType((int) $rec["style_id"]) == "sty") {
3279  return (int) $rec["style_id"];
3280  }
3281 
3282  return 0;
3283  }
3284 
3289  public static function lookupObjectForStyle(
3290  int $a_style_id
3291  ): array {
3292  global $DIC;
3293 
3294  $ilDB = $DIC->database();
3295 
3296  $obj_ids = array();
3297  if (ilObject::_lookupType($a_style_id) == "sty") {
3298  $set = $ilDB->query(
3299  "SELECT DISTINCT obj_id FROM style_usage " .
3300  " WHERE style_id = " . $ilDB->quote($a_style_id, "integer")
3301  );
3302 
3303  while ($rec = $ilDB->fetchAssoc($set)) {
3304  $obj_ids[] = (int) $rec["obj_id"];
3305  }
3306  }
3307  return $obj_ids;
3308  }
3309 }
static getWebspaceDir(string $mode="filesystem")
get webspace directory
templateExists(string $a_template_name)
Check whether template exists.
getXML()
get xml representation of style object todo: add mq_id
$res
Definition: ltiservices.php:69
string $type
static getEffectiveContentStyleId(int $a_style_id)
Get effective Style Id.
static _writeActive(int $a_id, bool $a_active)
updateMediaQuery(int $a_id, string $a_mquery)
static string $basic_style_image_dir
static _explodeRGB(string $a_rgb, bool $as_dec=false)
Explode an RGB string into an array.
static _isExpandable(string $a_type)
do_3_10_Migration()
Migrates 3.10 style to 3.11 style.
static _lookupTemplateIdByName(int $a_style_id, string $a_name)
Lookup table template preview.
txt(string $a_topic, string $a_default_lang_fallback_mod="")
gets the text for a given topic if the topic is not in the list, the topic itself with "-" will be re...
static _getStandardStyles(bool $a_exclude_default_style=false, bool $a_include_deactivated=false, int $a_scope=0)
Get standard styles.
ILIAS Style Content InternalRepoService $repo
static _addMissingStyleClassesToAllStyles(?array $a_styles=null)
Add missing style classes to all styles todo: add mq_id and custom handling.
characteristicExists(string $a_char, string $a_style_type)
Check whether characteristic exists.
getColors()
Get colors of style.
read()
read style properties
static _getImagesDirectory(int $a_style_id)
static _writeScope(int $a_id, int $a_scope)
static _getStyleParameterNumericUnits(bool $a_no_percentage=false)
getMediaQueryForId(int $a_id)
Get media query for id.
exportXML(string $a_dir)
export style xml file to directory
addParameter(string $a_tag, string $a_par, string $a_type, int $a_mq_id=0, bool $a_custom=false)
write style parameter to db
InternalDomainService $domain
static _getAllReferences(int $id)
get all reference ids for object ID
writeStyleSetting(string $a_name, string $a_value)
static array $pseudo_classes
getTemplateClasses(int $a_tid)
Get template classes.
ilTree $tree
isInTree(?int $a_node_id)
get all information of a node.
static _lookupContObjIdByStyleId(int $a_style_id)
handleXmlString(string $a_str)
static rCopy(string $a_sdir, string $a_tdir, bool $preserveTimeAttributes=false)
Copies content of a directory $a_sdir recursively to a directory $a_tdir.
setCharacteristics(array $a_chars)
static array $expandable_types
lookupTemplateName(int $a_t_id)
Lookup table template name for template ID.
static _getStyleParameterSubPar(string $par)
static _getObjectsDataForType(string $type, bool $omit_trash=false)
get all objects of a certain type
static _getStyleParameters(string $a_tag="")
loadLanguageModule(string $a_module)
Load language module.
static subStr(string $a_str, int $a_start, ?int $a_length=null)
Definition: class.ilStr.php:24
static _getClonableContentStyles()
Get all clonable styles (active standard styles and individual learning module styles with write perm...
static array $style_super_types
static _getTemplatePreview(ilObjStyleSheet $a_style, string $a_type, int $a_t_id, bool $a_small_mode=false)
Get table template preview.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _RGBToHLS(array $a_rgb)
RGB to HLS (both arrays, 0..255)
static _getPseudoClasses(string $tag)
static _writeStandard(int $a_id, bool $a_std)
Write standard flag.
static makeDirParents(string $a_dir)
Create a new directory and all parent directories.
static DOMDocument $basic_style_dom
addTemplateClass(int $a_t_id, string $a_type, string $a_class)
addTemplate(string $a_type, string $a_name, array $a_classes)
Add table template.
removeColor(string $a_name)
Remove a color.
$ilErr
Definition: raiseError.php:17
$path
Definition: ltiservices.php:32
setExportSubDir(string $a_dir)
Set local directory, that will be included within the zip file.
static isCoreStyle(string $a_type, string $a_class)
static _getStyleSuperTypeForType(string $a_type)
static writeOwner($obj_id, $style_id)
lookupStyleSetting(string $a_name)
Lookup style setting.
global $DIC
Definition: feed.php:28
deleteStyleParOfChar(string $a_type, string $a_class)
Delete style parameters of characteristic.
static _exists(int $id, bool $reference=false, ?string $type=null)
checks if an object exists in object_data
__construct(VocabulariesInterface $vocabularies)
updateTemplate(int $a_t_id, string $a_name, array $a_classes)
Update table template.
static _lookupTitle(int $obj_id)
static _lookupUpToDate(int $a_id)
ilLanguage $lng
getHideStatus(string $a_type, string $a_char)
Get characteristic hidden status.
getTemplateXML()
Get table template xml.
static _writeUpToDate(int $a_id, bool $a_up_to_date)
static delDir(string $a_dir, bool $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
ilDBInterface $db
static _createImagesDirectory(int $a_style_id)
Create images directory <data_dir>/sty/sty_<id>/images.
getTemplates(string $a_type)
Get table templates of style.
static _lookupStandard(int $a_id)
Lookup standard flag.
string $key
Consumer key/client ID value.
Definition: System.php:193
header include for all ilias files.
do_3_9_Migration(int $a_id)
Migrate old 3.9 styles.
deleteParameter(int $a_id)
delete style parameter
static string $basic_style_file
setStyle(array $a_style)
saveHideStatus(string $a_type, string $a_char, bool $a_hide)
Save characteristic hidden status.
writeTemplatePreview(int $a_t_id, string $a_preview_html)
Write table template preview.
static moveUploadedFile(string $a_file, string $a_name, string $a_target, bool $a_raise_errors=true, string $a_mode="move_uploaded")
move uploaded file
static _lookupActive(int $a_id)
Lookup active flag.
static getDataDir()
get data directory (outside webspace)
static _deleteStyleAssignments(int $a_style_id)
delete all style references to style
copyImagesToDir(string $a_target)
Copy images to directory.
static getContentStylePath(int $a_style_id, bool $add_random=true, bool $add_token=true)
get content style path static (to avoid full reading)
getStyleParameterGroups()
Get grouped parameter.
static _getColorFlavor(string $a_rgb, int $a_i)
Get color flavor.
static array $hideable_types
getColorCodeForName(string $a_name)
writeCSSFile(string $a_target_file="", string $a_image_dir="")
write css file to webspace directory
static _lookupTemplateName(int $a_t_id)
Lookup table template name for template ID.
getMaxMQueryOrderNr()
Get maximum media query order nr.
getCharacteristics(string $a_type="", bool $a_no_hidden=false, bool $a_include_core=true)
Get characteristics.
getPathId(int $a_endnode_id, int $a_startnode_id=0)
get path from a given startnode to a given endnode if startnode is not given the rootnode is startnod...
static array $num_unit_no_perc
static writeStyleUsage(int $a_obj_id, int $a_style_id)
Write style usage.
static _addMissingStyleClassesToStyle(int $a_id)
Add missing style classes to all styles.
__construct(int $a_id=0, bool $a_call_by_reference=false)
getExportSubDir()
The local directory, that will be included within the zip file.
global $ilSetting
Definition: privfeed.php:18
static _getStyleParameterValues(string $par)
static lookupObjectStyle(int $a_obj_id)
Lookup object style.
determineTemplateStyleClassType(string $t, string $k)
static zip(string $a_dir, string $a_file, bool $compress_content=false)
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
$q
Definition: shib_logout.php:21
static signFile(string $path_to_file)
static lookupObjectForStyle(int $a_style_id)
Lookup objects for style.
lookupTemplatePreview(int $a_t_id)
Lookup table template preview.
create(int $a_from_style=0, bool $a_import_mode=false)
Create a new style.
static string $basic_style_zip
static _HLSToRGB(array $a_hls)
HLS to RGB (both arrays, 0..255)
setUpToDate(bool $a_up_to_date=true)
Set style up to date (false + update will trigger css generation next time)
addCharacteristic(string $a_type, string $a_char, bool $a_hidden=false, int $order_nr=0, bool $outdated=false)
static array $filtered_groups
static _isHideable(string $a_type)
saveMediaQueryOrder(array $a_order_nr=null)
Save media query order.
static _determineTag(string $a_type)
static _lookupType(int $id, bool $reference=false)
static _getTemplateClassTypes(string $a_template_type="")
Get template class types.
deleteMediaQuery(int $a_id)
Delete media query.
$service
Definition: ltiservices.php:43
deleteCustomStylePars(string $a_tag, string $a_class, string $a_type, int $a_mq_id=0)
Delete style parameter by tag/class/parameter.
removeTemplate(int $a_t_id)
Remove table template.
static hexdec(string $hex)
do_3_10_CharMigration(int $a_id=0)
This is more or less a copy of Services/Migration/DBUpdate_1385/classes ilStyleMigration->addMissingS...
createImagesDirectory()
Create images directory <data_dir>/sty/sty_<id>/images.
getParametersOfClass(string $a_type, string $a_class, int $a_mq_id=0)
static makeDir(string $a_dir)
creates a new directory and inherits all filesystem permissions of the parent directory You may pass ...
createFromXMLFile(string $a_file, bool $a_skip_parent_create=false)
create style from xml file todo: add mq_id and custom
ilClone()
clone style sheet (note: styles have no ref ids and return an object id)
updateStyleParameter(int $a_id, string $a_value)
update style parameter per id
static _getStyleParameterInputType(string $par)
addMediaQuery(string $a_mquery, int $order_nr=0)
static sortArray(array $array, string $a_array_sortby_key, string $a_array_sortorder="asc", bool $a_numeric=false, bool $a_keep_keys=false)
$r