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, "<", ">", "&"); 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