Joomla Platform  13.1
Documentation des API du framework Joomla Platform
 Tout Classes Espaces de nommage Fichiers Fonctions Variables Pages
field.php
Aller à la documentation de ce fichier.
1 <?php
2 /**
3  * @package Joomla.Platform
4  * @subpackage Form
5  *
6  * @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
7  * @license GNU General Public License version 2 or later; see LICENSE
8  */
9 
10 defined('JPATH_PLATFORM') or die;
11 
12 /**
13  * Abstract Form Field class for the Joomla Platform.
14  *
15  * @package Joomla.Platform
16  * @subpackage Form
17  * @since 11.1
18  */
19 abstract class JFormField
20 {
21  /**
22  * The description text for the form field. Usually used in tooltips.
23  *
24  * @var string
25  * @since 11.1
26  */
27  protected $description;
28 
29  /**
30  * The hint text for the form field used to display hint inside the field.
31  *
32  * @var string
33  * @since 3.2
34  */
35  protected $hint;
36 
37  /**
38  * The autocomplete state for the form field. If 'off' element will not be automatically
39  * completed by browser.
40  *
41  * @var mixed
42  * @since 3.2
43  */
44  protected $autocomplete = 'on';
45 
46  /**
47  * The spellcheck state for the form field.
48  *
49  * @var boolean
50  * @since 3.2
51  */
52  protected $spellcheck = true;
53 
54  /**
55  * The autofocus request for the form field. If true element will be automatically
56  * focused on document load.
57  *
58  * @var boolean
59  * @since 3.2
60  */
61  protected $autofocus = false;
62 
63  /**
64  * The SimpleXMLElement object of the <field /> XML element that describes the form field.
65  *
66  * @var SimpleXMLElement
67  * @since 11.1
68  */
69  protected $element;
70 
71  /**
72  * The JForm object of the form attached to the form field.
73  *
74  * @var JForm
75  * @since 11.1
76  */
77  protected $form;
78 
79  /**
80  * The form control prefix for field names from the JForm object attached to the form field.
81  *
82  * @var string
83  * @since 11.1
84  */
85  protected $formControl;
86 
87  /**
88  * The hidden state for the form field.
89  *
90  * @var boolean
91  * @since 11.1
92  */
93  protected $hidden = false;
94 
95  /**
96  * True to translate the field label string.
97  *
98  * @var boolean
99  * @since 11.1
100  */
101  protected $translateLabel = true;
102 
103  /**
104  * True to translate the field description string.
105  *
106  * @var boolean
107  * @since 11.1
108  */
109  protected $translateDescription = true;
110 
111  /**
112  * True to translate the field hint string.
113  *
114  * @var boolean
115  * @since 3.2
116  */
117  protected $translateHint = true;
118 
119  /**
120  * The document id for the form field.
121  *
122  * @var string
123  * @since 11.1
124  */
125  protected $id;
126 
127  /**
128  * The input for the form field.
129  *
130  * @var string
131  * @since 11.1
132  */
133  protected $input;
134 
135  /**
136  * The label for the form field.
137  *
138  * @var string
139  * @since 11.1
140  */
141  protected $label;
142 
143  /**
144  * The multiple state for the form field. If true then multiple values are allowed for the
145  * field. Most often used for list field types.
146  *
147  * @var boolean
148  * @since 11.1
149  */
150  protected $multiple = false;
151 
152  /**
153  * Allows extensions to create repeat elements
154  *
155  * @var mixed
156  * @since 3.2
157  */
158  public $repeat = false;
159 
160  /**
161  * The pattern (Reg Ex) of value of the form field.
162  *
163  * @var string
164  * @since 11.1
165  */
166  protected $pattern;
167 
168  /**
169  * The name of the form field.
170  *
171  * @var string
172  * @since 11.1
173  */
174  protected $name;
175 
176  /**
177  * The name of the field.
178  *
179  * @var string
180  * @since 11.1
181  */
182  protected $fieldname;
183 
184  /**
185  * The group of the field.
186  *
187  * @var string
188  * @since 11.1
189  */
190  protected $group;
191 
192  /**
193  * The required state for the form field. If true then there must be a value for the field to
194  * be considered valid.
195  *
196  * @var boolean
197  * @since 11.1
198  */
199  protected $required = false;
200 
201  /**
202  * The disabled state for the form field. If true then the field will be disabled and user can't
203  * interact with the field.
204  *
205  * @var boolean
206  * @since 3.2
207  */
208  protected $disabled = false;
209 
210  /**
211  * The readonly state for the form field. If true then the field will be readonly.
212  *
213  * @var boolean
214  * @since 3.2
215  */
216  protected $readonly = false;
217 
218  /**
219  * The form field type.
220  *
221  * @var string
222  * @since 11.1
223  */
224  protected $type;
225 
226  /**
227  * The validation method for the form field. This value will determine which method is used
228  * to validate the value for a field.
229  *
230  * @var string
231  * @since 11.1
232  */
233  protected $validate;
234 
235  /**
236  * The value of the form field.
237  *
238  * @var mixed
239  * @since 11.1
240  */
241  protected $value;
242 
243  /**
244  * The default value of the form field.
245  *
246  * @var mixed
247  * @since 11.1
248  */
249  protected $default;
250 
251  /**
252  * The size of the form field.
253  *
254  * @var integer
255  * @since 3.2
256  */
257  protected $size;
258 
259  /**
260  * The class of the form field
261  *
262  * @var mixed
263  * @since 3.2
264  */
265  protected $class;
266 
267  /**
268  * The label's CSS class of the form field
269  *
270  * @var mixed
271  * @since 11.1
272  */
273  protected $labelClass;
274 
275  /**
276  * The javascript onchange of the form field.
277  *
278  * @var string
279  * @since 3.2
280  */
281  protected $onchange;
282 
283  /**
284  * The javascript onclick of the form field.
285  *
286  * @var string
287  * @since 3.2
288  */
289  protected $onclick;
290 
291  /**
292  * The count value for generated name field
293  *
294  * @var integer
295  * @since 11.1
296  */
297  protected static $count = 0;
298 
299  /**
300  * The string used for generated fields names
301  *
302  * @var string
303  * @since 11.1
304  */
305  protected static $generated_fieldname = '__field';
306 
307  /**
308  * Method to instantiate the form field object.
309  *
310  * @param JForm $form The form to attach to the form field object.
311  *
312  * @since 11.1
313  */
314  public function __construct($form = null)
315  {
316  // If there is a form passed into the constructor set the form and form control properties.
317  if ($form instanceof JForm)
318  {
319  $this->form = $form;
320  $this->formControl = $form->getFormControl();
321  }
322 
323  // Detect the field type if not set
324  if (!isset($this->type))
325  {
326  $parts = JStringNormalise::fromCamelCase(get_called_class(), true);
327 
328  if ($parts[0] == 'J')
329  {
330  $this->type = JString::ucfirst($parts[count($parts) - 1], '_');
331  }
332  else
333  {
334  $this->type = JString::ucfirst($parts[0], '_') . JString::ucfirst($parts[count($parts) - 1], '_');
335  }
336  }
337  }
338 
339  /**
340  * Method to get certain otherwise inaccessible properties from the form field object.
341  *
342  * @param string $name The property name for which to the the value.
343  *
344  * @return mixed The property value or null.
345  *
346  * @since 11.1
347  */
348  public function __get($name)
349  {
350  switch ($name)
351  {
352  case 'description':
353  case 'hint':
354  case 'formControl':
355  case 'hidden':
356  case 'id':
357  case 'multiple':
358  case 'name':
359  case 'required':
360  case 'type':
361  case 'validate':
362  case 'value':
363  case 'class':
364  case 'labelClass':
365  case 'size':
366  case 'onchange':
367  case 'onclick':
368  case 'fieldname':
369  case 'group':
370  case 'disabled':
371  case 'readonly':
372  case 'autofocus':
373  case 'autocomplete':
374  case 'spellcheck':
375  return $this->$name;
376 
377  case 'input':
378  // If the input hasn't yet been generated, generate it.
379  if (empty($this->input))
380  {
381  $this->input = $this->getInput();
382  }
383 
384  return $this->input;
385 
386  case 'label':
387  // If the label hasn't yet been generated, generate it.
388  if (empty($this->label))
389  {
390  $this->label = $this->getLabel();
391  }
392 
393  return $this->label;
394 
395  case 'title':
396  return $this->getTitle();
397  }
398 
399  return null;
400  }
401 
402  /**
403  * Method to set certain otherwise inaccessible properties of the form field object.
404  *
405  * @param string $name The property name for which to the the value.
406  * @param mixed $value The value of the property.
407  *
408  * @return void
409  *
410  * @since 3.2
411  */
412  public function __set($name, $value)
413  {
414  switch ($name)
415  {
416  case 'class':
417  // Removes spaces from left & right and extra spaces from middle
418  $value = preg_replace('/\s+/', ' ', trim((string) $value));
419 
420  case 'description':
421  case 'hint':
422  case 'value':
423  case 'labelClass':
424  case 'onchange':
425  case 'onclick':
426  case 'validate':
427  case 'pattern':
428  case 'default':
429  $this->$name = (string) $value;
430  break;
431 
432  case 'id':
433  $this->id = $this->getId((string) $value, $this->fieldname);
434  break;
435 
436  case 'fieldname':
437  $this->fieldname = $this->getFieldName((string) $value);
438  break;
439 
440  case 'name':
441  $this->fieldname = $this->getFieldName((string) $value);
442  $this->name = $this->getName($this->fieldname);
443  break;
444 
445  case 'multiple':
446  // Allow for field classes to force the multiple values option.
447  $value = (string) $value;
448  $value = $value === '' && isset($this->forceMultiple) ? (string) $this->forceMultiple : $value;
449 
450  case 'required':
451  case 'disabled':
452  case 'readonly':
453  case 'autofocus':
454  case 'hidden':
455  $value = (string) $value;
456  $this->$name = ($value === 'true' || $value === $name || $value === '1');
457  break;
458 
459  case 'autocomplete':
460  $value = (string) $value;
461  $value = ($value == 'on' || $value == '') ? 'on' : $value;
462  $this->$name = ($value === 'false' || $value === 'off' || $value === '0') ? false : $value;
463  break;
464 
465  case 'spellcheck':
466  case 'translateLabel':
467  case 'translateDescription':
468  case 'translateHint':
469  $value = (string) $value;
470  $this->$name = !($value === 'false' || $value === 'off' || $value === '0');
471  break;
472 
473  case 'size':
474  $this->$name = (int) $value;
475  break;
476 
477  default:
478  if (property_exists(__CLASS__, $name))
479  {
480  JLog::add("Cannot access protected / private property $name of " . __CLASS__);
481  }
482  else
483  {
484  $this->$name = $value;
485  }
486  }
487  }
488 
489  /**
490  * Method to attach a JForm object to the field.
491  *
492  * @param JForm $form The JForm object to attach to the form field.
493  *
494  * @return JFormField The form field object so that the method can be used in a chain.
495  *
496  * @since 11.1
497  */
498  public function setForm(JForm $form)
499  {
500  $this->form = $form;
501  $this->formControl = $form->getFormControl();
502 
503  return $this;
504  }
505 
506  /**
507  * Method to attach a JForm object to the field.
508  *
509  * @param SimpleXMLElement $element The SimpleXMLElement object representing the <field /> tag for the form field object.
510  * @param mixed $value The form field value to validate.
511  * @param string $group The field name group control value. This acts as as an array container for the field.
512  * For example if the field has name="foo" and the group value is set to "bar" then the
513  * full field name would end up being "bar[foo]".
514  *
515  * @return boolean True on success.
516  *
517  * @since 11.1
518  */
519  public function setup(SimpleXMLElement $element, $value, $group = null)
520  {
521  // Make sure there is a valid JFormField XML element.
522  if ((string) $element->getName() != 'field')
523  {
524  return false;
525  }
526 
527  // Reset the input and label values.
528  $this->input = null;
529  $this->label = null;
530 
531  // Set the XML element object.
532  $this->element = $element;
533 
534  // Set the group of the field.
535  $this->group = $group;
536 
537  $attributes = array(
538  'multiple', 'name', 'id', 'hint', 'class', 'description', 'labelClass', 'onchange',
539  'onclick', 'validate', 'pattern', 'default', 'required',
540  'disabled', 'readonly', 'autofocus', 'hidden', 'autocomplete', 'spellcheck',
541  'translateHint', 'translateLabel', 'translateDescription', 'size');
542 
543  $this->default = isset($element['value']) ? (string) $element['value'] : $this->default;
544 
545  // Set the field default value.
546  $this->value = $value;
547 
548  foreach ($attributes as $attributeName)
549  {
550  $this->__set($attributeName, $element[$attributeName]);
551  }
552 
553  // Allow for repeatable elements
554  $repeat = (string) $element['repeat'];
555  $this->repeat = ($repeat == 'true' || $repeat == 'multiple' || (!empty($this->form->repeat) && $this->form->repeat == 1));
556 
557  // Set the visibility.
558  $this->hidden = ($this->hidden || (string) $element['type'] == 'hidden');
559 
560  return true;
561  }
562 
563  /**
564  * Simple method to set the value
565  *
566  * @param mixed $value Value to set
567  *
568  * @return void
569  *
570  * @since 3.2
571  */
572  public function setValue($value)
573  {
574  $this->value = $value;
575  }
576 
577  /**
578  * Method to get the id used for the field input tag.
579  *
580  * @param string $fieldId The field element id.
581  * @param string $fieldName The field element name.
582  *
583  * @return string The id to be used for the field input tag.
584  *
585  * @since 11.1
586  */
587  protected function getId($fieldId, $fieldName)
588  {
589  $id = '';
590 
591  // If there is a form control set for the attached form add it first.
592  if ($this->formControl)
593  {
594  $id .= $this->formControl;
595  }
596 
597  // If the field is in a group add the group control to the field id.
598  if ($this->group)
599  {
600  // If we already have an id segment add the group control as another level.
601  if ($id)
602  {
603  $id .= '_' . str_replace('.', '_', $this->group);
604  }
605  else
606  {
607  $id .= str_replace('.', '_', $this->group);
608  }
609  }
610 
611  // If we already have an id segment add the field id/name as another level.
612  if ($id)
613  {
614  $id .= '_' . ($fieldId ? $fieldId : $fieldName);
615  }
616  else
617  {
618  $id .= ($fieldId ? $fieldId : $fieldName);
619  }
620 
621  // Clean up any invalid characters.
622  $id = preg_replace('#\W#', '_', $id);
623 
624  // If this is a repeatable element, add the repeat count to the ID
625  if ($this->repeat)
626  {
627  $repeatCounter = empty($this->form->repeatCounter) ? 0 : $this->form->repeatCounter;
628  $id .= '-' . $repeatCounter;
629 
630  if (strtolower($this->type) == 'radio')
631  {
632  $id .= '-';
633  }
634  }
635 
636  return $id;
637  }
638 
639  /**
640  * Method to get the field input markup.
641  *
642  * @return string The field input markup.
643  *
644  * @since 11.1
645  */
646  abstract protected function getInput();
647 
648  /**
649  * Method to get the field title.
650  *
651  * @return string The field title.
652  *
653  * @since 11.1
654  */
655  protected function getTitle()
656  {
657  $title = '';
658 
659  if ($this->hidden)
660  {
661  return $title;
662  }
663 
664  // Get the label text from the XML element, defaulting to the element name.
665  $title = $this->element['label'] ? (string) $this->element['label'] : (string) $this->element['name'];
666  $title = $this->translateLabel ? JText::_($title) : $title;
667 
668  return $title;
669  }
670 
671  /**
672  * Method to get the field label markup.
673  *
674  * @return string The field label markup.
675  *
676  * @since 11.1
677  */
678  protected function getLabel()
679  {
680  $label = '';
681 
682  if ($this->hidden)
683  {
684  return $label;
685  }
686 
687  // Get the label text from the XML element, defaulting to the element name.
688  $text = $this->element['label'] ? (string) $this->element['label'] : (string) $this->element['name'];
689  $text = $this->translateLabel ? JText::_($text) : $text;
690 
691  // Build the class for the label.
692  $class = !empty($this->description) ? 'hasTooltip' : '';
693  $class = $this->required == true ? $class . ' required' : $class;
694  $class = !empty($this->labelClass) ? $class . ' ' . $this->labelClass : $class;
695 
696  // Add the opening label tag and main attributes attributes.
697  $label .= '<label id="' . $this->id . '-lbl" for="' . $this->id . '" class="' . $class . '"';
698 
699  // If a description is specified, use it to build a tooltip.
700  if (!empty($this->description))
701  {
702  JHtml::_('bootstrap.tooltip');
703  $label .= ' title="' . JHtml::tooltipText(trim($text, ':'), JText::_($this->description), 0) . '"';
704  }
705 
706  // Add the label text and closing tag.
707  if ($this->required)
708  {
709  $label .= '>' . $text . '<span class="star">&#160;*</span></label>';
710  }
711  else
712  {
713  $label .= '>' . $text . '</label>';
714  }
715 
716  return $label;
717  }
718 
719  /**
720  * Method to get the name used for the field input tag.
721  *
722  * @param string $fieldName The field element name.
723  *
724  * @return string The name to be used for the field input tag.
725  *
726  * @since 11.1
727  */
728  protected function getName($fieldName)
729  {
730  // To support repeated element, extensions can set this in plugin->onRenderSettings
731  $repeatCounter = empty($this->form->repeatCounter) ? 0 : $this->form->repeatCounter;
732 
733  $name = '';
734 
735  // If there is a form control set for the attached form add it first.
736  if ($this->formControl)
737  {
738  $name .= $this->formControl;
739  }
740 
741  // If the field is in a group add the group control to the field name.
742  if ($this->group)
743  {
744  // If we already have a name segment add the group control as another level.
745  $groups = explode('.', $this->group);
746 
747  if ($name)
748  {
749  foreach ($groups as $group)
750  {
751  $name .= '[' . $group . ']';
752  }
753  }
754  else
755  {
756  $name .= array_shift($groups);
757 
758  foreach ($groups as $group)
759  {
760  $name .= '[' . $group . ']';
761  }
762  }
763  }
764 
765  // If we already have a name segment add the field name as another level.
766  if ($name)
767  {
768  $name .= '[' . $fieldName . ']';
769  }
770  else
771  {
772  $name .= $fieldName;
773  }
774 
775  // If the field should support multiple values add the final array segment.
776  if ($this->multiple)
777  {
778  switch (strtolower((string) $this->element['type']))
779  {
780  case 'text':
781  case 'textarea':
782  case 'email':
783  case 'password':
784  case 'radio':
785  case 'calendar':
786  case 'editor':
787  case 'hidden':
788  break;
789  default:
790  $name .= '[]';
791  }
792  }
793 
794  return $name;
795  }
796 
797  /**
798  * Method to get the field name used.
799  *
800  * @param string $fieldName The field element name.
801  *
802  * @return string The field name
803  *
804  * @since 11.1
805  */
806  protected function getFieldName($fieldName)
807  {
808  if ($fieldName)
809  {
810  return $fieldName;
811  }
812  else
813  {
814  self::$count = self::$count + 1;
815 
816  return self::$generated_fieldname . self::$count;
817  }
818  }
819 
820  /**
821  * Method to get an attribute of the field
822  *
823  * @param string $name Name of the attribute to get
824  * @param mixed $default Optional value to return if attribute not found
825  *
826  * @return mixed Value of the attribute / default
827  *
828  * @since 3.2
829  */
830  public function getAttribute($name, $default = null)
831  {
832  if ($this->element instanceof SimpleXMLElement)
833  {
834  $attributes = $this->element->attributes();
835 
836  // Ensure that the attribute exists
837  if (property_exists($attributes, $name))
838  {
839  $value = $attributes->$name;
840 
841  if ($value !== null)
842  {
843  return (string) $value;
844  }
845  }
846  }
847 
848  return $default;
849  }
850 
851  /**
852  * Method to get a control group with label and input.
853  *
854  * @return string A string containing the html for the control goup
855  *
856  * @since 3.2
857  */
858  public function getControlGroup()
859  {
860  if ($this->hidden)
861  {
862  return $this->getInput();
863  }
864 
865  return
866  '<div class="control-group">'
867  . '<div class="control-label">' . $this->getLabel() . '</div>'
868  . '<div class="controls">' . $this->getInput() . '</div>'
869  . '</div>';
870  }
871 }