[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Joomla! Content Management System 5 * 6 * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> 7 * @license GNU General Public License version 2 or later; see LICENSE.txt 8 */ 9 10 namespace Joomla\CMS\Form; 11 12 use Joomla\CMS\Factory; 13 use Joomla\CMS\Filter\InputFilter; 14 use Joomla\CMS\Form\Field\SubformField; 15 use Joomla\CMS\Language\Text; 16 use Joomla\CMS\Layout\FileLayout; 17 use Joomla\CMS\Log\Log; 18 use Joomla\Database\DatabaseAwareInterface; 19 use Joomla\Database\DatabaseAwareTrait; 20 use Joomla\Database\DatabaseInterface; 21 use Joomla\Database\Exception\DatabaseNotFoundException; 22 use Joomla\Registry\Registry; 23 use Joomla\String\Normalise; 24 use Joomla\String\StringHelper; 25 26 // phpcs:disable PSR1.Files.SideEffects 27 \defined('JPATH_PLATFORM') or die; 28 // phpcs:enable PSR1.Files.SideEffects 29 30 /** 31 * Abstract Form Field class for the Joomla Platform. 32 * 33 * @since 1.7.0 34 */ 35 abstract class FormField implements DatabaseAwareInterface 36 { 37 use DatabaseAwareTrait; 38 39 /** 40 * The description text for the form field. Usually used in tooltips. 41 * 42 * @var string 43 * @since 1.7.0 44 */ 45 protected $description; 46 47 /** 48 * The hint text for the form field used to display hint inside the field. 49 * 50 * @var string 51 * @since 3.2 52 */ 53 protected $hint; 54 55 /** 56 * The autocomplete state for the form field. If 'off' element will not be automatically 57 * completed by browser. 58 * 59 * @var mixed 60 * @since 3.2 61 */ 62 protected $autocomplete = 'on'; 63 64 /** 65 * The spellcheck state for the form field. 66 * 67 * @var boolean 68 * @since 3.2 69 */ 70 protected $spellcheck = true; 71 72 /** 73 * The autofocus request for the form field. If true element will be automatically 74 * focused on document load. 75 * 76 * @var boolean 77 * @since 3.2 78 */ 79 protected $autofocus = false; 80 81 /** 82 * The SimpleXMLElement object of the `<field>` XML element that describes the form field. 83 * 84 * @var \SimpleXMLElement 85 * @since 1.7.0 86 */ 87 protected $element; 88 89 /** 90 * The Form object of the form attached to the form field. 91 * 92 * @var Form 93 * @since 1.7.0 94 */ 95 protected $form; 96 97 /** 98 * The form control prefix for field names from the Form object attached to the form field. 99 * 100 * @var string 101 * @since 1.7.0 102 */ 103 protected $formControl; 104 105 /** 106 * The hidden state for the form field. 107 * 108 * @var boolean 109 * @since 1.7.0 110 */ 111 protected $hidden = false; 112 113 /** 114 * Should the label be hidden when rendering the form field? This may be useful if you have the 115 * label rendering in a legend in your form field itself for radio buttons in a fieldset etc. 116 * If you use this flag you should ensure you display the label in your form (for a11y etc.) 117 * 118 * @var boolean 119 * @since 4.0.0 120 */ 121 protected $hiddenLabel = false; 122 123 /** 124 * Should the description be hidden when rendering the form field? This may be useful if you have the 125 * description rendering in your form field itself for e.g. note fields. 126 * 127 * @var boolean 128 * @since 4.0.0 129 */ 130 protected $hiddenDescription = false; 131 132 /** 133 * True to translate the field label string. 134 * 135 * @var boolean 136 * @since 1.7.0 137 */ 138 protected $translateLabel = true; 139 140 /** 141 * True to translate the field description string. 142 * 143 * @var boolean 144 * @since 1.7.0 145 */ 146 protected $translateDescription = true; 147 148 /** 149 * True to translate the field hint string. 150 * 151 * @var boolean 152 * @since 3.2 153 */ 154 protected $translateHint = true; 155 156 /** 157 * The document id for the form field. 158 * 159 * @var string 160 * @since 1.7.0 161 */ 162 protected $id; 163 164 /** 165 * The input for the form field. 166 * 167 * @var string 168 * @since 1.7.0 169 */ 170 protected $input; 171 172 /** 173 * The label for the form field. 174 * 175 * @var string 176 * @since 1.7.0 177 */ 178 protected $label; 179 180 /** 181 * The multiple state for the form field. If true then multiple values are allowed for the 182 * field. Most often used for list field types. 183 * 184 * @var boolean 185 * @since 1.7.0 186 */ 187 protected $multiple = false; 188 189 /** 190 * Allows extensions to create repeat elements 191 * 192 * @var mixed 193 * @since 3.2 194 */ 195 public $repeat = false; 196 197 /** 198 * The pattern (Reg Ex) of value of the form field. 199 * 200 * @var string 201 * @since 1.7.0 202 */ 203 protected $pattern; 204 205 /** 206 * The validation text of invalid value of the form field. 207 * 208 * @var string 209 * @since 4.0.0 210 */ 211 protected $validationtext; 212 213 /** 214 * The name of the form field. 215 * 216 * @var string 217 * @since 1.7.0 218 */ 219 protected $name; 220 221 /** 222 * The name of the field. 223 * 224 * @var string 225 * @since 1.7.0 226 */ 227 protected $fieldname; 228 229 /** 230 * The group of the field. 231 * 232 * @var string 233 * @since 1.7.0 234 */ 235 protected $group; 236 237 /** 238 * The required state for the form field. If true then there must be a value for the field to 239 * be considered valid. 240 * 241 * @var boolean 242 * @since 1.7.0 243 */ 244 protected $required = false; 245 246 /** 247 * The disabled state for the form field. If true then the field will be disabled and user can't 248 * interact with the field. 249 * 250 * @var boolean 251 * @since 3.2 252 */ 253 protected $disabled = false; 254 255 /** 256 * The readonly state for the form field. If true then the field will be readonly. 257 * 258 * @var boolean 259 * @since 3.2 260 */ 261 protected $readonly = false; 262 263 /** 264 * The form field type. 265 * 266 * @var string 267 * @since 1.7.0 268 */ 269 protected $type; 270 271 /** 272 * The validation method for the form field. This value will determine which method is used 273 * to validate the value for a field. 274 * 275 * @var string 276 * @since 1.7.0 277 */ 278 protected $validate; 279 280 /** 281 * The value of the form field. 282 * 283 * @var mixed 284 * @since 1.7.0 285 */ 286 protected $value; 287 288 /** 289 * The default value of the form field. 290 * 291 * @var mixed 292 * @since 1.7.0 293 */ 294 protected $default; 295 296 /** 297 * The size of the form field. 298 * 299 * @var integer 300 * @since 3.2 301 */ 302 protected $size; 303 304 /** 305 * The class of the form field 306 * 307 * @var mixed 308 * @since 3.2 309 */ 310 protected $class; 311 312 /** 313 * The label's CSS class of the form field 314 * 315 * @var mixed 316 * @since 1.7.0 317 */ 318 protected $labelclass; 319 320 /** 321 * The javascript onchange of the form field. 322 * 323 * @var string 324 * @since 3.2 325 */ 326 protected $onchange; 327 328 /** 329 * The javascript onclick of the form field. 330 * 331 * @var string 332 * @since 3.2 333 */ 334 protected $onclick; 335 336 /** 337 * The conditions to show/hide the field. 338 * 339 * @var string 340 * @since 3.7.0 341 */ 342 protected $showon; 343 344 /** 345 * The parent class of the field 346 * 347 * @var string 348 * @since 4.0.0 349 */ 350 protected $parentclass; 351 352 /** 353 * The count value for generated name field 354 * 355 * @var integer 356 * @since 1.7.0 357 */ 358 protected static $count = 0; 359 360 /** 361 * The string used for generated fields names 362 * 363 * @var string 364 * @since 1.7.0 365 */ 366 protected static $generated_fieldname = '__field'; 367 368 /** 369 * Name of the layout being used to render the field 370 * 371 * @var string 372 * @since 3.5 373 */ 374 protected $layout; 375 376 /** 377 * Layout to render the form field 378 * 379 * @var string 380 */ 381 protected $renderLayout = 'joomla.form.renderfield'; 382 383 /** 384 * Layout to render the label 385 * 386 * @var string 387 */ 388 protected $renderLabelLayout = 'joomla.form.renderlabel'; 389 390 /** 391 * The data-attribute name and values of the form field. 392 * For example, data-action-type="click" data-action-type="change" 393 * 394 * @var array 395 * 396 * @since 4.0.0 397 */ 398 protected $dataAttributes = array(); 399 400 /** 401 * Method to instantiate the form field object. 402 * 403 * @param Form $form The form to attach to the form field object. 404 * 405 * @since 1.7.0 406 */ 407 public function __construct($form = null) 408 { 409 // If there is a form passed into the constructor set the form and form control properties. 410 if ($form instanceof Form) { 411 $this->form = $form; 412 $this->formControl = $form->getFormControl(); 413 } 414 415 // Detect the field type if not set 416 if (!isset($this->type)) { 417 $parts = Normalise::fromCamelCase(\get_called_class(), true); 418 419 if ($parts[0] === 'J') { 420 $this->type = StringHelper::ucfirst($parts[\count($parts) - 1], '_'); 421 } else { 422 $this->type = StringHelper::ucfirst($parts[0], '_') . StringHelper::ucfirst($parts[\count($parts) - 1], '_'); 423 } 424 } 425 } 426 427 /** 428 * Method to get certain otherwise inaccessible properties from the form field object. 429 * 430 * @param string $name The property name for which to get the value. 431 * 432 * @return mixed The property value or null. 433 * 434 * @since 1.7.0 435 */ 436 public function __get($name) 437 { 438 switch ($name) { 439 case 'description': 440 case 'hint': 441 case 'formControl': 442 case 'hidden': 443 case 'id': 444 case 'multiple': 445 case 'name': 446 case 'required': 447 case 'type': 448 case 'validate': 449 case 'value': 450 case 'class': 451 case 'layout': 452 case 'labelclass': 453 case 'size': 454 case 'onchange': 455 case 'onclick': 456 case 'fieldname': 457 case 'group': 458 case 'disabled': 459 case 'readonly': 460 case 'autofocus': 461 case 'autocomplete': 462 case 'spellcheck': 463 case 'validationtext': 464 case 'showon': 465 case 'parentclass': 466 return $this->$name; 467 468 case 'input': 469 // If the input hasn't yet been generated, generate it. 470 if (empty($this->input)) { 471 $this->input = $this->getInput(); 472 } 473 474 return $this->input; 475 476 case 'label': 477 // If the label hasn't yet been generated, generate it. 478 if (empty($this->label)) { 479 $this->label = $this->getLabel(); 480 } 481 482 return $this->label; 483 484 case 'title': 485 return $this->getTitle(); 486 487 default: 488 // Check for data attribute 489 if (strpos($name, 'data-') === 0 && array_key_exists($name, $this->dataAttributes)) { 490 return $this->dataAttributes[$name]; 491 } 492 } 493 } 494 495 /** 496 * Method to set certain otherwise inaccessible properties of the form field object. 497 * 498 * @param string $name The property name for which to set the value. 499 * @param mixed $value The value of the property. 500 * 501 * @return void 502 * 503 * @since 3.2 504 */ 505 public function __set($name, $value) 506 { 507 switch ($name) { 508 case 'class': 509 // Removes spaces from left & right and extra spaces from middle 510 $value = preg_replace('/\s+/', ' ', trim((string) $value)); 511 512 // No break 513 514 case 'description': 515 case 'hint': 516 case 'value': 517 case 'labelclass': 518 case 'layout': 519 case 'onchange': 520 case 'onclick': 521 case 'validate': 522 case 'pattern': 523 case 'validationtext': 524 case 'group': 525 case 'showon': 526 case 'parentclass': 527 case 'default': 528 case 'autocomplete': 529 $this->$name = (string) $value; 530 break; 531 532 case 'id': 533 $this->id = $this->getId((string) $value, $this->fieldname); 534 break; 535 536 case 'fieldname': 537 $this->fieldname = $this->getFieldName((string) $value); 538 break; 539 540 case 'name': 541 $this->fieldname = $this->getFieldName((string) $value); 542 $this->name = $this->getName($this->fieldname); 543 break; 544 545 case 'multiple': 546 // Allow for field classes to force the multiple values option. 547 $value = (string) $value; 548 $value = $value === '' && isset($this->forceMultiple) ? (string) $this->forceMultiple : $value; 549 550 // No break 551 552 case 'required': 553 case 'disabled': 554 case 'readonly': 555 case 'autofocus': 556 case 'hidden': 557 $value = (string) $value; 558 $this->$name = ($value === 'true' || $value === $name || $value === '1'); 559 break; 560 561 case 'spellcheck': 562 case 'translateLabel': 563 case 'translateDescription': 564 case 'translateHint': 565 $value = (string) $value; 566 $this->$name = !($value === 'false' || $value === 'off' || $value === '0'); 567 break; 568 569 case 'translate_label': 570 $value = (string) $value; 571 $this->translateLabel = $this->translateLabel && !($value === 'false' || $value === 'off' || $value === '0'); 572 break; 573 574 case 'translate_description': 575 $value = (string) $value; 576 $this->translateDescription = $this->translateDescription && !($value === 'false' || $value === 'off' || $value === '0'); 577 break; 578 579 case 'size': 580 $this->$name = (int) $value; 581 break; 582 583 default: 584 // Detect data attribute(s) 585 if (strpos($name, 'data-') === 0) { 586 $this->dataAttributes[$name] = $value; 587 } else { 588 if (property_exists(__CLASS__, $name)) { 589 Log::add("Cannot access protected / private property $name of " . __CLASS__); 590 } else { 591 $this->$name = $value; 592 } 593 } 594 } 595 } 596 597 /** 598 * Method to attach a Form object to the field. 599 * 600 * @param Form $form The Form object to attach to the form field. 601 * 602 * @return FormField The form field object so that the method can be used in a chain. 603 * 604 * @since 1.7.0 605 */ 606 public function setForm(Form $form) 607 { 608 $this->form = $form; 609 $this->formControl = $form->getFormControl(); 610 611 return $this; 612 } 613 614 /** 615 * Method to attach a Form object to the field. 616 * 617 * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. 618 * @param mixed $value The form field value to validate. 619 * @param string $group The field name group control value. This acts as as an array container for the field. 620 * For example if the field has name="foo" and the group value is set to "bar" then the 621 * full field name would end up being "bar[foo]". 622 * 623 * @return boolean True on success. 624 * 625 * @since 1.7.0 626 */ 627 public function setup(\SimpleXMLElement $element, $value, $group = null) 628 { 629 // Make sure there is a valid FormField XML element. 630 if ((string) $element->getName() !== 'field') { 631 return false; 632 } 633 634 // Reset the input and label values. 635 $this->input = null; 636 $this->label = null; 637 638 // Set the XML element object. 639 $this->element = $element; 640 641 // Set the group of the field. 642 $this->group = $group; 643 644 $attributes = array( 645 'multiple', 'name', 'id', 'hint', 'class', 'description', 'labelclass', 'onchange', 'onclick', 'validate', 'pattern', 'validationtext', 646 'default', 'required', 'disabled', 'readonly', 'autofocus', 'hidden', 'autocomplete', 'spellcheck', 'translateHint', 'translateLabel', 647 'translate_label', 'translateDescription', 'translate_description', 'size', 'showon'); 648 649 $this->default = isset($element['value']) ? (string) $element['value'] : $this->default; 650 651 // Set the field default value. 652 if ($element['multiple'] && \is_string($value) && \is_array(json_decode($value, true))) { 653 $this->value = (array) json_decode($value); 654 } else { 655 $this->value = $value; 656 } 657 658 // Lets detect miscellaneous data attribute. For eg, data-* 659 foreach ($this->element->attributes() as $key => $value) { 660 if (strpos($key, 'data-') === 0) { 661 // Data attribute key value pair 662 $this->dataAttributes[$key] = $value; 663 } 664 } 665 666 foreach ($attributes as $attributeName) { 667 $this->__set($attributeName, $element[$attributeName]); 668 } 669 670 // Allow for repeatable elements 671 $repeat = (string) $element['repeat']; 672 $this->repeat = ($repeat === 'true' || $repeat === 'multiple' || (!empty($this->form->repeat) && $this->form->repeat == 1)); 673 674 // Set the visibility. 675 $this->hidden = ($this->hidden || strtolower((string) $this->element['type']) === 'hidden'); 676 677 $this->layout = !empty($this->element['layout']) ? (string) $this->element['layout'] : $this->layout; 678 679 $this->parentclass = isset($this->element['parentclass']) ? (string) $this->element['parentclass'] : $this->parentclass; 680 681 // Add required to class list if field is required. 682 if ($this->required) { 683 $this->class = trim($this->class . ' required'); 684 } 685 686 return true; 687 } 688 689 /** 690 * Simple method to set the value 691 * 692 * @param mixed $value Value to set 693 * 694 * @return void 695 * 696 * @since 3.2 697 */ 698 public function setValue($value) 699 { 700 $this->value = $value; 701 } 702 703 /** 704 * Method to get the id used for the field input tag. 705 * 706 * @param string $fieldId The field element id. 707 * @param string $fieldName The field element name. 708 * 709 * @return string The id to be used for the field input tag. 710 * 711 * @since 1.7.0 712 */ 713 protected function getId($fieldId, $fieldName) 714 { 715 $id = ''; 716 717 // If there is a form control set for the attached form add it first. 718 if ($this->formControl) { 719 $id .= $this->formControl; 720 } 721 722 // If the field is in a group add the group control to the field id. 723 if ($this->group) { 724 // If we already have an id segment add the group control as another level. 725 if ($id) { 726 $id .= '_' . str_replace('.', '_', $this->group); 727 } else { 728 $id .= str_replace('.', '_', $this->group); 729 } 730 } 731 732 // If we already have an id segment add the field id/name as another level. 733 if ($id) { 734 $id .= '_' . ($fieldId ?: $fieldName); 735 } else { 736 $id .= ($fieldId ?: $fieldName); 737 } 738 739 // Clean up any invalid characters. 740 $id = preg_replace('#\W#', '_', $id); 741 742 // If this is a repeatable element, add the repeat count to the ID 743 if ($this->repeat) { 744 $repeatCounter = empty($this->form->repeatCounter) ? 0 : $this->form->repeatCounter; 745 $id .= '-' . $repeatCounter; 746 747 if (strtolower($this->type) === 'radio') { 748 $id .= '-'; 749 } 750 } 751 752 return $id; 753 } 754 755 /** 756 * Method to get the field input markup. 757 * 758 * @return string The field input markup. 759 * 760 * @since 1.7.0 761 */ 762 protected function getInput() 763 { 764 if (empty($this->layout)) { 765 throw new \UnexpectedValueException(sprintf('%s has no layout assigned.', $this->name)); 766 } 767 768 return $this->getRenderer($this->layout)->render($this->getLayoutData()); 769 } 770 771 /** 772 * Method to get the field title. 773 * 774 * @return string The field title. 775 * 776 * @since 1.7.0 777 */ 778 protected function getTitle() 779 { 780 $title = ''; 781 782 if ($this->hidden) { 783 return $title; 784 } 785 786 // Get the label text from the XML element, defaulting to the element name. 787 $title = $this->element['label'] ? (string) $this->element['label'] : (string) $this->element['name']; 788 $title = $this->translateLabel ? Text::_($title) : $title; 789 790 return $title; 791 } 792 793 /** 794 * Method to get the field label markup. 795 * 796 * @return string The field label markup. 797 * 798 * @since 1.7.0 799 */ 800 protected function getLabel() 801 { 802 if ($this->hidden) { 803 return ''; 804 } 805 806 $data = $this->getLayoutData(); 807 808 // Forcing the Alias field to display the tip below 809 $position = $this->element['name'] === 'alias' ? ' data-bs-placement="bottom" ' : ''; 810 811 // Here mainly for B/C with old layouts. This can be done in the layouts directly 812 $extraData = array( 813 'text' => $data['label'], 814 'for' => $this->id, 815 'classes' => explode(' ', $data['labelclass']), 816 'position' => $position, 817 ); 818 819 return $this->getRenderer($this->renderLabelLayout)->render(array_merge($data, $extraData)); 820 } 821 822 /** 823 * Method to get the name used for the field input tag. 824 * 825 * @param string $fieldName The field element name. 826 * 827 * @return string The name to be used for the field input tag. 828 * 829 * @since 1.7.0 830 */ 831 protected function getName($fieldName) 832 { 833 // To support repeated element, extensions can set this in plugin->onRenderSettings 834 835 $name = ''; 836 837 // If there is a form control set for the attached form add it first. 838 if ($this->formControl) { 839 $name .= $this->formControl; 840 } 841 842 // If the field is in a group add the group control to the field name. 843 if ($this->group) { 844 // If we already have a name segment add the group control as another level. 845 $groups = explode('.', $this->group); 846 847 if ($name) { 848 foreach ($groups as $group) { 849 $name .= '[' . $group . ']'; 850 } 851 } else { 852 $name .= array_shift($groups); 853 854 foreach ($groups as $group) { 855 $name .= '[' . $group . ']'; 856 } 857 } 858 } 859 860 // If we already have a name segment add the field name as another level. 861 if ($name) { 862 $name .= '[' . $fieldName . ']'; 863 } else { 864 $name .= $fieldName; 865 } 866 867 // If the field should support multiple values add the final array segment. 868 if ($this->multiple) { 869 switch (strtolower((string) $this->element['type'])) { 870 case 'text': 871 case 'textarea': 872 case 'email': 873 case 'password': 874 case 'radio': 875 case 'calendar': 876 case 'editor': 877 case 'hidden': 878 break; 879 default: 880 $name .= '[]'; 881 } 882 } 883 884 return $name; 885 } 886 887 /** 888 * Method to get the field name used. 889 * 890 * @param string $fieldName The field element name. 891 * 892 * @return string The field name 893 * 894 * @since 1.7.0 895 */ 896 protected function getFieldName($fieldName) 897 { 898 if ($fieldName) { 899 return $fieldName; 900 } else { 901 self::$count = self::$count + 1; 902 903 return self::$generated_fieldname . self::$count; 904 } 905 } 906 907 /** 908 * Method to get an attribute of the field 909 * 910 * @param string $name Name of the attribute to get 911 * @param mixed $default Optional value to return if attribute not found 912 * 913 * @return mixed Value of the attribute / default 914 * 915 * @since 3.2 916 */ 917 public function getAttribute($name, $default = null) 918 { 919 if ($this->element instanceof \SimpleXMLElement) { 920 $attributes = $this->element->attributes(); 921 922 // Ensure that the attribute exists 923 if ($attributes->$name !== null) { 924 return (string) $attributes->$name; 925 } 926 } 927 928 return $default; 929 } 930 931 /** 932 * Method to get data attributes. For example, data-user-type 933 * 934 * @return array list of data attribute(s) 935 * 936 * @since 4.0.0 937 */ 938 public function getDataAttributes() 939 { 940 return $this->dataAttributes; 941 } 942 943 /** 944 * Method to render data attributes to html. 945 * 946 * @return string A HTML Tag Attribute string of data attribute(s) 947 * 948 * @since 4.0.0 949 */ 950 public function renderDataAttributes() 951 { 952 $dataAttribute = ''; 953 $dataAttributes = $this->getDataAttributes(); 954 955 if (!empty($dataAttributes)) { 956 foreach ($dataAttributes as $key => $attrValue) { 957 $dataAttribute .= ' ' . $key . '="' . htmlspecialchars($attrValue, ENT_COMPAT, 'UTF-8') . '"'; 958 } 959 } 960 961 return $dataAttribute; 962 } 963 964 /** 965 * Render a layout of this field 966 * 967 * @param string $layoutId Layout identifier 968 * @param array $data Optional data for the layout 969 * 970 * @return string 971 * 972 * @since 3.5 973 */ 974 public function render($layoutId, $data = array()) 975 { 976 $data = array_merge($this->getLayoutData(), $data); 977 978 return $this->getRenderer($layoutId)->render($data); 979 } 980 981 /** 982 * Method to get a control group with label and input. 983 * 984 * @param array $options Options to be passed into the rendering of the field 985 * 986 * @return string A string containing the html for the control group 987 * 988 * @since 3.2 989 */ 990 public function renderField($options = array()) 991 { 992 if ($this->hidden) { 993 return $this->getInput(); 994 } 995 996 if (!isset($options['class'])) { 997 $options['class'] = ''; 998 } 999 1000 $options['rel'] = ''; 1001 1002 if (empty($options['hiddenLabel'])) { 1003 if ($this->getAttribute('hiddenLabel')) { 1004 $options['hiddenLabel'] = $this->getAttribute('hiddenLabel') == 'true'; 1005 } else { 1006 $options['hiddenLabel'] = $this->hiddenLabel; 1007 } 1008 } 1009 1010 if (empty($options['hiddenDescription'])) { 1011 if ($this->getAttribute('hiddenDescription')) { 1012 $options['hiddenDescription'] = $this->getAttribute('hiddenDescription') == 'true'; 1013 } else { 1014 $options['hiddenDescription'] = $this->hiddenDescription; 1015 } 1016 } 1017 1018 $options['inlineHelp'] = isset($this->form->getXml()->config->inlinehelp['button']) 1019 ? ((string) $this->form->getXml()->config->inlinehelp['button'] == 'show' ?: false) 1020 : false; 1021 1022 if ($this->showon) { 1023 $options['rel'] = ' data-showon=\'' . 1024 json_encode(FormHelper::parseShowOnConditions($this->showon, $this->formControl, $this->group)) . '\''; 1025 $options['showonEnabled'] = true; 1026 } 1027 1028 $data = array( 1029 'input' => $this->getInput(), 1030 'label' => $this->getLabel(), 1031 'options' => $options, 1032 ); 1033 1034 $data = array_merge($this->getLayoutData(), $data); 1035 1036 return $this->getRenderer($this->renderLayout)->render($data); 1037 } 1038 1039 /** 1040 * Method to filter a field value. 1041 * 1042 * @param mixed $value The optional value to use as the default for the field. 1043 * @param string $group The optional dot-separated form group path on which to find the field. 1044 * @param Registry $input An optional Registry object with the entire data set to filter 1045 * against the entire form. 1046 * 1047 * @return mixed The filtered value. 1048 * 1049 * @since 4.0.0 1050 * @throws \UnexpectedValueException 1051 */ 1052 public function filter($value, $group = null, Registry $input = null) 1053 { 1054 // Make sure there is a valid SimpleXMLElement. 1055 if (!($this->element instanceof \SimpleXMLElement)) { 1056 throw new \UnexpectedValueException(sprintf('%s::filter `element` is not an instance of SimpleXMLElement', \get_class($this))); 1057 } 1058 1059 // Get the field filter type. 1060 $filter = (string) $this->element['filter']; 1061 1062 if ($filter !== '') { 1063 $required = ((string) $this->element['required'] === 'true' || (string) $this->element['required'] === 'required'); 1064 1065 if (($value === '' || $value === null) && !$required) { 1066 return ''; 1067 } 1068 1069 // Check for a callback filter 1070 if (strpos($filter, '::') !== false && \is_callable(explode('::', $filter))) { 1071 return \call_user_func(explode('::', $filter), $value); 1072 } 1073 1074 // Load the FormRule object for the field. FormRule objects take precedence over PHP functions 1075 $obj = FormHelper::loadFilterType($filter); 1076 1077 // Run the filter rule. 1078 if ($obj) { 1079 return $obj->filter($this->element, $value, $group, $input, $this->form); 1080 } 1081 1082 if (\function_exists($filter)) { 1083 return \call_user_func($filter, $value); 1084 } 1085 1086 if ($this instanceof SubformField) { 1087 $subForm = $this->loadSubForm(); 1088 1089 // Subform field may have a default value, that is a JSON string 1090 if ($value && is_string($value)) { 1091 $value = json_decode($value, true); 1092 1093 // The string is invalid json 1094 if (!$value) { 1095 return null; 1096 } 1097 } 1098 1099 if ($this->multiple) { 1100 $return = array(); 1101 1102 if ($value) { 1103 foreach ($value as $key => $val) { 1104 $return[$key] = $subForm->filter($val); 1105 } 1106 } 1107 } else { 1108 $return = $subForm->filter($value); 1109 } 1110 1111 return $return; 1112 } 1113 } 1114 1115 return InputFilter::getInstance()->clean($value, $filter); 1116 } 1117 1118 /** 1119 * Method to validate a FormField object based on field data. 1120 * 1121 * @param mixed $value The optional value to use as the default for the field. 1122 * @param string $group The optional dot-separated form group path on which to find the field. 1123 * @param Registry $input An optional Registry object with the entire data set to validate 1124 * against the entire form. 1125 * 1126 * @return boolean|\Exception Boolean true if field value is valid, Exception on failure. 1127 * 1128 * @since 4.0.0 1129 * @throws \InvalidArgumentException 1130 * @throws \UnexpectedValueException 1131 */ 1132 public function validate($value, $group = null, Registry $input = null) 1133 { 1134 // Make sure there is a valid SimpleXMLElement. 1135 if (!($this->element instanceof \SimpleXMLElement)) { 1136 throw new \UnexpectedValueException(sprintf('%s::validate `element` is not an instance of SimpleXMLElement', \get_class($this))); 1137 } 1138 1139 $valid = true; 1140 1141 // Check if the field is required. 1142 $required = ((string) $this->element['required'] === 'true' || (string) $this->element['required'] === 'required'); 1143 1144 if ($this->element['label']) { 1145 $fieldLabel = $this->element['label']; 1146 1147 // Try to translate label if not set to false 1148 $translate = (string) $this->element['translateLabel']; 1149 1150 if (!($translate === 'false' || $translate === 'off' || $translate === '0')) { 1151 $fieldLabel = Text::_($fieldLabel); 1152 } 1153 } else { 1154 $fieldLabel = Text::_($this->element['name']); 1155 } 1156 1157 // If the field is required and the value is empty return an error message. 1158 if ($required && (($value === '') || ($value === null))) { 1159 $message = Text::sprintf('JLIB_FORM_VALIDATE_FIELD_REQUIRED', $fieldLabel); 1160 1161 return new \RuntimeException($message); 1162 } 1163 1164 // Get the field validation rule. 1165 if ($type = (string) $this->element['validate']) { 1166 // Load the FormRule object for the field. 1167 $rule = FormHelper::loadRuleType($type); 1168 1169 // If the object could not be loaded return an error message. 1170 if ($rule === false) { 1171 throw new \UnexpectedValueException(sprintf('%s::validate() rule `%s` missing.', \get_class($this), $type)); 1172 } 1173 1174 if ($rule instanceof DatabaseAwareInterface) { 1175 try { 1176 $rule->setDatabase($this->getDatabase()); 1177 } catch (DatabaseNotFoundException $e) { 1178 @trigger_error(sprintf('Database must be set, this will not be caught anymore in 5.0.'), E_USER_DEPRECATED); 1179 $rule->setDatabase(Factory::getContainer()->get(DatabaseInterface::class)); 1180 } 1181 } 1182 1183 try { 1184 // Run the field validation rule test. 1185 $valid = $rule->test($this->element, $value, $group, $input, $this->form); 1186 } catch (\Exception $e) { 1187 return $e; 1188 } 1189 } 1190 1191 if ($valid !== false && $this instanceof SubformField) { 1192 // Load the subform validation rule. 1193 $rule = FormHelper::loadRuleType('Subform'); 1194 1195 if ($rule instanceof DatabaseAwareInterface) { 1196 try { 1197 $rule->setDatabase($this->getDatabase()); 1198 } catch (DatabaseNotFoundException $e) { 1199 @trigger_error(sprintf('Database must be set, this will not be caught anymore in 5.0.'), E_USER_DEPRECATED); 1200 $rule->setDatabase(Factory::getContainer()->get(DatabaseInterface::class)); 1201 } 1202 } 1203 1204 try { 1205 // Run the field validation rule test. 1206 $valid = $rule->test($this->element, $value, $group, $input, $this->form); 1207 } catch (\Exception $e) { 1208 return $e; 1209 } 1210 } 1211 1212 // Check if the field is valid. 1213 if ($valid === false) { 1214 // Does the field have a defined error message? 1215 $message = (string) $this->element['message']; 1216 1217 if ($message) { 1218 $message = Text::_($this->element['message']); 1219 } else { 1220 $message = Text::sprintf('JLIB_FORM_VALIDATE_FIELD_INVALID', $fieldLabel); 1221 } 1222 1223 return new \UnexpectedValueException($message); 1224 } 1225 1226 return $valid; 1227 } 1228 1229 /** 1230 * Method to post-process a field value. 1231 * 1232 * @param mixed $value The optional value to use as the default for the field. 1233 * @param string $group The optional dot-separated form group path on which to find the field. 1234 * @param Registry $input An optional Registry object with the entire data set to filter 1235 * against the entire form. 1236 * 1237 * @return mixed The processed value. 1238 * 1239 * @since 4.0.0 1240 */ 1241 public function postProcess($value, $group = null, Registry $input = null) 1242 { 1243 return $value; 1244 } 1245 1246 /** 1247 * Method to get the data to be passed to the layout for rendering. 1248 * 1249 * @return array 1250 * 1251 * @since 3.5 1252 */ 1253 protected function getLayoutData() 1254 { 1255 // Label preprocess 1256 $label = !empty($this->element['label']) ? (string) $this->element['label'] : null; 1257 $label = $label && $this->translateLabel ? Text::_($label) : $label; 1258 1259 // Description preprocess 1260 $description = !empty($this->description) ? $this->description : null; 1261 $description = !empty($description) && $this->translateDescription ? Text::_($description) : $description; 1262 1263 $alt = preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname); 1264 1265 return [ 1266 'autocomplete' => $this->autocomplete, 1267 'autofocus' => $this->autofocus, 1268 'class' => $this->class, 1269 'description' => $description, 1270 'disabled' => $this->disabled, 1271 'field' => $this, 1272 'group' => $this->group, 1273 'hidden' => $this->hidden, 1274 'hint' => $this->translateHint ? Text::alt($this->hint, $alt) : $this->hint, 1275 'id' => $this->id, 1276 'label' => $label, 1277 'labelclass' => $this->labelclass, 1278 'multiple' => $this->multiple, 1279 'name' => $this->name, 1280 'onchange' => $this->onchange, 1281 'onclick' => $this->onclick, 1282 'pattern' => $this->pattern, 1283 'validationtext' => $this->validationtext, 1284 'readonly' => $this->readonly, 1285 'repeat' => $this->repeat, 1286 'required' => (bool) $this->required, 1287 'size' => $this->size, 1288 'spellcheck' => $this->spellcheck, 1289 'validate' => $this->validate, 1290 'value' => $this->value, 1291 'dataAttribute' => $this->renderDataAttributes(), 1292 'dataAttributes' => $this->dataAttributes, 1293 'parentclass' => $this->parentclass, 1294 ]; 1295 } 1296 1297 /** 1298 * Allow to override renderer include paths in child fields 1299 * 1300 * @return array 1301 * 1302 * @since 3.5 1303 */ 1304 protected function getLayoutPaths() 1305 { 1306 $renderer = new FileLayout('default'); 1307 1308 return $renderer->getDefaultIncludePaths(); 1309 } 1310 1311 /** 1312 * Get the renderer 1313 * 1314 * @param string $layoutId Id to load 1315 * 1316 * @return FileLayout 1317 * 1318 * @since 3.5 1319 */ 1320 protected function getRenderer($layoutId = 'default') 1321 { 1322 $renderer = new FileLayout($layoutId); 1323 1324 $renderer->setDebug($this->isDebugEnabled()); 1325 1326 $layoutPaths = $this->getLayoutPaths(); 1327 1328 if ($layoutPaths) { 1329 $renderer->setIncludePaths($layoutPaths); 1330 } 1331 1332 return $renderer; 1333 } 1334 1335 /** 1336 * Is debug enabled for this field 1337 * 1338 * @return boolean 1339 * 1340 * @since 3.5 1341 */ 1342 protected function isDebugEnabled() 1343 { 1344 return $this->getAttribute('debug', 'false') === 'true'; 1345 } 1346 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |