From c399fe8171e2948c64e8605c6f0947fa310e271a Mon Sep 17 00:00:00 2001 From: Dimitris Grammatikogiannis Date: Thu, 17 Mar 2022 00:57:50 +0100 Subject: [PATCH 01/16] list-show-on --- layouts/joomla/form/field/list.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/layouts/joomla/form/field/list.php b/layouts/joomla/form/field/list.php index c827e314a99ac..42a528e031257 100644 --- a/layouts/joomla/form/field/list.php +++ b/layouts/joomla/form/field/list.php @@ -9,6 +9,7 @@ defined('_JEXEC') or die; +use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; extract($displayData); @@ -42,9 +43,10 @@ * @var array $options Options available for this field. * @var string $dataAttribute Miscellaneous data attributes preprocessed for HTML output * @var array $dataAttributes Miscellaneous data attribute for eg, data-* + * @var boolen $hasShowOn Is the list using the showon */ -$html = array(); +$html = []; $attr = ''; // Initialize the field attributes. @@ -57,6 +59,11 @@ $attr .= !empty($description) ? ' aria-describedby="' . ($id ?: $name) . '-desc"' : ''; $attr .= $dataAttribute; +if ($hasShowOn) +{ + Factory::getDocument()->getWebAssetManager()->useScript('showon'); +} + // To avoid user's confusion, readonly="readonly" should imply disabled="disabled". if ($readonly || $disabled) { From cb41ce089fd3d0f769d538fb3b7b648fd5581008 Mon Sep 17 00:00:00 2001 From: Dimitris Grammatikogiannis Date: Thu, 17 Mar 2022 01:01:16 +0100 Subject: [PATCH 02/16] Update ListField.php --- libraries/src/Form/Field/ListField.php | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/libraries/src/Form/Field/ListField.php b/libraries/src/Form/Field/ListField.php index 00aeb511e4fda..a2862a01ebde1 100644 --- a/libraries/src/Form/Field/ListField.php +++ b/libraries/src/Form/Field/ListField.php @@ -45,6 +45,14 @@ class ListField extends FormField */ protected $layout = 'joomla.form.field.list'; + /** + * Does the list has showOn + * + * @var boolean + * @since 4.1.1 + */ + protected $hasShowOn = false; + /** * Method to get the field input markup for a generic list. * Use the multiple attribute to enable multiselect. @@ -55,9 +63,9 @@ class ListField extends FormField */ protected function getInput() { - $data = $this->getLayoutData(); - - $data['options'] = (array) $this->getOptions(); + $data = $this->getLayoutData(); + $data['options'] = (array) $this->getOptions(); + $data['hasShowOn'] = $this->hasShowOn; return $this->getRenderer($this->layout)->render($data); } @@ -72,7 +80,7 @@ protected function getInput() protected function getOptions() { $fieldname = preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname); - $options = array(); + $options = []; foreach ($this->element->xpath('option') as $option) { @@ -123,14 +131,14 @@ protected function getOptions() $selected = (string) $option['selected']; $selected = ($selected === 'true' || $selected === 'selected' || $selected === '1'); - $tmp = array( + $tmp = [ 'value' => $value, 'text' => Text::alt($text, $fieldname), 'disable' => $disabled, 'class' => (string) $option['class'], 'selected' => ($checked || $selected), 'checked' => ($checked || $selected), - ); + ]; // Set some event handler attributes. But really, should be using unobtrusive js. $tmp['onclick'] = (string) $option['onclick']; @@ -138,6 +146,7 @@ protected function getOptions() if ((string) $option['showon']) { + $this->hasShowOn = true; $encodedConditions = json_encode( FormHelper::parseShowOnConditions((string) $option['showon'], $this->formControl, $this->group) ); From ac8bed219fa2e5848bfcc28f4190b168c3147aad Mon Sep 17 00:00:00 2001 From: Dimitris Grammatikogiannis Date: Thu, 17 Mar 2022 11:42:36 +0100 Subject: [PATCH 03/16] Update libraries/src/Form/Field/ListField.php Co-authored-by: Richard Fath --- libraries/src/Form/Field/ListField.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/src/Form/Field/ListField.php b/libraries/src/Form/Field/ListField.php index a2862a01ebde1..f2924b2f3ffa1 100644 --- a/libraries/src/Form/Field/ListField.php +++ b/libraries/src/Form/Field/ListField.php @@ -46,10 +46,10 @@ class ListField extends FormField protected $layout = 'joomla.form.field.list'; /** - * Does the list has showOn + * Does the list have showOn * * @var boolean - * @since 4.1.1 + * @since __DEPLOY_VERSION__ */ protected $hasShowOn = false; From 6a7684ecad79912e6f618a5d55daa6ae797fa743 Mon Sep 17 00:00:00 2001 From: Dimitris Grammatikogiannis Date: Fri, 18 Mar 2022 09:16:41 +0100 Subject: [PATCH 04/16] Update list.php --- layouts/joomla/form/field/list.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/layouts/joomla/form/field/list.php b/layouts/joomla/form/field/list.php index 42a528e031257..257284d6024e0 100644 --- a/layouts/joomla/form/field/list.php +++ b/layouts/joomla/form/field/list.php @@ -9,7 +9,6 @@ defined('_JEXEC') or die; -use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; extract($displayData); @@ -43,7 +42,6 @@ * @var array $options Options available for this field. * @var string $dataAttribute Miscellaneous data attributes preprocessed for HTML output * @var array $dataAttributes Miscellaneous data attribute for eg, data-* - * @var boolen $hasShowOn Is the list using the showon */ $html = []; @@ -59,11 +57,6 @@ $attr .= !empty($description) ? ' aria-describedby="' . ($id ?: $name) . '-desc"' : ''; $attr .= $dataAttribute; -if ($hasShowOn) -{ - Factory::getDocument()->getWebAssetManager()->useScript('showon'); -} - // To avoid user's confusion, readonly="readonly" should imply disabled="disabled". if ($readonly || $disabled) { From 93b2068b9010150e5d2a9bc68395e090260d49cb Mon Sep 17 00:00:00 2001 From: Dimitris Grammatikogiannis Date: Fri, 18 Mar 2022 09:19:39 +0100 Subject: [PATCH 05/16] Update ListField.php --- libraries/src/Form/Field/ListField.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/src/Form/Field/ListField.php b/libraries/src/Form/Field/ListField.php index f2924b2f3ffa1..afd189b6ec4ba 100644 --- a/libraries/src/Form/Field/ListField.php +++ b/libraries/src/Form/Field/ListField.php @@ -63,9 +63,9 @@ class ListField extends FormField */ protected function getInput() { - $data = $this->getLayoutData(); - $data['options'] = (array) $this->getOptions(); - $data['hasShowOn'] = $this->hasShowOn; + $data = $this->getLayoutData(); + $data['options'] = (array) $this->getOptions(); + $options['showonEnabled'] = $this->hasShowOn; return $this->getRenderer($this->layout)->render($data); } From 3f44437cc89e902b34c2cd1560c82d79d1222194 Mon Sep 17 00:00:00 2001 From: Dimitris Grammatikogiannis Date: Fri, 18 Mar 2022 09:20:16 +0100 Subject: [PATCH 06/16] Update list.php --- layouts/joomla/form/field/list.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layouts/joomla/form/field/list.php b/layouts/joomla/form/field/list.php index 257284d6024e0..c827e314a99ac 100644 --- a/layouts/joomla/form/field/list.php +++ b/layouts/joomla/form/field/list.php @@ -44,7 +44,7 @@ * @var array $dataAttributes Miscellaneous data attribute for eg, data-* */ -$html = []; +$html = array(); $attr = ''; // Initialize the field attributes. From 723f805a7eba122353866ce77fa6d05ed52120a5 Mon Sep 17 00:00:00 2001 From: Dimitris Grammatikogiannis Date: Fri, 18 Mar 2022 09:21:38 +0100 Subject: [PATCH 07/16] Update ListField.php --- libraries/src/Form/Field/ListField.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/src/Form/Field/ListField.php b/libraries/src/Form/Field/ListField.php index afd189b6ec4ba..149784394de3a 100644 --- a/libraries/src/Form/Field/ListField.php +++ b/libraries/src/Form/Field/ListField.php @@ -63,9 +63,9 @@ class ListField extends FormField */ protected function getInput() { - $data = $this->getLayoutData(); - $data['options'] = (array) $this->getOptions(); - $options['showonEnabled'] = $this->hasShowOn; + $data = $this->getLayoutData(); + $data['options'] = (array) $this->getOptions(); + $data['showonEnabled'] = $this->hasShowOn; return $this->getRenderer($this->layout)->render($data); } From 284aa6c72e7e8a3f912317929bb5ba04edf3a64a Mon Sep 17 00:00:00 2001 From: Dimitris Grammatikogiannis Date: Sun, 20 Mar 2022 11:42:02 +0100 Subject: [PATCH 08/16] Update FormField.php --- libraries/src/Form/FormField.php | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/libraries/src/Form/FormField.php b/libraries/src/Form/FormField.php index d328421e3bf69..62072d4c8e286 100644 --- a/libraries/src/Form/FormField.php +++ b/libraries/src/Form/FormField.php @@ -1316,17 +1316,12 @@ public function postProcess($value, $group = null, Registry $input = null) */ protected function getLayoutData() { - // Label preprocess - $label = !empty($this->element['label']) ? (string) $this->element['label'] : null; - $label = $label && $this->translateLabel ? Text::_($label) : $label; - - // Description preprocess + $label = !empty($this->element['label']) ? (string) $this->element['label'] : null; + $label = $label && $this->translateLabel ? Text::_($label) : $label; $description = !empty($this->description) ? $this->description : null; $description = !empty($description) && $this->translateDescription ? Text::_($description) : $description; - - $alt = preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname); - - return [ + $alt = preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname); + $options = [ 'autocomplete' => $this->autocomplete, 'autofocus' => $this->autofocus, 'class' => $this->class, @@ -1356,6 +1351,20 @@ protected function getLayoutData() 'dataAttributes' => $this->dataAttributes, 'parentclass' => $this->parentclass, ]; + + // Enable showOn from nested options + if (!empty((array) $this->element->xpath('option'))) + { + foreach ($this->element->xpath('option') as $option) + { + if ((string) $option['showon']) + { + $options['showonEnabled'] = true; + } + } + } + + return $options; } /** From 113a68fb96db098c6c1f4b8328205aec2525d735 Mon Sep 17 00:00:00 2001 From: Dimitris Grammatikogiannis Date: Sun, 20 Mar 2022 11:44:21 +0100 Subject: [PATCH 09/16] Update ListField.php --- libraries/src/Form/Field/ListField.php | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/libraries/src/Form/Field/ListField.php b/libraries/src/Form/Field/ListField.php index 149784394de3a..767b6e34f8f6b 100644 --- a/libraries/src/Form/Field/ListField.php +++ b/libraries/src/Form/Field/ListField.php @@ -45,14 +45,6 @@ class ListField extends FormField */ protected $layout = 'joomla.form.field.list'; - /** - * Does the list have showOn - * - * @var boolean - * @since __DEPLOY_VERSION__ - */ - protected $hasShowOn = false; - /** * Method to get the field input markup for a generic list. * Use the multiple attribute to enable multiselect. @@ -63,9 +55,8 @@ class ListField extends FormField */ protected function getInput() { - $data = $this->getLayoutData(); - $data['options'] = (array) $this->getOptions(); - $data['showonEnabled'] = $this->hasShowOn; + $data = $this->getLayoutData(); + $data['options'] = (array) $this->getOptions(); return $this->getRenderer($this->layout)->render($data); } @@ -80,7 +71,7 @@ protected function getInput() protected function getOptions() { $fieldname = preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname); - $options = []; + $options = array(); foreach ($this->element->xpath('option') as $option) { @@ -131,14 +122,14 @@ protected function getOptions() $selected = (string) $option['selected']; $selected = ($selected === 'true' || $selected === 'selected' || $selected === '1'); - $tmp = [ + $tmp = array( 'value' => $value, 'text' => Text::alt($text, $fieldname), 'disable' => $disabled, 'class' => (string) $option['class'], 'selected' => ($checked || $selected), 'checked' => ($checked || $selected), - ]; + ); // Set some event handler attributes. But really, should be using unobtrusive js. $tmp['onclick'] = (string) $option['onclick']; @@ -146,7 +137,6 @@ protected function getOptions() if ((string) $option['showon']) { - $this->hasShowOn = true; $encodedConditions = json_encode( FormHelper::parseShowOnConditions((string) $option['showon'], $this->formControl, $this->group) ); From b761cf4fb84fbc2446f887b8a00eb4d96b9a3f0d Mon Sep 17 00:00:00 2001 From: Dimitris Grammatikogiannis Date: Sun, 20 Mar 2022 11:59:39 +0100 Subject: [PATCH 10/16] Update ListField.php --- libraries/src/Form/Field/ListField.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/src/Form/Field/ListField.php b/libraries/src/Form/Field/ListField.php index 767b6e34f8f6b..00aeb511e4fda 100644 --- a/libraries/src/Form/Field/ListField.php +++ b/libraries/src/Form/Field/ListField.php @@ -55,7 +55,8 @@ class ListField extends FormField */ protected function getInput() { - $data = $this->getLayoutData(); + $data = $this->getLayoutData(); + $data['options'] = (array) $this->getOptions(); return $this->getRenderer($this->layout)->render($data); From 281b1c99ca8af34b943f649777abec49d8ac528c Mon Sep 17 00:00:00 2001 From: Dimitris Grammatikogiannis Date: Sun, 20 Mar 2022 12:35:35 +0100 Subject: [PATCH 11/16] Tiny perf --- libraries/src/Form/FormField.php | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/src/Form/FormField.php b/libraries/src/Form/FormField.php index 62072d4c8e286..58b3987b8001e 100644 --- a/libraries/src/Form/FormField.php +++ b/libraries/src/Form/FormField.php @@ -1360,6 +1360,7 @@ protected function getLayoutData() if ((string) $option['showon']) { $options['showonEnabled'] = true; + break; } } } From 32997d579e865bc08cd639ebb420332024038ea4 Mon Sep 17 00:00:00 2001 From: Dimitris Grammatikogiannis Date: Sun, 20 Mar 2022 12:39:54 +0100 Subject: [PATCH 12/16] Update libraries/src/Form/FormField.php Co-authored-by: Richard Fath --- libraries/src/Form/FormField.php | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/src/Form/FormField.php b/libraries/src/Form/FormField.php index 58b3987b8001e..824594aef6816 100644 --- a/libraries/src/Form/FormField.php +++ b/libraries/src/Form/FormField.php @@ -1360,6 +1360,7 @@ protected function getLayoutData() if ((string) $option['showon']) { $options['showonEnabled'] = true; + break; } } From d428b1ffb2e35e787dc7f3a08fd7becafc60f045 Mon Sep 17 00:00:00 2001 From: toroworx Date: Fri, 1 Apr 2022 17:59:04 +0200 Subject: [PATCH 13/16] Fix showon for nested option --- libraries/src/Form/FormField.php | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/libraries/src/Form/FormField.php b/libraries/src/Form/FormField.php index 824594aef6816..1902ad5b70382 100644 --- a/libraries/src/Form/FormField.php +++ b/libraries/src/Form/FormField.php @@ -1067,7 +1067,23 @@ public function renderField($options = array()) } } - if ($this->showon) + // Check if the field has showon in nested option + $hasOptionShowOn = false; + + if (!empty((array) $this->element->xpath('option'))) + { + foreach ($this->element->xpath('option') as $option) + { + if ((string) $option['showon']) + { + $hasOptionShowOn = true; + + break; + } + } + } + + if ($this->showon || $hasOptionShowOn) { $options['rel'] = ' data-showon=\'' . json_encode(FormHelper::parseShowOnConditions($this->showon, $this->formControl, $this->group)) . '\''; @@ -1352,20 +1368,6 @@ protected function getLayoutData() 'parentclass' => $this->parentclass, ]; - // Enable showOn from nested options - if (!empty((array) $this->element->xpath('option'))) - { - foreach ($this->element->xpath('option') as $option) - { - if ((string) $option['showon']) - { - $options['showonEnabled'] = true; - - break; - } - } - } - return $options; } From 198cc58246224896be3618bd4e11469b2ff13d28 Mon Sep 17 00:00:00 2001 From: Joomla! Bot Date: Mon, 27 Jun 2022 22:59:08 +0200 Subject: [PATCH 14/16] Phase 1 convert BRANCH to PSR-12 --- libraries/src/Form/FormField.php | 2743 ++++++++++++++---------------- 1 file changed, 1316 insertions(+), 1427 deletions(-) diff --git a/libraries/src/Form/FormField.php b/libraries/src/Form/FormField.php index 48a47a165db3e..16cd11083e887 100644 --- a/libraries/src/Form/FormField.php +++ b/libraries/src/Form/FormField.php @@ -1,4 +1,5 @@ ` XML element that describes the form field. - * - * @var \SimpleXMLElement - * @since 1.7.0 - */ - protected $element; - - /** - * The Form object of the form attached to the form field. - * - * @var Form - * @since 1.7.0 - */ - protected $form; - - /** - * The form control prefix for field names from the Form object attached to the form field. - * - * @var string - * @since 1.7.0 - */ - protected $formControl; - - /** - * The hidden state for the form field. - * - * @var boolean - * @since 1.7.0 - */ - protected $hidden = false; - - /** - * Should the label be hidden when rendering the form field? This may be useful if you have the - * label rendering in a legend in your form field itself for radio buttons in a fieldset etc. - * If you use this flag you should ensure you display the label in your form (for a11y etc.) - * - * @var boolean - * @since 4.0.0 - */ - protected $hiddenLabel = false; - - /** - * Should the description be hidden when rendering the form field? This may be useful if you have the - * description rendering in your form field itself for e.g. note fields. - * - * @var boolean - * @since 4.0.0 - */ - protected $hiddenDescription = false; - - /** - * True to translate the field label string. - * - * @var boolean - * @since 1.7.0 - */ - protected $translateLabel = true; - - /** - * True to translate the field description string. - * - * @var boolean - * @since 1.7.0 - */ - protected $translateDescription = true; - - /** - * True to translate the field hint string. - * - * @var boolean - * @since 3.2 - */ - protected $translateHint = true; - - /** - * The document id for the form field. - * - * @var string - * @since 1.7.0 - */ - protected $id; - - /** - * The input for the form field. - * - * @var string - * @since 1.7.0 - */ - protected $input; - - /** - * The label for the form field. - * - * @var string - * @since 1.7.0 - */ - protected $label; - - /** - * The multiple state for the form field. If true then multiple values are allowed for the - * field. Most often used for list field types. - * - * @var boolean - * @since 1.7.0 - */ - protected $multiple = false; - - /** - * Allows extensions to create repeat elements - * - * @var mixed - * @since 3.2 - */ - public $repeat = false; - - /** - * The pattern (Reg Ex) of value of the form field. - * - * @var string - * @since 1.7.0 - */ - protected $pattern; - - /** - * The validation text of invalid value of the form field. - * - * @var string - * @since 4.0.0 - */ - protected $validationtext; - - /** - * The name of the form field. - * - * @var string - * @since 1.7.0 - */ - protected $name; - - /** - * The name of the field. - * - * @var string - * @since 1.7.0 - */ - protected $fieldname; - - /** - * The group of the field. - * - * @var string - * @since 1.7.0 - */ - protected $group; - - /** - * The required state for the form field. If true then there must be a value for the field to - * be considered valid. - * - * @var boolean - * @since 1.7.0 - */ - protected $required = false; - - /** - * The disabled state for the form field. If true then the field will be disabled and user can't - * interact with the field. - * - * @var boolean - * @since 3.2 - */ - protected $disabled = false; - - /** - * The readonly state for the form field. If true then the field will be readonly. - * - * @var boolean - * @since 3.2 - */ - protected $readonly = false; - - /** - * The form field type. - * - * @var string - * @since 1.7.0 - */ - protected $type; - - /** - * The validation method for the form field. This value will determine which method is used - * to validate the value for a field. - * - * @var string - * @since 1.7.0 - */ - protected $validate; - - /** - * The value of the form field. - * - * @var mixed - * @since 1.7.0 - */ - protected $value; - - /** - * The default value of the form field. - * - * @var mixed - * @since 1.7.0 - */ - protected $default; - - /** - * The size of the form field. - * - * @var integer - * @since 3.2 - */ - protected $size; - - /** - * The class of the form field - * - * @var mixed - * @since 3.2 - */ - protected $class; - - /** - * The label's CSS class of the form field - * - * @var mixed - * @since 1.7.0 - */ - protected $labelclass; - - /** - * The javascript onchange of the form field. - * - * @var string - * @since 3.2 - */ - protected $onchange; - - /** - * The javascript onclick of the form field. - * - * @var string - * @since 3.2 - */ - protected $onclick; - - /** - * The conditions to show/hide the field. - * - * @var string - * @since 3.7.0 - */ - protected $showon; - - /** - * The parent class of the field - * - * @var string - * @since 4.0.0 - */ - protected $parentclass; - - /** - * The count value for generated name field - * - * @var integer - * @since 1.7.0 - */ - protected static $count = 0; - - /** - * The string used for generated fields names - * - * @var string - * @since 1.7.0 - */ - protected static $generated_fieldname = '__field'; - - /** - * Name of the layout being used to render the field - * - * @var string - * @since 3.5 - */ - protected $layout; - - /** - * Layout to render the form field - * - * @var string - */ - protected $renderLayout = 'joomla.form.renderfield'; - - /** - * Layout to render the label - * - * @var string - */ - protected $renderLabelLayout = 'joomla.form.renderlabel'; - - /** - * The data-attribute name and values of the form field. - * For example, data-action-type="click" data-action-type="change" - * - * @var array - * - * @since 4.0.0 - */ - protected $dataAttributes = array(); - - /** - * Method to instantiate the form field object. - * - * @param Form $form The form to attach to the form field object. - * - * @since 1.7.0 - */ - public function __construct($form = null) - { - // If there is a form passed into the constructor set the form and form control properties. - if ($form instanceof Form) - { - $this->form = $form; - $this->formControl = $form->getFormControl(); - } - - // Detect the field type if not set - if (!isset($this->type)) - { - $parts = Normalise::fromCamelCase(\get_called_class(), true); - - if ($parts[0] === 'J') - { - $this->type = StringHelper::ucfirst($parts[\count($parts) - 1], '_'); - } - else - { - $this->type = StringHelper::ucfirst($parts[0], '_') . StringHelper::ucfirst($parts[\count($parts) - 1], '_'); - } - } - } - - /** - * Method to get certain otherwise inaccessible properties from the form field object. - * - * @param string $name The property name for which to get the value. - * - * @return mixed The property value or null. - * - * @since 1.7.0 - */ - public function __get($name) - { - switch ($name) - { - case 'description': - case 'hint': - case 'formControl': - case 'hidden': - case 'id': - case 'multiple': - case 'name': - case 'required': - case 'type': - case 'validate': - case 'value': - case 'class': - case 'layout': - case 'labelclass': - case 'size': - case 'onchange': - case 'onclick': - case 'fieldname': - case 'group': - case 'disabled': - case 'readonly': - case 'autofocus': - case 'autocomplete': - case 'spellcheck': - case 'validationtext': - case 'showon': - case 'parentclass': - return $this->$name; - - case 'input': - // If the input hasn't yet been generated, generate it. - if (empty($this->input)) - { - $this->input = $this->getInput(); - } - - return $this->input; - - case 'label': - // If the label hasn't yet been generated, generate it. - if (empty($this->label)) - { - $this->label = $this->getLabel(); - } - - return $this->label; - - case 'title': - return $this->getTitle(); - - default: - // Check for data attribute - if (strpos($name, 'data-') === 0 && array_key_exists($name, $this->dataAttributes)) - { - return $this->dataAttributes[$name]; - } - } - } - - /** - * Method to set certain otherwise inaccessible properties of the form field object. - * - * @param string $name The property name for which to set the value. - * @param mixed $value The value of the property. - * - * @return void - * - * @since 3.2 - */ - public function __set($name, $value) - { - switch ($name) - { - case 'class': - // Removes spaces from left & right and extra spaces from middle - $value = preg_replace('/\s+/', ' ', trim((string) $value)); - - case 'description': - case 'hint': - case 'value': - case 'labelclass': - case 'layout': - case 'onchange': - case 'onclick': - case 'validate': - case 'pattern': - case 'validationtext': - case 'group': - case 'showon': - case 'parentclass': - case 'default': - case 'autocomplete': - $this->$name = (string) $value; - break; - - case 'id': - $this->id = $this->getId((string) $value, $this->fieldname); - break; - - case 'fieldname': - $this->fieldname = $this->getFieldName((string) $value); - break; - - case 'name': - $this->fieldname = $this->getFieldName((string) $value); - $this->name = $this->getName($this->fieldname); - break; - - case 'multiple': - // Allow for field classes to force the multiple values option. - $value = (string) $value; - $value = $value === '' && isset($this->forceMultiple) ? (string) $this->forceMultiple : $value; - - case 'required': - case 'disabled': - case 'readonly': - case 'autofocus': - case 'hidden': - $value = (string) $value; - $this->$name = ($value === 'true' || $value === $name || $value === '1'); - break; - - case 'spellcheck': - case 'translateLabel': - case 'translateDescription': - case 'translateHint': - $value = (string) $value; - $this->$name = !($value === 'false' || $value === 'off' || $value === '0'); - break; - - case 'translate_label': - $value = (string) $value; - $this->translateLabel = $this->translateLabel && !($value === 'false' || $value === 'off' || $value === '0'); - break; - - case 'translate_description': - $value = (string) $value; - $this->translateDescription = $this->translateDescription && !($value === 'false' || $value === 'off' || $value === '0'); - break; - - case 'size': - $this->$name = (int) $value; - break; - - default: - // Detect data attribute(s) - if (strpos($name, 'data-') === 0) - { - $this->dataAttributes[$name] = $value; - } - else - { - if (property_exists(__CLASS__, $name)) - { - Log::add("Cannot access protected / private property $name of " . __CLASS__); - } - else - { - $this->$name = $value; - } - } - } - } - - /** - * Method to attach a Form object to the field. - * - * @param Form $form The Form object to attach to the form field. - * - * @return FormField The form field object so that the method can be used in a chain. - * - * @since 1.7.0 - */ - public function setForm(Form $form) - { - $this->form = $form; - $this->formControl = $form->getFormControl(); - - return $this; - } - - /** - * Method to attach a Form object to the field. - * - * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `` tag for the form field object. - * @param mixed $value The form field value to validate. - * @param string $group The field name group control value. This acts as as an array container for the field. - * For example if the field has name="foo" and the group value is set to "bar" then the - * full field name would end up being "bar[foo]". - * - * @return boolean True on success. - * - * @since 1.7.0 - */ - public function setup(\SimpleXMLElement $element, $value, $group = null) - { - // Make sure there is a valid FormField XML element. - if ((string) $element->getName() !== 'field') - { - return false; - } - - // Reset the input and label values. - $this->input = null; - $this->label = null; - - // Set the XML element object. - $this->element = $element; - - // Set the group of the field. - $this->group = $group; - - $attributes = array( - 'multiple', 'name', 'id', 'hint', 'class', 'description', 'labelclass', 'onchange', 'onclick', 'validate', 'pattern', 'validationtext', - 'default', 'required', 'disabled', 'readonly', 'autofocus', 'hidden', 'autocomplete', 'spellcheck', 'translateHint', 'translateLabel', - 'translate_label', 'translateDescription', 'translate_description', 'size', 'showon'); - - $this->default = isset($element['value']) ? (string) $element['value'] : $this->default; - - // Set the field default value. - if ($element['multiple'] && \is_string($value) && \is_array(json_decode($value, true))) - { - $this->value = (array) json_decode($value); - } - else - { - $this->value = $value; - } - - // Lets detect miscellaneous data attribute. For eg, data-* - foreach ($this->element->attributes() as $key => $value) - { - if (strpos($key, 'data-') === 0) - { - // Data attribute key value pair - $this->dataAttributes[$key] = $value; - } - } - - foreach ($attributes as $attributeName) - { - $this->__set($attributeName, $element[$attributeName]); - } - - // Allow for repeatable elements - $repeat = (string) $element['repeat']; - $this->repeat = ($repeat === 'true' || $repeat === 'multiple' || (!empty($this->form->repeat) && $this->form->repeat == 1)); - - // Set the visibility. - $this->hidden = ($this->hidden || strtolower((string) $this->element['type']) === 'hidden'); - - $this->layout = !empty($this->element['layout']) ? (string) $this->element['layout'] : $this->layout; - - $this->parentclass = isset($this->element['parentclass']) ? (string) $this->element['parentclass'] : $this->parentclass; - - // Add required to class list if field is required. - if ($this->required) - { - $this->class = trim($this->class . ' required'); - } - - return true; - } - - /** - * Simple method to set the value - * - * @param mixed $value Value to set - * - * @return void - * - * @since 3.2 - */ - public function setValue($value) - { - $this->value = $value; - } - - /** - * Method to get the id used for the field input tag. - * - * @param string $fieldId The field element id. - * @param string $fieldName The field element name. - * - * @return string The id to be used for the field input tag. - * - * @since 1.7.0 - */ - protected function getId($fieldId, $fieldName) - { - $id = ''; - - // If there is a form control set for the attached form add it first. - if ($this->formControl) - { - $id .= $this->formControl; - } - - // If the field is in a group add the group control to the field id. - if ($this->group) - { - // If we already have an id segment add the group control as another level. - if ($id) - { - $id .= '_' . str_replace('.', '_', $this->group); - } - else - { - $id .= str_replace('.', '_', $this->group); - } - } - - // If we already have an id segment add the field id/name as another level. - if ($id) - { - $id .= '_' . ($fieldId ?: $fieldName); - } - else - { - $id .= ($fieldId ?: $fieldName); - } - - // Clean up any invalid characters. - $id = preg_replace('#\W#', '_', $id); - - // If this is a repeatable element, add the repeat count to the ID - if ($this->repeat) - { - $repeatCounter = empty($this->form->repeatCounter) ? 0 : $this->form->repeatCounter; - $id .= '-' . $repeatCounter; - - if (strtolower($this->type) === 'radio') - { - $id .= '-'; - } - } - - return $id; - } - - /** - * Method to get the field input markup. - * - * @return string The field input markup. - * - * @since 1.7.0 - */ - protected function getInput() - { - if (empty($this->layout)) - { - throw new \UnexpectedValueException(sprintf('%s has no layout assigned.', $this->name)); - } - - return $this->getRenderer($this->layout)->render($this->getLayoutData()); - } - - /** - * Method to get the field title. - * - * @return string The field title. - * - * @since 1.7.0 - */ - protected function getTitle() - { - $title = ''; - - if ($this->hidden) - { - return $title; - } - - // Get the label text from the XML element, defaulting to the element name. - $title = $this->element['label'] ? (string) $this->element['label'] : (string) $this->element['name']; - $title = $this->translateLabel ? Text::_($title) : $title; - - return $title; - } - - /** - * Method to get the field label markup. - * - * @return string The field label markup. - * - * @since 1.7.0 - */ - protected function getLabel() - { - if ($this->hidden) - { - return ''; - } - - $data = $this->getLayoutData(); - - // Forcing the Alias field to display the tip below - $position = $this->element['name'] === 'alias' ? ' data-bs-placement="bottom" ' : ''; - - // Here mainly for B/C with old layouts. This can be done in the layouts directly - $extraData = array( - 'text' => $data['label'], - 'for' => $this->id, - 'classes' => explode(' ', $data['labelclass']), - 'position' => $position, - ); - - return $this->getRenderer($this->renderLabelLayout)->render(array_merge($data, $extraData)); - } - - /** - * Method to get the name used for the field input tag. - * - * @param string $fieldName The field element name. - * - * @return string The name to be used for the field input tag. - * - * @since 1.7.0 - */ - protected function getName($fieldName) - { - // To support repeated element, extensions can set this in plugin->onRenderSettings - - $name = ''; - - // If there is a form control set for the attached form add it first. - if ($this->formControl) - { - $name .= $this->formControl; - } - - // If the field is in a group add the group control to the field name. - if ($this->group) - { - // If we already have a name segment add the group control as another level. - $groups = explode('.', $this->group); - - if ($name) - { - foreach ($groups as $group) - { - $name .= '[' . $group . ']'; - } - } - else - { - $name .= array_shift($groups); - - foreach ($groups as $group) - { - $name .= '[' . $group . ']'; - } - } - } - - // If we already have a name segment add the field name as another level. - if ($name) - { - $name .= '[' . $fieldName . ']'; - } - else - { - $name .= $fieldName; - } - - // If the field should support multiple values add the final array segment. - if ($this->multiple) - { - switch (strtolower((string) $this->element['type'])) - { - case 'text': - case 'textarea': - case 'email': - case 'password': - case 'radio': - case 'calendar': - case 'editor': - case 'hidden': - break; - default: - $name .= '[]'; - } - } - - return $name; - } - - /** - * Method to get the field name used. - * - * @param string $fieldName The field element name. - * - * @return string The field name - * - * @since 1.7.0 - */ - protected function getFieldName($fieldName) - { - if ($fieldName) - { - return $fieldName; - } - else - { - self::$count = self::$count + 1; - - return self::$generated_fieldname . self::$count; - } - } - - /** - * Method to get an attribute of the field - * - * @param string $name Name of the attribute to get - * @param mixed $default Optional value to return if attribute not found - * - * @return mixed Value of the attribute / default - * - * @since 3.2 - */ - public function getAttribute($name, $default = null) - { - if ($this->element instanceof \SimpleXMLElement) - { - $attributes = $this->element->attributes(); - - // Ensure that the attribute exists - if ($attributes->$name !== null) - { - return (string) $attributes->$name; - } - } - - return $default; - } - - /** - * Method to get data attributes. For example, data-user-type - * - * @return array list of data attribute(s) - * - * @since 4.0.0 - */ - public function getDataAttributes() - { - return $this->dataAttributes; - } - - /** - * Method to render data attributes to html. - * - * @return string A HTML Tag Attribute string of data attribute(s) - * - * @since 4.0.0 - */ - public function renderDataAttributes() - { - $dataAttribute = ''; - $dataAttributes = $this->getDataAttributes(); - - if (!empty($dataAttributes)) - { - foreach ($dataAttributes as $key => $attrValue) - { - $dataAttribute .= ' ' . $key . '="' . htmlspecialchars($attrValue, ENT_COMPAT, 'UTF-8') . '"'; - } - } - - return $dataAttribute; - } - - /** - * Render a layout of this field - * - * @param string $layoutId Layout identifier - * @param array $data Optional data for the layout - * - * @return string - * - * @since 3.5 - */ - public function render($layoutId, $data = array()) - { - $data = array_merge($this->getLayoutData(), $data); - - return $this->getRenderer($layoutId)->render($data); - } - - /** - * Method to get a control group with label and input. - * - * @param array $options Options to be passed into the rendering of the field - * - * @return string A string containing the html for the control group - * - * @since 3.2 - */ - public function renderField($options = array()) - { - if ($this->hidden) - { - return $this->getInput(); - } - - if (!isset($options['class'])) - { - $options['class'] = ''; - } - - $options['rel'] = ''; - - if (empty($options['hiddenLabel'])) - { - if ($this->getAttribute('hiddenLabel')) - { - $options['hiddenLabel'] = $this->getAttribute('hiddenLabel') == 'true'; - } - else - { - $options['hiddenLabel'] = $this->hiddenLabel; - } - } - - if (empty($options['hiddenDescription'])) - { - if ($this->getAttribute('hiddenDescription')) - { - $options['hiddenDescription'] = $this->getAttribute('hiddenDescription') == 'true'; - } - else - { - $options['hiddenDescription'] = $this->hiddenDescription; - } - } - - $options['inlineHelp'] = isset($this->form->getXml()->config->inlinehelp['button']) - ? ((string) $this->form->getXml()->config->inlinehelp['button'] == 'show' ?: false) - : false; - - // Check if the field has showon in nested option - $hasOptionShowOn = false; - - if (!empty((array) $this->element->xpath('option'))) - { - foreach ($this->element->xpath('option') as $option) - { - if ((string) $option['showon']) - { - $hasOptionShowOn = true; - - break; - } - } - } - - if ($this->showon || $hasOptionShowOn) - { - $options['rel'] = ' data-showon=\'' . - json_encode(FormHelper::parseShowOnConditions($this->showon, $this->formControl, $this->group)) . '\''; - $options['showonEnabled'] = true; - } - - $data = array( - 'input' => $this->getInput(), - 'label' => $this->getLabel(), - 'options' => $options, - ); - - $data = array_merge($this->getLayoutData(), $data); - - return $this->getRenderer($this->renderLayout)->render($data); - } - - /** - * Method to filter a field value. - * - * @param mixed $value The optional value to use as the default for the field. - * @param string $group The optional dot-separated form group path on which to find the field. - * @param Registry $input An optional Registry object with the entire data set to filter - * against the entire form. - * - * @return mixed The filtered value. - * - * @since 4.0.0 - * @throws \UnexpectedValueException - */ - public function filter($value, $group = null, Registry $input = null) - { - // Make sure there is a valid SimpleXMLElement. - if (!($this->element instanceof \SimpleXMLElement)) - { - throw new \UnexpectedValueException(sprintf('%s::filter `element` is not an instance of SimpleXMLElement', \get_class($this))); - } - - // Get the field filter type. - $filter = (string) $this->element['filter']; - - if ($filter !== '') - { - $required = ((string) $this->element['required'] === 'true' || (string) $this->element['required'] === 'required'); - - if (($value === '' || $value === null) && !$required) - { - return ''; - } - - // Check for a callback filter - if (strpos($filter, '::') !== false && \is_callable(explode('::', $filter))) - { - return \call_user_func(explode('::', $filter), $value); - } - - // Load the FormRule object for the field. FormRule objects take precedence over PHP functions - $obj = FormHelper::loadFilterType($filter); - - // Run the filter rule. - if ($obj) - { - return $obj->filter($this->element, $value, $group, $input, $this->form); - } - - if (\function_exists($filter)) - { - return \call_user_func($filter, $value); - } - - if ($this instanceof SubformField) - { - $subForm = $this->loadSubForm(); - - // Subform field may have a default value, that is a JSON string - if ($value && is_string($value)) - { - $value = json_decode($value, true); - - // The string is invalid json - if (!$value) - { - return null; - } - } - - if ($this->multiple) - { - $return = array(); - - if ($value) - { - foreach ($value as $key => $val) - { - $return[$key] = $subForm->filter($val); - } - } - } - else - { - $return = $subForm->filter($value); - } - - return $return; - } - } - - return InputFilter::getInstance()->clean($value, $filter); - } - - /** - * Method to validate a FormField object based on field data. - * - * @param mixed $value The optional value to use as the default for the field. - * @param string $group The optional dot-separated form group path on which to find the field. - * @param Registry $input An optional Registry object with the entire data set to validate - * against the entire form. - * - * @return boolean|\Exception Boolean true if field value is valid, Exception on failure. - * - * @since 4.0.0 - * @throws \InvalidArgumentException - * @throws \UnexpectedValueException - */ - public function validate($value, $group = null, Registry $input = null) - { - // Make sure there is a valid SimpleXMLElement. - if (!($this->element instanceof \SimpleXMLElement)) - { - throw new \UnexpectedValueException(sprintf('%s::validate `element` is not an instance of SimpleXMLElement', \get_class($this))); - } - - $valid = true; - - // Check if the field is required. - $required = ((string) $this->element['required'] === 'true' || (string) $this->element['required'] === 'required'); - - if ($this->element['label']) - { - $fieldLabel = $this->element['label']; - - // Try to translate label if not set to false - $translate = (string) $this->element['translateLabel']; - - if (!($translate === 'false' || $translate === 'off' || $translate === '0')) - { - $fieldLabel = Text::_($fieldLabel); - } - } - else - { - $fieldLabel = Text::_($this->element['name']); - } - - // If the field is required and the value is empty return an error message. - if ($required && (($value === '') || ($value === null))) - { - $message = Text::sprintf('JLIB_FORM_VALIDATE_FIELD_REQUIRED', $fieldLabel); - - return new \RuntimeException($message); - } - - // Get the field validation rule. - if ($type = (string) $this->element['validate']) - { - // Load the FormRule object for the field. - $rule = FormHelper::loadRuleType($type); - - // If the object could not be loaded return an error message. - if ($rule === false) - { - throw new \UnexpectedValueException(sprintf('%s::validate() rule `%s` missing.', \get_class($this), $type)); - } - - if ($rule instanceof DatabaseAwareInterface) - { - try - { - $rule->setDatabase($this->getDatabase()); - } - catch (DatabaseNotFoundException $e) - { - @trigger_error(sprintf('Database must be set, this will not be caught anymore in 5.0.'), E_USER_DEPRECATED); - $rule->setDatabase(Factory::getContainer()->get(DatabaseInterface::class)); - } - } - - try - { - // Run the field validation rule test. - $valid = $rule->test($this->element, $value, $group, $input, $this->form); - } - catch (\Exception $e) - { - return $e; - } - } - - if ($valid !== false && $this instanceof SubformField) - { - // Load the subform validation rule. - $rule = FormHelper::loadRuleType('Subform'); - - if ($rule instanceof DatabaseAwareInterface) - { - try - { - $rule->setDatabase($this->getDatabase()); - } - catch (DatabaseNotFoundException $e) - { - @trigger_error(sprintf('Database must be set, this will not be caught anymore in 5.0.'), E_USER_DEPRECATED); - $rule->setDatabase(Factory::getContainer()->get(DatabaseInterface::class)); - } - } - - try - { - // Run the field validation rule test. - $valid = $rule->test($this->element, $value, $group, $input, $this->form); - } - catch (\Exception $e) - { - return $e; - } - } - - // Check if the field is valid. - if ($valid === false) - { - // Does the field have a defined error message? - $message = (string) $this->element['message']; - - if ($message) - { - $message = Text::_($this->element['message']); - } - else - { - $message = Text::sprintf('JLIB_FORM_VALIDATE_FIELD_INVALID', $fieldLabel); - } - - return new \UnexpectedValueException($message); - } - - return $valid; - } - - /** - * Method to post-process a field value. - * - * @param mixed $value The optional value to use as the default for the field. - * @param string $group The optional dot-separated form group path on which to find the field. - * @param Registry $input An optional Registry object with the entire data set to filter - * against the entire form. - * - * @return mixed The processed value. - * - * @since 4.0.0 - */ - public function postProcess($value, $group = null, Registry $input = null) - { - return $value; - } - - /** - * Method to get the data to be passed to the layout for rendering. - * - * @return array - * - * @since 3.5 - */ - protected function getLayoutData() - { - $label = !empty($this->element['label']) ? (string) $this->element['label'] : null; - $label = $label && $this->translateLabel ? Text::_($label) : $label; - $description = !empty($this->description) ? $this->description : null; - $description = !empty($description) && $this->translateDescription ? Text::_($description) : $description; - $alt = preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname); - $options = [ - 'autocomplete' => $this->autocomplete, - 'autofocus' => $this->autofocus, - 'class' => $this->class, - 'description' => $description, - 'disabled' => $this->disabled, - 'field' => $this, - 'group' => $this->group, - 'hidden' => $this->hidden, - 'hint' => $this->translateHint ? Text::alt($this->hint, $alt) : $this->hint, - 'id' => $this->id, - 'label' => $label, - 'labelclass' => $this->labelclass, - 'multiple' => $this->multiple, - 'name' => $this->name, - 'onchange' => $this->onchange, - 'onclick' => $this->onclick, - 'pattern' => $this->pattern, - 'validationtext' => $this->validationtext, - 'readonly' => $this->readonly, - 'repeat' => $this->repeat, - 'required' => (bool) $this->required, - 'size' => $this->size, - 'spellcheck' => $this->spellcheck, - 'validate' => $this->validate, - 'value' => $this->value, - 'dataAttribute' => $this->renderDataAttributes(), - 'dataAttributes' => $this->dataAttributes, - 'parentclass' => $this->parentclass, - ]; - - return $options; - } - - /** - * Allow to override renderer include paths in child fields - * - * @return array - * - * @since 3.5 - */ - protected function getLayoutPaths() - { - $renderer = new FileLayout('default'); - - return $renderer->getDefaultIncludePaths(); - } - - /** - * Get the renderer - * - * @param string $layoutId Id to load - * - * @return FileLayout - * - * @since 3.5 - */ - protected function getRenderer($layoutId = 'default') - { - $renderer = new FileLayout($layoutId); - - $renderer->setDebug($this->isDebugEnabled()); - - $layoutPaths = $this->getLayoutPaths(); - - if ($layoutPaths) - { - $renderer->setIncludePaths($layoutPaths); - } - - return $renderer; - } - - /** - * Is debug enabled for this field - * - * @return boolean - * - * @since 3.5 - */ - protected function isDebugEnabled() - { - return $this->getAttribute('debug', 'false') === 'true'; - } + use DatabaseAwareTrait; + + /** + * The description text for the form field. Usually used in tooltips. + * + * @var string + * @since 1.7.0 + */ + protected $description; + + /** + * The hint text for the form field used to display hint inside the field. + * + * @var string + * @since 3.2 + */ + protected $hint; + + /** + * The autocomplete state for the form field. If 'off' element will not be automatically + * completed by browser. + * + * @var mixed + * @since 3.2 + */ + protected $autocomplete = 'on'; + + /** + * The spellcheck state for the form field. + * + * @var boolean + * @since 3.2 + */ + protected $spellcheck = true; + + /** + * The autofocus request for the form field. If true element will be automatically + * focused on document load. + * + * @var boolean + * @since 3.2 + */ + protected $autofocus = false; + + /** + * The SimpleXMLElement object of the `` XML element that describes the form field. + * + * @var \SimpleXMLElement + * @since 1.7.0 + */ + protected $element; + + /** + * The Form object of the form attached to the form field. + * + * @var Form + * @since 1.7.0 + */ + protected $form; + + /** + * The form control prefix for field names from the Form object attached to the form field. + * + * @var string + * @since 1.7.0 + */ + protected $formControl; + + /** + * The hidden state for the form field. + * + * @var boolean + * @since 1.7.0 + */ + protected $hidden = false; + + /** + * Should the label be hidden when rendering the form field? This may be useful if you have the + * label rendering in a legend in your form field itself for radio buttons in a fieldset etc. + * If you use this flag you should ensure you display the label in your form (for a11y etc.) + * + * @var boolean + * @since 4.0.0 + */ + protected $hiddenLabel = false; + + /** + * Should the description be hidden when rendering the form field? This may be useful if you have the + * description rendering in your form field itself for e.g. note fields. + * + * @var boolean + * @since 4.0.0 + */ + protected $hiddenDescription = false; + + /** + * True to translate the field label string. + * + * @var boolean + * @since 1.7.0 + */ + protected $translateLabel = true; + + /** + * True to translate the field description string. + * + * @var boolean + * @since 1.7.0 + */ + protected $translateDescription = true; + + /** + * True to translate the field hint string. + * + * @var boolean + * @since 3.2 + */ + protected $translateHint = true; + + /** + * The document id for the form field. + * + * @var string + * @since 1.7.0 + */ + protected $id; + + /** + * The input for the form field. + * + * @var string + * @since 1.7.0 + */ + protected $input; + + /** + * The label for the form field. + * + * @var string + * @since 1.7.0 + */ + protected $label; + + /** + * The multiple state for the form field. If true then multiple values are allowed for the + * field. Most often used for list field types. + * + * @var boolean + * @since 1.7.0 + */ + protected $multiple = false; + + /** + * Allows extensions to create repeat elements + * + * @var mixed + * @since 3.2 + */ + public $repeat = false; + + /** + * The pattern (Reg Ex) of value of the form field. + * + * @var string + * @since 1.7.0 + */ + protected $pattern; + + /** + * The validation text of invalid value of the form field. + * + * @var string + * @since 4.0.0 + */ + protected $validationtext; + + /** + * The name of the form field. + * + * @var string + * @since 1.7.0 + */ + protected $name; + + /** + * The name of the field. + * + * @var string + * @since 1.7.0 + */ + protected $fieldname; + + /** + * The group of the field. + * + * @var string + * @since 1.7.0 + */ + protected $group; + + /** + * The required state for the form field. If true then there must be a value for the field to + * be considered valid. + * + * @var boolean + * @since 1.7.0 + */ + protected $required = false; + + /** + * The disabled state for the form field. If true then the field will be disabled and user can't + * interact with the field. + * + * @var boolean + * @since 3.2 + */ + protected $disabled = false; + + /** + * The readonly state for the form field. If true then the field will be readonly. + * + * @var boolean + * @since 3.2 + */ + protected $readonly = false; + + /** + * The form field type. + * + * @var string + * @since 1.7.0 + */ + protected $type; + + /** + * The validation method for the form field. This value will determine which method is used + * to validate the value for a field. + * + * @var string + * @since 1.7.0 + */ + protected $validate; + + /** + * The value of the form field. + * + * @var mixed + * @since 1.7.0 + */ + protected $value; + + /** + * The default value of the form field. + * + * @var mixed + * @since 1.7.0 + */ + protected $default; + + /** + * The size of the form field. + * + * @var integer + * @since 3.2 + */ + protected $size; + + /** + * The class of the form field + * + * @var mixed + * @since 3.2 + */ + protected $class; + + /** + * The label's CSS class of the form field + * + * @var mixed + * @since 1.7.0 + */ + protected $labelclass; + + /** + * The javascript onchange of the form field. + * + * @var string + * @since 3.2 + */ + protected $onchange; + + /** + * The javascript onclick of the form field. + * + * @var string + * @since 3.2 + */ + protected $onclick; + + /** + * The conditions to show/hide the field. + * + * @var string + * @since 3.7.0 + */ + protected $showon; + + /** + * The parent class of the field + * + * @var string + * @since 4.0.0 + */ + protected $parentclass; + + /** + * The count value for generated name field + * + * @var integer + * @since 1.7.0 + */ + protected static $count = 0; + + /** + * The string used for generated fields names + * + * @var string + * @since 1.7.0 + */ + protected static $generated_fieldname = '__field'; + + /** + * Name of the layout being used to render the field + * + * @var string + * @since 3.5 + */ + protected $layout; + + /** + * Layout to render the form field + * + * @var string + */ + protected $renderLayout = 'joomla.form.renderfield'; + + /** + * Layout to render the label + * + * @var string + */ + protected $renderLabelLayout = 'joomla.form.renderlabel'; + + /** + * The data-attribute name and values of the form field. + * For example, data-action-type="click" data-action-type="change" + * + * @var array + * + * @since 4.0.0 + */ + protected $dataAttributes = array(); + + /** + * Method to instantiate the form field object. + * + * @param Form $form The form to attach to the form field object. + * + * @since 1.7.0 + */ + public function __construct($form = null) + { + // If there is a form passed into the constructor set the form and form control properties. + if ($form instanceof Form) { + $this->form = $form; + $this->formControl = $form->getFormControl(); + } + + // Detect the field type if not set + if (!isset($this->type)) { + $parts = Normalise::fromCamelCase(\get_called_class(), true); + + if ($parts[0] === 'J') { + $this->type = StringHelper::ucfirst($parts[\count($parts) - 1], '_'); + } else { + $this->type = StringHelper::ucfirst($parts[0], '_') . StringHelper::ucfirst($parts[\count($parts) - 1], '_'); + } + } + } + + /** + * Method to get certain otherwise inaccessible properties from the form field object. + * + * @param string $name The property name for which to get the value. + * + * @return mixed The property value or null. + * + * @since 1.7.0 + */ + public function __get($name) + { + switch ($name) { + case 'description': + case 'hint': + case 'formControl': + case 'hidden': + case 'id': + case 'multiple': + case 'name': + case 'required': + case 'type': + case 'validate': + case 'value': + case 'class': + case 'layout': + case 'labelclass': + case 'size': + case 'onchange': + case 'onclick': + case 'fieldname': + case 'group': + case 'disabled': + case 'readonly': + case 'autofocus': + case 'autocomplete': + case 'spellcheck': + case 'validationtext': + case 'showon': + case 'parentclass': + return $this->$name; + + case 'input': + // If the input hasn't yet been generated, generate it. + if (empty($this->input)) { + $this->input = $this->getInput(); + } + + return $this->input; + + case 'label': + // If the label hasn't yet been generated, generate it. + if (empty($this->label)) { + $this->label = $this->getLabel(); + } + + return $this->label; + + case 'title': + return $this->getTitle(); + + default: + // Check for data attribute + if (strpos($name, 'data-') === 0 && array_key_exists($name, $this->dataAttributes)) { + return $this->dataAttributes[$name]; + } + } + } + + /** + * Method to set certain otherwise inaccessible properties of the form field object. + * + * @param string $name The property name for which to set the value. + * @param mixed $value The value of the property. + * + * @return void + * + * @since 3.2 + */ + public function __set($name, $value) + { + switch ($name) { + case 'class': + // Removes spaces from left & right and extra spaces from middle + $value = preg_replace('/\s+/', ' ', trim((string) $value)); + + case 'description': + case 'hint': + case 'value': + case 'labelclass': + case 'layout': + case 'onchange': + case 'onclick': + case 'validate': + case 'pattern': + case 'validationtext': + case 'group': + case 'showon': + case 'parentclass': + case 'default': + case 'autocomplete': + $this->$name = (string) $value; + break; + + case 'id': + $this->id = $this->getId((string) $value, $this->fieldname); + break; + + case 'fieldname': + $this->fieldname = $this->getFieldName((string) $value); + break; + + case 'name': + $this->fieldname = $this->getFieldName((string) $value); + $this->name = $this->getName($this->fieldname); + break; + + case 'multiple': + // Allow for field classes to force the multiple values option. + $value = (string) $value; + $value = $value === '' && isset($this->forceMultiple) ? (string) $this->forceMultiple : $value; + + case 'required': + case 'disabled': + case 'readonly': + case 'autofocus': + case 'hidden': + $value = (string) $value; + $this->$name = ($value === 'true' || $value === $name || $value === '1'); + break; + + case 'spellcheck': + case 'translateLabel': + case 'translateDescription': + case 'translateHint': + $value = (string) $value; + $this->$name = !($value === 'false' || $value === 'off' || $value === '0'); + break; + + case 'translate_label': + $value = (string) $value; + $this->translateLabel = $this->translateLabel && !($value === 'false' || $value === 'off' || $value === '0'); + break; + + case 'translate_description': + $value = (string) $value; + $this->translateDescription = $this->translateDescription && !($value === 'false' || $value === 'off' || $value === '0'); + break; + + case 'size': + $this->$name = (int) $value; + break; + + default: + // Detect data attribute(s) + if (strpos($name, 'data-') === 0) { + $this->dataAttributes[$name] = $value; + } else { + if (property_exists(__CLASS__, $name)) { + Log::add("Cannot access protected / private property $name of " . __CLASS__); + } else { + $this->$name = $value; + } + } + } + } + + /** + * Method to attach a Form object to the field. + * + * @param Form $form The Form object to attach to the form field. + * + * @return FormField The form field object so that the method can be used in a chain. + * + * @since 1.7.0 + */ + public function setForm(Form $form) + { + $this->form = $form; + $this->formControl = $form->getFormControl(); + + return $this; + } + + /** + * Method to attach a Form object to the field. + * + * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `` tag for the form field object. + * @param mixed $value The form field value to validate. + * @param string $group The field name group control value. This acts as as an array container for the field. + * For example if the field has name="foo" and the group value is set to "bar" then the + * full field name would end up being "bar[foo]". + * + * @return boolean True on success. + * + * @since 1.7.0 + */ + public function setup(\SimpleXMLElement $element, $value, $group = null) + { + // Make sure there is a valid FormField XML element. + if ((string) $element->getName() !== 'field') { + return false; + } + + // Reset the input and label values. + $this->input = null; + $this->label = null; + + // Set the XML element object. + $this->element = $element; + + // Set the group of the field. + $this->group = $group; + + $attributes = array( + 'multiple', 'name', 'id', 'hint', 'class', 'description', 'labelclass', 'onchange', 'onclick', 'validate', 'pattern', 'validationtext', + 'default', 'required', 'disabled', 'readonly', 'autofocus', 'hidden', 'autocomplete', 'spellcheck', 'translateHint', 'translateLabel', + 'translate_label', 'translateDescription', 'translate_description', 'size', 'showon'); + + $this->default = isset($element['value']) ? (string) $element['value'] : $this->default; + + // Set the field default value. + if ($element['multiple'] && \is_string($value) && \is_array(json_decode($value, true))) { + $this->value = (array) json_decode($value); + } else { + $this->value = $value; + } + + // Lets detect miscellaneous data attribute. For eg, data-* + foreach ($this->element->attributes() as $key => $value) { + if (strpos($key, 'data-') === 0) { + // Data attribute key value pair + $this->dataAttributes[$key] = $value; + } + } + + foreach ($attributes as $attributeName) { + $this->__set($attributeName, $element[$attributeName]); + } + + // Allow for repeatable elements + $repeat = (string) $element['repeat']; + $this->repeat = ($repeat === 'true' || $repeat === 'multiple' || (!empty($this->form->repeat) && $this->form->repeat == 1)); + + // Set the visibility. + $this->hidden = ($this->hidden || strtolower((string) $this->element['type']) === 'hidden'); + + $this->layout = !empty($this->element['layout']) ? (string) $this->element['layout'] : $this->layout; + + $this->parentclass = isset($this->element['parentclass']) ? (string) $this->element['parentclass'] : $this->parentclass; + + // Add required to class list if field is required. + if ($this->required) { + $this->class = trim($this->class . ' required'); + } + + return true; + } + + /** + * Simple method to set the value + * + * @param mixed $value Value to set + * + * @return void + * + * @since 3.2 + */ + public function setValue($value) + { + $this->value = $value; + } + + /** + * Method to get the id used for the field input tag. + * + * @param string $fieldId The field element id. + * @param string $fieldName The field element name. + * + * @return string The id to be used for the field input tag. + * + * @since 1.7.0 + */ + protected function getId($fieldId, $fieldName) + { + $id = ''; + + // If there is a form control set for the attached form add it first. + if ($this->formControl) { + $id .= $this->formControl; + } + + // If the field is in a group add the group control to the field id. + if ($this->group) { + // If we already have an id segment add the group control as another level. + if ($id) { + $id .= '_' . str_replace('.', '_', $this->group); + } else { + $id .= str_replace('.', '_', $this->group); + } + } + + // If we already have an id segment add the field id/name as another level. + if ($id) { + $id .= '_' . ($fieldId ?: $fieldName); + } else { + $id .= ($fieldId ?: $fieldName); + } + + // Clean up any invalid characters. + $id = preg_replace('#\W#', '_', $id); + + // If this is a repeatable element, add the repeat count to the ID + if ($this->repeat) { + $repeatCounter = empty($this->form->repeatCounter) ? 0 : $this->form->repeatCounter; + $id .= '-' . $repeatCounter; + + if (strtolower($this->type) === 'radio') { + $id .= '-'; + } + } + + return $id; + } + + /** + * Method to get the field input markup. + * + * @return string The field input markup. + * + * @since 1.7.0 + */ + protected function getInput() + { + if (empty($this->layout)) { + throw new \UnexpectedValueException(sprintf('%s has no layout assigned.', $this->name)); + } + + return $this->getRenderer($this->layout)->render($this->getLayoutData()); + } + + /** + * Method to get the field title. + * + * @return string The field title. + * + * @since 1.7.0 + */ + protected function getTitle() + { + $title = ''; + + if ($this->hidden) { + return $title; + } + + // Get the label text from the XML element, defaulting to the element name. + $title = $this->element['label'] ? (string) $this->element['label'] : (string) $this->element['name']; + $title = $this->translateLabel ? Text::_($title) : $title; + + return $title; + } + + /** + * Method to get the field label markup. + * + * @return string The field label markup. + * + * @since 1.7.0 + */ + protected function getLabel() + { + if ($this->hidden) { + return ''; + } + + $data = $this->getLayoutData(); + + // Forcing the Alias field to display the tip below + $position = $this->element['name'] === 'alias' ? ' data-bs-placement="bottom" ' : ''; + + // Here mainly for B/C with old layouts. This can be done in the layouts directly + $extraData = array( + 'text' => $data['label'], + 'for' => $this->id, + 'classes' => explode(' ', $data['labelclass']), + 'position' => $position, + ); + + return $this->getRenderer($this->renderLabelLayout)->render(array_merge($data, $extraData)); + } + + /** + * Method to get the name used for the field input tag. + * + * @param string $fieldName The field element name. + * + * @return string The name to be used for the field input tag. + * + * @since 1.7.0 + */ + protected function getName($fieldName) + { + // To support repeated element, extensions can set this in plugin->onRenderSettings + + $name = ''; + + // If there is a form control set for the attached form add it first. + if ($this->formControl) { + $name .= $this->formControl; + } + + // If the field is in a group add the group control to the field name. + if ($this->group) { + // If we already have a name segment add the group control as another level. + $groups = explode('.', $this->group); + + if ($name) { + foreach ($groups as $group) { + $name .= '[' . $group . ']'; + } + } else { + $name .= array_shift($groups); + + foreach ($groups as $group) { + $name .= '[' . $group . ']'; + } + } + } + + // If we already have a name segment add the field name as another level. + if ($name) { + $name .= '[' . $fieldName . ']'; + } else { + $name .= $fieldName; + } + + // If the field should support multiple values add the final array segment. + if ($this->multiple) { + switch (strtolower((string) $this->element['type'])) { + case 'text': + case 'textarea': + case 'email': + case 'password': + case 'radio': + case 'calendar': + case 'editor': + case 'hidden': + break; + default: + $name .= '[]'; + } + } + + return $name; + } + + /** + * Method to get the field name used. + * + * @param string $fieldName The field element name. + * + * @return string The field name + * + * @since 1.7.0 + */ + protected function getFieldName($fieldName) + { + if ($fieldName) { + return $fieldName; + } else { + self::$count = self::$count + 1; + + return self::$generated_fieldname . self::$count; + } + } + + /** + * Method to get an attribute of the field + * + * @param string $name Name of the attribute to get + * @param mixed $default Optional value to return if attribute not found + * + * @return mixed Value of the attribute / default + * + * @since 3.2 + */ + public function getAttribute($name, $default = null) + { + if ($this->element instanceof \SimpleXMLElement) { + $attributes = $this->element->attributes(); + + // Ensure that the attribute exists + if ($attributes->$name !== null) { + return (string) $attributes->$name; + } + } + + return $default; + } + + /** + * Method to get data attributes. For example, data-user-type + * + * @return array list of data attribute(s) + * + * @since 4.0.0 + */ + public function getDataAttributes() + { + return $this->dataAttributes; + } + + /** + * Method to render data attributes to html. + * + * @return string A HTML Tag Attribute string of data attribute(s) + * + * @since 4.0.0 + */ + public function renderDataAttributes() + { + $dataAttribute = ''; + $dataAttributes = $this->getDataAttributes(); + + if (!empty($dataAttributes)) { + foreach ($dataAttributes as $key => $attrValue) { + $dataAttribute .= ' ' . $key . '="' . htmlspecialchars($attrValue, ENT_COMPAT, 'UTF-8') . '"'; + } + } + + return $dataAttribute; + } + + /** + * Render a layout of this field + * + * @param string $layoutId Layout identifier + * @param array $data Optional data for the layout + * + * @return string + * + * @since 3.5 + */ + public function render($layoutId, $data = array()) + { + $data = array_merge($this->getLayoutData(), $data); + + return $this->getRenderer($layoutId)->render($data); + } + + /** + * Method to get a control group with label and input. + * + * @param array $options Options to be passed into the rendering of the field + * + * @return string A string containing the html for the control group + * + * @since 3.2 + */ + public function renderField($options = array()) + { + if ($this->hidden) { + return $this->getInput(); + } + + if (!isset($options['class'])) { + $options['class'] = ''; + } + + $options['rel'] = ''; + + if (empty($options['hiddenLabel'])) { + if ($this->getAttribute('hiddenLabel')) { + $options['hiddenLabel'] = $this->getAttribute('hiddenLabel') == 'true'; + } else { + $options['hiddenLabel'] = $this->hiddenLabel; + } + } + + if (empty($options['hiddenDescription'])) { + if ($this->getAttribute('hiddenDescription')) { + $options['hiddenDescription'] = $this->getAttribute('hiddenDescription') == 'true'; + } else { + $options['hiddenDescription'] = $this->hiddenDescription; + } + } + + $options['inlineHelp'] = isset($this->form->getXml()->config->inlinehelp['button']) + ? ((string) $this->form->getXml()->config->inlinehelp['button'] == 'show' ?: false) + : false; + + // Check if the field has showon in nested option + $hasOptionShowOn = false; + + if (!empty((array) $this->element->xpath('option'))) { + foreach ($this->element->xpath('option') as $option) { + if ((string) $option['showon']) { + $hasOptionShowOn = true; + + break; + } + } + } + + if ($this->showon || $hasOptionShowOn) { + $options['rel'] = ' data-showon=\'' . + json_encode(FormHelper::parseShowOnConditions($this->showon, $this->formControl, $this->group)) . '\''; + $options['showonEnabled'] = true; + } + + $data = array( + 'input' => $this->getInput(), + 'label' => $this->getLabel(), + 'options' => $options, + ); + + $data = array_merge($this->getLayoutData(), $data); + + return $this->getRenderer($this->renderLayout)->render($data); + } + + /** + * Method to filter a field value. + * + * @param mixed $value The optional value to use as the default for the field. + * @param string $group The optional dot-separated form group path on which to find the field. + * @param Registry $input An optional Registry object with the entire data set to filter + * against the entire form. + * + * @return mixed The filtered value. + * + * @since 4.0.0 + * @throws \UnexpectedValueException + */ + public function filter($value, $group = null, Registry $input = null) + { + // Make sure there is a valid SimpleXMLElement. + if (!($this->element instanceof \SimpleXMLElement)) { + throw new \UnexpectedValueException(sprintf('%s::filter `element` is not an instance of SimpleXMLElement', \get_class($this))); + } + + // Get the field filter type. + $filter = (string) $this->element['filter']; + + if ($filter !== '') { + $required = ((string) $this->element['required'] === 'true' || (string) $this->element['required'] === 'required'); + + if (($value === '' || $value === null) && !$required) { + return ''; + } + + // Check for a callback filter + if (strpos($filter, '::') !== false && \is_callable(explode('::', $filter))) { + return \call_user_func(explode('::', $filter), $value); + } + + // Load the FormRule object for the field. FormRule objects take precedence over PHP functions + $obj = FormHelper::loadFilterType($filter); + + // Run the filter rule. + if ($obj) { + return $obj->filter($this->element, $value, $group, $input, $this->form); + } + + if (\function_exists($filter)) { + return \call_user_func($filter, $value); + } + + if ($this instanceof SubformField) { + $subForm = $this->loadSubForm(); + + // Subform field may have a default value, that is a JSON string + if ($value && is_string($value)) { + $value = json_decode($value, true); + + // The string is invalid json + if (!$value) { + return null; + } + } + + if ($this->multiple) { + $return = array(); + + if ($value) { + foreach ($value as $key => $val) { + $return[$key] = $subForm->filter($val); + } + } + } else { + $return = $subForm->filter($value); + } + + return $return; + } + } + + return InputFilter::getInstance()->clean($value, $filter); + } + + /** + * Method to validate a FormField object based on field data. + * + * @param mixed $value The optional value to use as the default for the field. + * @param string $group The optional dot-separated form group path on which to find the field. + * @param Registry $input An optional Registry object with the entire data set to validate + * against the entire form. + * + * @return boolean|\Exception Boolean true if field value is valid, Exception on failure. + * + * @since 4.0.0 + * @throws \InvalidArgumentException + * @throws \UnexpectedValueException + */ + public function validate($value, $group = null, Registry $input = null) + { + // Make sure there is a valid SimpleXMLElement. + if (!($this->element instanceof \SimpleXMLElement)) { + throw new \UnexpectedValueException(sprintf('%s::validate `element` is not an instance of SimpleXMLElement', \get_class($this))); + } + + $valid = true; + + // Check if the field is required. + $required = ((string) $this->element['required'] === 'true' || (string) $this->element['required'] === 'required'); + + if ($this->element['label']) { + $fieldLabel = $this->element['label']; + + // Try to translate label if not set to false + $translate = (string) $this->element['translateLabel']; + + if (!($translate === 'false' || $translate === 'off' || $translate === '0')) { + $fieldLabel = Text::_($fieldLabel); + } + } else { + $fieldLabel = Text::_($this->element['name']); + } + + // If the field is required and the value is empty return an error message. + if ($required && (($value === '') || ($value === null))) { + $message = Text::sprintf('JLIB_FORM_VALIDATE_FIELD_REQUIRED', $fieldLabel); + + return new \RuntimeException($message); + } + + // Get the field validation rule. + if ($type = (string) $this->element['validate']) { + // Load the FormRule object for the field. + $rule = FormHelper::loadRuleType($type); + + // If the object could not be loaded return an error message. + if ($rule === false) { + throw new \UnexpectedValueException(sprintf('%s::validate() rule `%s` missing.', \get_class($this), $type)); + } + + if ($rule instanceof DatabaseAwareInterface) { + try { + $rule->setDatabase($this->getDatabase()); + } catch (DatabaseNotFoundException $e) { + @trigger_error(sprintf('Database must be set, this will not be caught anymore in 5.0.'), E_USER_DEPRECATED); + $rule->setDatabase(Factory::getContainer()->get(DatabaseInterface::class)); + } + } + + try { + // Run the field validation rule test. + $valid = $rule->test($this->element, $value, $group, $input, $this->form); + } catch (\Exception $e) { + return $e; + } + } + + if ($valid !== false && $this instanceof SubformField) { + // Load the subform validation rule. + $rule = FormHelper::loadRuleType('Subform'); + + if ($rule instanceof DatabaseAwareInterface) { + try { + $rule->setDatabase($this->getDatabase()); + } catch (DatabaseNotFoundException $e) { + @trigger_error(sprintf('Database must be set, this will not be caught anymore in 5.0.'), E_USER_DEPRECATED); + $rule->setDatabase(Factory::getContainer()->get(DatabaseInterface::class)); + } + } + + try { + // Run the field validation rule test. + $valid = $rule->test($this->element, $value, $group, $input, $this->form); + } catch (\Exception $e) { + return $e; + } + } + + // Check if the field is valid. + if ($valid === false) { + // Does the field have a defined error message? + $message = (string) $this->element['message']; + + if ($message) { + $message = Text::_($this->element['message']); + } else { + $message = Text::sprintf('JLIB_FORM_VALIDATE_FIELD_INVALID', $fieldLabel); + } + + return new \UnexpectedValueException($message); + } + + return $valid; + } + + /** + * Method to post-process a field value. + * + * @param mixed $value The optional value to use as the default for the field. + * @param string $group The optional dot-separated form group path on which to find the field. + * @param Registry $input An optional Registry object with the entire data set to filter + * against the entire form. + * + * @return mixed The processed value. + * + * @since 4.0.0 + */ + public function postProcess($value, $group = null, Registry $input = null) + { + return $value; + } + + /** + * Method to get the data to be passed to the layout for rendering. + * + * @return array + * + * @since 3.5 + */ + protected function getLayoutData() + { + $label = !empty($this->element['label']) ? (string) $this->element['label'] : null; + $label = $label && $this->translateLabel ? Text::_($label) : $label; + $description = !empty($this->description) ? $this->description : null; + $description = !empty($description) && $this->translateDescription ? Text::_($description) : $description; + $alt = preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname); + $options = [ + 'autocomplete' => $this->autocomplete, + 'autofocus' => $this->autofocus, + 'class' => $this->class, + 'description' => $description, + 'disabled' => $this->disabled, + 'field' => $this, + 'group' => $this->group, + 'hidden' => $this->hidden, + 'hint' => $this->translateHint ? Text::alt($this->hint, $alt) : $this->hint, + 'id' => $this->id, + 'label' => $label, + 'labelclass' => $this->labelclass, + 'multiple' => $this->multiple, + 'name' => $this->name, + 'onchange' => $this->onchange, + 'onclick' => $this->onclick, + 'pattern' => $this->pattern, + 'validationtext' => $this->validationtext, + 'readonly' => $this->readonly, + 'repeat' => $this->repeat, + 'required' => (bool) $this->required, + 'size' => $this->size, + 'spellcheck' => $this->spellcheck, + 'validate' => $this->validate, + 'value' => $this->value, + 'dataAttribute' => $this->renderDataAttributes(), + 'dataAttributes' => $this->dataAttributes, + 'parentclass' => $this->parentclass, + ]; + + return $options; + } + + /** + * Allow to override renderer include paths in child fields + * + * @return array + * + * @since 3.5 + */ + protected function getLayoutPaths() + { + $renderer = new FileLayout('default'); + + return $renderer->getDefaultIncludePaths(); + } + + /** + * Get the renderer + * + * @param string $layoutId Id to load + * + * @return FileLayout + * + * @since 3.5 + */ + protected function getRenderer($layoutId = 'default') + { + $renderer = new FileLayout($layoutId); + + $renderer->setDebug($this->isDebugEnabled()); + + $layoutPaths = $this->getLayoutPaths(); + + if ($layoutPaths) { + $renderer->setIncludePaths($layoutPaths); + } + + return $renderer; + } + + /** + * Is debug enabled for this field + * + * @return boolean + * + * @since 3.5 + */ + protected function isDebugEnabled() + { + return $this->getAttribute('debug', 'false') === 'true'; + } } From 5cce55590db35cf0d5cb903741040db338b35864 Mon Sep 17 00:00:00 2001 From: Joomla! Bot Date: Mon, 27 Jun 2022 22:59:09 +0200 Subject: [PATCH 15/16] Phase 2 convert BRANCH to PSR-12 --- libraries/src/Form/FormField.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/src/Form/FormField.php b/libraries/src/Form/FormField.php index 16cd11083e887..3266db9d4bd47 100644 --- a/libraries/src/Form/FormField.php +++ b/libraries/src/Form/FormField.php @@ -9,8 +9,6 @@ namespace Joomla\CMS\Form; -\defined('JPATH_PLATFORM') or die; - use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Form\Field\SubformField; From eb876a5031eb71054b3e124b23a1d784fcb302e7 Mon Sep 17 00:00:00 2001 From: Richard Fath Date: Sat, 26 Aug 2023 18:17:16 +0200 Subject: [PATCH 16/16] Fix PHPCS failing in drone --- libraries/src/Form/FormField.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/src/Form/FormField.php b/libraries/src/Form/FormField.php index c82e290235550..29bbad8ec2cd7 100644 --- a/libraries/src/Form/FormField.php +++ b/libraries/src/Form/FormField.php @@ -509,6 +509,8 @@ public function __set($name, $value) // Removes spaces from left & right and extra spaces from middle $value = preg_replace('/\s+/', ' ', trim((string) $value)); + // No break + case 'description': case 'hint': case 'value': @@ -545,6 +547,8 @@ public function __set($name, $value) $value = (string) $value; $value = $value === '' && isset($this->forceMultiple) ? (string) $this->forceMultiple : $value; + // No break + case 'required': case 'disabled': case 'readonly':