[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Form/ -> FormField.php (source)

   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  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer