1<?php
2/*
3    +-----------------------------------------------------------------------------+
4    | ILIAS open source                                                           |
5    +-----------------------------------------------------------------------------+
6    | Copyright (c) 1998-2007 ILIAS open source, University of Cologne            |
7    |                                                                             |
8    | This program is free software; you can redistribute it and/or               |
9    | modify it under the terms of the GNU General Public License                 |
10    | as published by the Free Software Foundation; either version 2              |
11    | of the License, or (at your option) any later version.                      |
12    |                                                                             |
13    | This program is distributed in the hope that it will be useful,             |
14    | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
15    | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
16    | GNU General Public License for more details.                                |
17    |                                                                             |
18    | You should have received a copy of the GNU General Public License           |
19    | along with this program; if not, write to the Free Software                 |
20    | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. |
21    +-----------------------------------------------------------------------------+
22*/
23
24include_once("./Services/Form/classes/class.ilSubEnabledFormPropertyGUI.php");
25include_once "./Services/RTE/classes/class.ilRTE.php";
26
27/**
28* This class represents a text area property in a property form.
29*
30* @author Alex Killing <alex.killing@gmx.de>
31* @version $Id$
32* @ingroup	ServicesForm
33*/
34class ilTextAreaInputGUI extends ilSubEnabledFormPropertyGUI
35{
36    protected $value;
37    protected $cols;
38    protected $rows;
39    protected $usert;
40    protected $rtetags;
41    protected $plugins;
42    protected $removeplugins;
43    protected $buttons;
44    protected $rtesupport;
45    protected $use_tags_for_rte_only = true;
46    protected $max_num_chars;
47    protected $min_num_chars;
48
49    /**
50     * @var int
51     */
52    protected $initial_rte_width = 795;
53
54    /**
55    * Array of tinymce buttons which should be disabled
56    *
57    * @var		Array
58    * @type		Array
59    * @access	protected
60    *
61    */
62    protected $disabled_buttons = array();
63
64    /**
65    * Use purifier or not
66    *
67    * @var		boolean
68    * @type		boolean
69    * @access	protected
70    *
71    */
72    protected $usePurifier = false;
73
74    /**
75    * Instance of ilHtmlPurifierInterface
76    *
77    * @var		ilHtmlPurifierInterface
78    * @type		ilHtmlPurifierInterface
79    * @access	protected
80    *
81    */
82    protected $Purifier = null;
83
84    /**
85    * TinyMCE root block element which surrounds the generated html
86    *
87    * @var		string
88    * @type		string
89    * @access	protected
90    */
91    protected $root_block_element = null;
92
93    protected $rte_tag_set = array(
94        "mini" => array("strong", "em", "u", "ol", "li", "ul", "blockquote", "a", "p", "span", "br"), // #13286/#17981
95        "standard" => array("strong", "em", "u", "ol", "li", "ul", "p", "div",
96            "i", "b", "code", "sup", "sub", "pre", "strike", "gap"),
97        "extended" => array(
98            "a","blockquote","br","cite","code","div","em","h1","h2","h3",
99            "h4","h5","h6","hr","li","ol","p",
100            "pre","span","strike","strong","sub","sup","u","ul",
101            "i", "b", "gap"),
102        "extended_img" => array(
103            "a","blockquote","br","cite","code","div","em","h1","h2","h3",
104            "h4","h5","h6","hr","img","li","ol","p",
105            "pre","span","strike","strong","sub","sup","u","ul",
106            "i", "b", "gap"),
107        "extended_table" => array(
108            "a","blockquote","br","cite","code","div","em","h1","h2","h3",
109            "h4","h5","h6","hr","li","ol","p",
110            "pre","span","strike","strong","sub","sup","table","td",
111            "tr","u","ul", "i", "b", "gap"),
112        "extended_table_img" => array(
113            "a","blockquote","br","cite","code","div","em","h1","h2","h3",
114            "h4","h5","h6","hr","img","li","ol","p",
115            "pre","span","strike","strong","sub","sup","table","td",
116            "tr","u","ul", "i", "b", "gap"),
117        "full" => array(
118            "a","blockquote","br","cite","code","div","em","h1","h2","h3",
119            "h4","h5","h6","hr","img","li","ol","p",
120            "pre","span","strike","strong","sub","sup","table","td",
121            "tr","u","ul","ruby","rbc","rtc","rb","rt","rp", "i", "b", "gap"));
122
123
124    /**
125    * Constructor
126    *
127    * @param	string	$a_title	Title
128    * @param	string	$a_postvar	Post Variable
129    */
130    public function __construct($a_title = "", $a_postvar = "")
131    {
132        global $DIC;
133
134        $this->lng = $DIC->language();
135        parent::__construct($a_title, $a_postvar);
136        $this->setType("textarea");
137        $this->setRteTagSet("standard");
138        $this->plugins = array();
139        $this->removeplugins = array();
140        $this->buttons = array();
141        $this->rteSupport = array();
142    }
143
144    /**
145    * Set Value.
146    *
147    * @param	string	$a_value	Value
148    */
149    public function setValue($a_value)
150    {
151        $this->value = $a_value;
152    }
153
154    /**
155    * Get Value.
156    *
157    * @return	string	Value
158    */
159    public function getValue()
160    {
161        return $this->value;
162    }
163
164    /**
165    * Set Cols.
166    *
167    * @deprecated
168    * @param	int	$a_cols	Cols
169    */
170    public function setCols($a_cols)
171    {
172        // obsolete because of bootstrap
173        $this->cols = $a_cols;
174    }
175
176    /**
177    * Get Cols.
178    *
179    * @return	int	Cols
180    */
181    public function getCols()
182    {
183        return $this->cols;
184    }
185
186    /**
187    * Set Rows.
188    *
189    * @param	int	$a_rows	Rows
190    */
191    public function setRows($a_rows)
192    {
193        $this->rows = $a_rows;
194    }
195
196    /**
197    * Get Rows.
198    *
199    * @return	int	Rows
200    */
201    public function getRows()
202    {
203        return $this->rows;
204    }
205
206    /**
207     * Set Maximum number of characters allowed.
208     *
209     * @param int $a_number	Characters
210     */
211    public function setMaxNumOfChars($a_number)
212    {
213        $this->max_num_chars = $a_number;
214    }
215
216    /**
217     * Get Maximum number of characters allowed.
218     */
219    public function getMaxNumOfChars()
220    {
221        return $this->max_num_chars;
222    }
223
224    /**
225     * Set Minimum number of characters allowed.
226     *
227     * @param int $a_number	Characters
228     */
229    public function setMinNumOfChars($a_number)
230    {
231        $this->min_num_chars = $a_number;
232    }
233
234    /**
235     * Get Minimum number of characters allowed.
236     */
237    public function getMinNumOfChars()
238    {
239        return $this->min_num_chars;
240    }
241
242    /**
243     * Set Use Rich Text Editing.
244     *
245     * @param	int	$a_usert	Use Rich Text Editing
246     * @param	string $version
247     */
248    public function setUseRte($a_usert, $version = '')
249    {
250        $this->usert = $a_usert;
251
252        if (strlen($version)) {
253            $this->rteSupport['version'] = $version;
254        }
255    }
256
257    /**
258    * Get Use Rich Text Editing.
259    *
260    * @return	int	Use Rich Text Editing
261    */
262    public function getUseRte()
263    {
264        return $this->usert;
265    }
266
267    /**
268    * Add RTE plugin.
269    *
270    * @param string $a_plugin Plugin name
271    */
272    public function addPlugin($a_plugin)
273    {
274        $this->plugins[$a_plugin] = $a_plugin;
275    }
276
277    /**
278    * Remove RTE plugin.
279    *
280    * @param string $a_plugin Plugin name
281    */
282    public function removePlugin($a_plugin)
283    {
284        $this->removeplugins[$a_plugin] = $a_plugin;
285    }
286
287    /**
288    * Add RTE button.
289    *
290    * @param string $a_button Button name
291    */
292    public function addButton($a_button)
293    {
294        $this->buttons[$a_button] = $a_button;
295    }
296
297    /**
298    * Remove RTE button.
299    *
300    * @param string $a_button Button name
301    */
302    public function removeButton($a_button)
303    {
304        unset($this->buttons[$a_button]);
305    }
306
307    /**
308    * Set RTE support for a special module
309    *
310    * @param int $obj_id Object ID
311    * @param string $obj_type Object Type
312    * @param string $module ILIAS module
313    */
314    public function setRTESupport($obj_id, $obj_type, $module, $cfg_template = null, $hide_switch = false, $version = null)
315    {
316        $this->rteSupport = array("obj_id" => $obj_id, "obj_type" => $obj_type, "module" => $module, 'cfg_template' => $cfg_template, 'hide_switch' => $hide_switch, 'version' => $version);
317    }
318
319    /**
320    * Remove RTE support for a special module
321    */
322    public function removeRTESupport()
323    {
324        $this->rteSupport = array();
325    }
326
327    /**
328    * Set Valid RTE Tags.
329    *
330    * @param	array	$a_rtetags	Valid RTE Tags
331    */
332    public function setRteTags($a_rtetags)
333    {
334        $this->rtetags = $a_rtetags;
335    }
336
337    /**
338    * Get Valid RTE Tags.
339    *
340    * @return	array	Valid RTE Tags
341    */
342    public function getRteTags()
343    {
344        return $this->rtetags;
345    }
346
347    /**
348    * Set Set of Valid RTE Tags
349    *
350    * @return	array	Set name "standard", "extended", "extended_img",
351    *					"extended_table", "extended_table_img", "full"
352    */
353    public function setRteTagSet($a_set_name)
354    {
355        $this->setRteTags($this->rte_tag_set[$a_set_name]);
356    }
357
358    /**
359    * Get Set of Valid RTE Tags
360    *
361    * @return	array	Set name "standard", "extended", "extended_img",
362    *					"extended_table", "extended_table_img", "full"
363    */
364    public function getRteTagSet($a_set_name)
365    {
366        return $this->rte_tag_set[$a_set_name];
367    }
368
369
370    /**
371    * RTE Tag string
372    */
373    public function getRteTagString()
374    {
375        $result = "";
376        foreach ($this->getRteTags() as $tag) {
377            $result .= "<$tag>";
378        }
379        return $result;
380    }
381
382    /**
383     * Set use tags for RTE only (default is true)
384     *
385     * @param boolean $a_val use tags for RTE only
386     */
387    public function setUseTagsForRteOnly($a_val)
388    {
389        $this->use_tags_for_rte_only = $a_val;
390    }
391
392    /**
393     * Get use tags for RTE only (default is true)
394     *
395     * @return boolean use tags for RTE only
396     */
397    public function getUseTagsForRteOnly()
398    {
399        return $this->use_tags_for_rte_only;
400    }
401
402    /**
403    * Set value by array
404    *
405    * @param	array	$a_values	value array
406    */
407    public function setValueByArray($a_values)
408    {
409        $this->setValue($a_values[$this->getPostVar()]);
410
411        foreach ($this->getSubItems() as $item) {
412            $item->setValueByArray($a_values);
413        }
414    }
415
416    /**
417    * Check input, strip slashes etc. set alert, if input is not ok.
418    *
419    * @return	boolean		Input ok, true/false
420    */
421    public function checkInput()
422    {
423        $lng = $this->lng;
424        include_once("./Services/AdvancedEditing/classes/class.ilObjAdvancedEditing.php");
425
426        if ($this->usePurifier() && $this->getPurifier()) {
427            $_POST[$this->getPostVar()] = ilUtil::stripOnlySlashes($_POST[$this->getPostVar()]);
428            $_POST[$this->getPostVar()] = $this->getPurifier()->purify($_POST[$this->getPostVar()]);
429        } else {
430            $allowed = $this->getRteTagString();
431            if ($this->plugins["latex"] == "latex" && !is_int(strpos($allowed, "<span>"))) {
432                $allowed .= "<span>";
433            }
434            $_POST[$this->getPostVar()] = ($this->getUseRte() || !$this->getUseTagsForRteOnly())
435                ? ilUtil::stripSlashes($_POST[$this->getPostVar()], true, $allowed)
436                : $this->stripSlashesAddSpaceFallback($_POST[$this->getPostVar()]);
437        }
438
439        $_POST[$this->getPostVar()] = self::removeProhibitedCharacters($_POST[$this->getPostVar()]);
440
441        if ($this->getRequired() && trim($_POST[$this->getPostVar()]) == "") {
442            $this->setAlert($lng->txt("msg_input_is_required"));
443
444            return false;
445        }
446
447        if ($this->isCharLimited()) {
448            //avoid whitespace surprises. #20630, #20674
449            $ascii_whitespaces = chr(194) . chr(160);
450            $ascii_breaklines = chr(13) . chr(10);
451
452            $to_replace = array($ascii_whitespaces, $ascii_breaklines, "&lt;", "&gt;", "&amp;");
453            $replace_to = array(' ', '', "_", "_", "_");
454
455            #20630 mbstring extension is mandatory for 5.4
456            $chars_entered = mb_strlen(strip_tags(str_replace($to_replace, $replace_to, $_POST[$this->getPostVar()])));
457
458            if ($this->getMaxNumOfChars() && ($chars_entered > $this->getMaxNumOfChars())) {
459                $this->setAlert($lng->txt("msg_input_char_limit_max"));
460
461                return false;
462            } elseif ($this->getMinNumOfChars() && ($chars_entered < $this->getMinNumOfChars())) {
463                $this->setAlert($lng->txt("msg_input_char_limit_min"));
464
465                return false;
466            }
467        }
468
469        return $this->checkSubItemsInput();
470    }
471
472    /**
473    * Insert property html
474    *
475    * @return	int	Size
476    */
477    public function insert($a_tpl)
478    {
479        $lng = $this->lng;
480
481        $ttpl = new ilTemplate("tpl.prop_textarea.html", true, true, "Services/Form");
482
483        // disabled rte
484        if ($this->getUseRte() && $this->getDisabled()) {
485            $ttpl->setCurrentBlock("disabled_rte");
486            $ttpl->setVariable("DR_VAL", $this->getValue());
487            $ttpl->parseCurrentBlock();
488        } else {
489            if ($this->getUseRte()) {
490                $rtestring = ilRTE::_getRTEClassname();
491                include_once "./Services/RTE/classes/class.$rtestring.php";
492                $rte = new $rtestring($this->rteSupport['version']);
493
494                $rte->setInitialWidth($this->getInitialRteWidth());
495
496                // @todo: Check this.
497                $rte->addPlugin("emotions");
498                foreach ($this->plugins as $plugin) {
499                    if (strlen($plugin)) {
500                        $rte->addPlugin($plugin);
501                    }
502                }
503                foreach ($this->removeplugins as $plugin) {
504                    if (strlen($plugin)) {
505                        $rte->removePlugin($plugin);
506                    }
507                }
508
509                foreach ($this->buttons as $button) {
510                    if (strlen($button)) {
511                        $rte->addButton($button);
512                    }
513                }
514
515                $rte->disableButtons($this->getDisabledButtons());
516
517                if ($this->getRTERootBlockElement() !== null) {
518                    $rte->setRTERootBlockElement($this->getRTERootBlockElement());
519                }
520
521                if (count($this->rteSupport) >= 3) {
522                    $rte->addRTESupport($this->rteSupport["obj_id"], $this->rteSupport["obj_type"], $this->rteSupport["module"], false, $this->rteSupport['cfg_template'], $this->rteSupport['hide_switch']);
523                } else {
524                    // disable all plugins for mini-tagset
525                    if (!array_diff($this->getRteTags(), $this->getRteTagSet("mini"))) {
526                        $rte->removeAllPlugins();
527
528                        // #13603 - "paste from word" is essential
529                        $rte->addPlugin("paste");
530
531                        // #11980 - p-tag is mandatory but we do not want the icons it comes with
532                        $rte->disableButtons(array("anchor", "justifyleft", "justifycenter",
533                            "justifyright", "justifyfull", "formatselect", "removeformat",
534                            "cut", "copy", "paste", "pastetext")); // JF, 2013-12-09
535                    }
536
537                    $rte->addCustomRTESupport(0, "", $this->getRteTags());
538                }
539
540                $ttpl->touchBlock("prop_ta_w");
541                $ttpl->setCurrentBlock("prop_textarea");
542                $ttpl->setVariable("ROWS", $this->getRows());
543            } else {
544                $ttpl->touchBlock("no_rteditor");
545
546                if ($this->getCols() > 5) {
547                    $ttpl->setCurrentBlock("prop_ta_c");
548                    $ttpl->setVariable("COLS", $this->getCols());
549                    $ttpl->parseCurrentBlock();
550                } else {
551                    $ttpl->touchBlock("prop_ta_w");
552                }
553
554                $ttpl->setCurrentBlock("prop_textarea");
555                $ttpl->setVariable("ROWS", $this->getRows());
556            }
557            if (!$this->getDisabled()) {
558                $ttpl->setVariable(
559                    "POST_VAR",
560                    $this->getPostVar()
561                );
562            }
563            $ttpl->setVariable("ID", $this->getFieldId());
564            if ($this->getDisabled()) {
565                $ttpl->setVariable('DISABLED', 'disabled="disabled" ');
566            }
567            $ttpl->setVariable("PROPERTY_VALUE", ilUtil::prepareFormOutput($this->getValue()));
568
569            if ($this->getRequired()) {
570                $ttpl->setVariable("REQUIRED", "required=\"required\"");
571            }
572
573            if ($this->isCharLimited()) {
574                $ttpl->setVariable("MAXCHARS", $this->getMaxNumOfChars());
575                $ttpl->setVariable("MINCHARS", $this->getMinNumOfChars());
576
577                $lng->toJS("form_chars_remaining");
578            }
579
580            $ttpl->parseCurrentBlock();
581        }
582
583        if ($this->isCharLimited()) {
584            $ttpl->setVariable("FEEDBACK_MAX_LIMIT", $this->getMaxNumOfChars());
585            $ttpl->setVariable("FEEDBACK_ID", $this->getFieldId());
586            $ttpl->setVariable("CHARS_REMAINING", $lng->txt("form_chars_remaining"));
587        }
588
589        if ($this->getDisabled()) {
590            $ttpl->setVariable(
591                "HIDDEN_INPUT",
592                $this->getHiddenTag($this->getPostVar(), $this->getValue())
593            );
594        }
595        $a_tpl->setCurrentBlock("prop_generic");
596        $a_tpl->setVariable("PROP_GENERIC", $ttpl->get());
597        $a_tpl->parseCurrentBlock();
598    }
599
600    /**
601    * Setter/Getter for the html purifier usage
602    *
603    * @param	boolean	$a_flag	Use purifier or not
604    * @return	mixed	Returns instance of ilTextAreaInputGUI or boolean
605    * @access	public
606    */
607    public function usePurifier($a_flag = null)
608    {
609        if (null === $a_flag) {
610            return $this->usePurifier;
611        }
612
613        $this->usePurifier = $a_flag;
614        return $this;
615    }
616
617    /**
618    * Setter for the html purifier
619    *
620    * @param	ilHtmlPurifierInterface	Instance of ilHtmlPurifierInterface
621    * @return	ilTextAreaInputGUI		Instance of ilTextAreaInputGUI
622    * @access	public
623    */
624    public function setPurifier(ilHtmlPurifierInterface $Purifier)
625    {
626        $this->Purifier = $Purifier;
627        return $this;
628    }
629
630    /**
631    * Getter for the html purifier
632    *
633    * @return	ilHtmlPurifierInterface	Instance of ilHtmlPurifierInterface
634    * @access	public
635    */
636    public function getPurifier()
637    {
638        return $this->Purifier;
639    }
640
641    /**
642    * Setter for the TinyMCE root block element
643    *
644    * @param	string				$a_root_block_element	root block element
645    * @return	ilTextAreaInputGUI	Instance of ilTextAreaInputGUI
646    * @access	public
647    */
648    public function setRTERootBlockElement($a_root_block_element)
649    {
650        $this->root_block_element = $a_root_block_element;
651        return $this;
652    }
653
654    /**
655    * Getter for the TinyMCE root block element
656    *
657    * @return	string	Root block element of TinyMCE
658    * @access	public
659    */
660    public function getRTERootBlockElement()
661    {
662        return $this->root_block_element;
663    }
664
665    /**
666    * Sets buttons which should be disabled in TinyMCE
667    *
668    * @param	mixed	$a_button	Either a button string or an array of button strings
669    * @return	ilTextAreaInputGUI	Instance of ilTextAreaInputGUI
670    * @access	public
671    *
672    */
673    public function disableButtons($a_button)
674    {
675        if (is_array($a_button)) {
676            $this->disabled_buttons = array_unique(array_merge($this->disabled_buttons, $a_button));
677        } else {
678            $this->disabled_buttons = array_unique(array_merge($this->disabled_buttons, array($a_button)));
679        }
680
681        return $this;
682    }
683
684    /**
685    * Returns the disabled TinyMCE buttons
686    *
687    * @param	boolean	$as_array	Should the disabled buttons be returned as a string or as an array
688    * @return	Array	Array of disabled buttons
689    * @access	public
690    *
691    */
692    public function getDisabledButtons($as_array = true)
693    {
694        if (!$as_array) {
695            return implode(',', $this->disabled_buttons);
696        } else {
697            return $this->disabled_buttons;
698        }
699    }
700
701    /**
702     * @return int
703     */
704    public function getInitialRteWidth()
705    {
706        return $this->initial_rte_width;
707    }
708
709    /**
710     * @param int $initial_rte_width
711     */
712    public function setInitialRteWidth($initial_rte_width)
713    {
714        $this->initial_rte_width = $initial_rte_width;
715    }
716
717    public function isCharLimited()
718    {
719        if ($this->getMaxNumOfChars() || $this->getMinNumOfChars()) {
720            return true;
721        }
722
723        return false;
724    }
725}
726