Webylon 3.1 API Docs
  • Package
  • Class
  • Tree
  • Deprecated
  • Download
Version: current
  • 3.2
  • 3.1

Packages

  • auth
  • Booking
  • cart
    • shipping
    • steppedcheckout
  • Catalog
  • cms
    • assets
    • batchaction
    • batchactions
    • bulkloading
    • comments
    • content
    • core
    • export
    • newsletter
    • publishers
    • reports
    • security
    • tasks
  • Dashboard
  • DataObjectManager
  • event
  • faq
  • forms
    • actions
    • core
    • fields-basic
    • fields-dataless
    • fields-datetime
    • fields-files
    • fields-formatted
    • fields-formattedinput
    • fields-relational
    • fields-structural
    • transformations
    • validators
  • googlesitemaps
  • guestbook
  • installer
  • newsletter
  • None
  • photo
    • gallery
  • PHP
  • polls
  • recaptcha
  • sapphire
    • api
    • bulkloading
    • control
    • core
    • cron
    • dev
    • email
    • fields-formattedinput
    • filesystem
    • formatters
    • forms
    • i18n
    • integration
    • misc
    • model
    • parsers
    • search
    • security
    • tasks
    • testing
    • tools
    • validation
    • view
    • widgets
  • seo
    • open
      • graph
  • sfDateTimePlugin
  • spamprotection
  • stealth
    • captha
  • subsites
  • userform
    • pagetypes
  • userforms
  • webylon
  • widgets

Classes

  • DataObjectManager_Popup
  • FileDataObjectManager_Popup
  • Form
  • Form_FieldMap
  • FormField
  • FormResponse
  • ImageDataObjectManager_Popup
  • MediawebPage_Popup
  • Order_CancelForm
  • PhotoAlbumManager_Popup
  1 <?php
  2 /**
  3  * Represents a field in a form. 
  4  *  
  5  * A FieldSet contains a number of FormField objects which make up the whole of a form.
  6  * In addition to single fields, FormField objects can be "composite", for example, the {@link TabSet}
  7  * field.  Composite fields let us define complex forms without having to resort to custom HTML.
  8  * 
  9  * <b>Subclassing</b>
 10  * 
 11  * Define a {@link dataValue()} method that returns a value suitable for inserting into a single database field. 
 12  * For example, you might tidy up the format of a date or currency field.
 13  * Define {@link saveInto()} to totally customise saving. 
 14  * For example, data might be saved to the filesystem instead of the data record, 
 15  * or saved to a component of the data record instead of the data record itself.
 16  * 
 17  * @package forms
 18  * @subpackage core
 19  */
 20 class FormField extends RequestHandler {
 21 
 22     // Глобальный флаг использования html5 полей на сайте
 23     protected static $use_html5 = false;
 24     
 25     // разрешение использования html5 для текущего поля
 26     protected $fieldHTML5 = true; 
 27 
 28     protected $form;
 29     protected $name, $title, $value ,$message, $messageType, $extraClass;
 30     protected $extraAttributes = array();
 31     protected $html5Attributes = array();
 32     
 33     // autocomplete parametr, can use without html5
 34     protected $autocomplete = false;
 35     
 36     /**
 37      * @var $description string Adds a "title"-attribute to the markup.
 38      * @todo Implement in all subclasses
 39      */
 40     protected $description;
 41     
 42     /**
 43      * @var $extraClasses array Extra CSS-classes for the formfield-container
 44      */
 45     protected $extraClasses;
 46     
 47     public $dontEscape;
 48     
 49     /**
 50      * @var $rightTitle string Used in SmallFieldHolder() to force a right-aligned label.
 51      */
 52     protected $rightTitle;
 53     
 54     /**
 55      * @var $leftTitle string Used in SmallFieldHolder() to force a left-aligned label with correct spacing.
 56      * Please use $title for FormFields rendered with FieldHolder().
 57      */
 58     protected $leftTitle;
 59     
 60     /**
 61      * Set the "tabindex" HTML attribute on the field.
 62      *
 63      * @var int
 64      */
 65     protected $tabIndex;
 66 
 67     /**
 68      * Stores a reference to the FieldSet that contains this object.
 69      * @var FieldSet
 70      */ 
 71     protected $containerFieldSet;
 72     
 73     /**
 74      * @var $readonly boolean
 75      */
 76     protected $readonly = false;
 77 
 78     /**
 79      * @var $disabled boolean
 80      */
 81     protected $disabled = false;
 82     
 83     /**
 84      * @var Custom Validation Message for the Field
 85      */
 86     protected $customValidationMessage = "";
 87 
 88     static function allow_html5($val = true) {
 89         self::$use_html5 = $val;
 90     }
 91 
 92     static function use_html5() {
 93         return self::$use_html5;
 94     }
 95     
 96     function allowHTML5($val) {
 97         $this->fieldHTML5 = $val; 
 98     }
 99     
100     function useHTML5() {
101         return (self::use_html5() && $this->fieldHTML5);
102     }
103     
104     // возможные значения атрибута autocomplete поля (взято тут https://wiki.whatwg.org/wiki/Autocomplete_Types#4.10.7.3.1.1_Specifying_field_data_type_hints)
105     static $possible_autocomplete_values = array('name', 'given-name', 'family-name', 'street-address', 'city', 'region', 'postal-code', 'country-name', 'email', 'tel');
106     
107     /**
108      * Create a new field.
109      * @param name The internal field name, passed to forms.
110      * @param title The field label.
111      * @param value The value of the field.
112      * @param form Reference to the container form
113      * @param maxLength The Maximum length of the attribute
114      */
115     function __construct($name, $title = null, $value = null, $form = null, $rightTitle = null) {
116         $this->name = $name;
117         $this->title = ($title === null) ? $name : $title;
118 
119         if($value !== NULL) $this->setValue($value);
120         if($form) $this->setForm($form);
121 
122         parent::__construct();
123     }
124     
125     // выставляем атрибут autocomplete - возможно нужна проверка на подходящие значения
126     function setHTML5Attribute($title, $value) {        
127         $this->html5Attributes[$title] = $value;        
128     }
129     
130     function setAutocomplete($value) {
131         if (array_search($value, self::$possible_autocomplete_values) !== false) {
132             $this->autocomplete = $value;
133             return true;
134         }
135         return false;
136     }
137     
138     /**
139      * Return a Link to this field
140      */
141     function Link($action = null) {
142         return Controller::join_links($this->form->FormAction(), 'field/' . $this->name, $action);
143     }
144     
145     /**
146      * Returns the HTML ID of the field - used in the template by label tags.
147      * The ID is generated as FormName_FieldName.  All Field functions should ensure
148      * that this ID is included in the field.
149      */
150     function id() { 
151         $name = ereg_replace('(^-)|(-$)','',ereg_replace('[^A-Za-z0-9_-]+','-',$this->Name()));
152         if($this->form) return $this->form->FormName() . '_' . $name;
153         else return $name;
154     }
155     
156     /**
157      * Returns the field name - used by templates.
158      * 
159      * @return string
160      */
161     function Name() {
162         return $this->name;
163     }
164     
165     function attrName() {
166         return $this->name;
167     }
168     
169     /** 
170      * Returns the field message, used by form validation.
171      * Use {@link setError()} to set this property.
172      * 
173      * @return string
174      */
175     function Message() {
176         return $this->message;
177     } 
178     
179     /** 
180      * Returns the field message type, used by form validation.
181      * Arbitrary value which is mostly used for CSS classes
182      * in the rendered HTML, e.g. "required".
183      * Use {@link setError()} to set this property.
184      * 
185      * @return string
186      */
187     function MessageType() {
188         return $this->messageType;
189     } 
190     
191     /**
192      * Returns the field value - used by templates.
193      */
194     function Value() {
195         return $this->value;
196     }
197     
198     /**
199      * Method to save this form field into the given data object.
200      * By default, makes use of $this->dataValue()
201      */
202     function saveInto(DataObjectInterface $record) {
203         if($this->name) {
204             $record->setCastedField($this->name, $this->dataValue());
205         }
206     }
207     
208     /**
209      * Returns the field value suitable for insertion into the data object
210      */
211     function dataValue() { 
212         return $this->value;
213     }
214     
215     /**
216      * Returns the field label - used by templates.
217      */
218     function Title() { 
219         return $this->title;
220     }
221     
222     function setTitle($val) { 
223         $this->title = $val;
224     }
225     
226     function RightTitle() {
227         return $this->rightTitle;
228     }
229     
230     function setRightTitle($val) { 
231         $this->rightTitle = $val;
232     }
233 
234     function LeftTitle() {
235         return $this->leftTitle;
236     }
237     
238     function setLeftTitle($val) { 
239         $this->leftTitle = $val;
240     }
241     
242     /**
243      * Set tabindex HTML attribute
244      * (defaults to none).
245      *
246      * @param int $index
247      */
248     public function setTabIndex($index) {
249         $this->tabIndex = $index;
250     }
251     
252     /**
253      * Get tabindex (if previously set)
254      *
255      * @return int
256      */
257     public function getTabIndex() {
258         return $this->tabIndex;
259     }
260 
261     /**
262      * Get tabindex HTML string
263      *
264      * @param int $increment Increase current tabindex by this value
265      * @return string
266      */
267     protected function getTabIndexHTML($increment = 0) {
268         $tabIndex = (int)$this->getTabIndex() + (int)$increment;
269         return (is_numeric($tabIndex)) ? ' tabindex = "' . $tabIndex . '"' : '';
270     }
271     
272     /**
273      * Compiles all CSS-classes. Optionally includes a "nolabel"-class
274      * if no title was set on the formfield.
275      * Uses {@link Message()} and {@link MessageType()} to add validatoin
276      * error classes which can be used to style the contained tags.
277      * 
278      * @return String CSS-classnames
279      */
280     function extraClass() {
281         $output = "";
282         if(is_array($this->extraClasses)) {
283             $output = " " . implode($this->extraClasses, " ");
284         }
285         
286         // Allow customization of label and field tag positioning
287         if(!$this->Title()) $output .= " nolabel";
288         
289         // Allow custom styling of any element in the container based
290         // on validation errors, e.g. red borders on input tags.
291         // CSS-Class needs to be different from the one rendered
292         // through {@link FieldHolder()}
293         if($this->Message()) $output .= " holder-" . $this->MessageType();
294         
295         return $output;
296     }
297     
298     /**
299      * Add a CSS-class to the formfield-container.
300      * 
301      * @param $class String
302      */
303     function addExtraClass($class) {
304         $this->extraClasses[$class] = $class;
305     }
306 
307     /**
308      * Remove a CSS-class from the formfield-container.
309      * 
310      * @param $class String
311      */
312     function removeExtraClass($class) {
313         if(isset($this->extraClasses) && array_key_exists($class, $this->extraClasses)) unset($this->extraClasses[$class]);
314     }
315 
316     /**
317      * Add an attribute
318      * 
319      * @param $name String
320      * @param $value String
321      */
322     function addExtraAttribute($name, $value) {
323         $this->extraAttributes[$name] = $value;
324     }
325 
326     /**
327      * Remove an attribute
328      * 
329      * @param $name String
330      */
331     function removeExtraAttribute($name) {
332         if(isset($this->extraAttributes) && array_key_exists($name, $this->extraAttributes)) unset($this->extraAttributes[$name]);
333     }
334 
335     /**
336      * Returns a version of a title suitable for insertion into an HTML attribute
337      */
338     function attrTitle() {
339         return Convert::raw2att($this->title);
340     }
341     /**
342      * Returns a version of a title suitable for insertion into an HTML attribute
343      */
344     function attrValue() {
345         return Convert::raw2att($this->value);
346     }
347     
348     /**
349      * Set the field value.
350      * Returns $this.
351      */
352     function setValue($value) {
353         $this->value = $value; return $this;
354     }
355     
356     /**
357      * Set the field name
358      */
359     function setName($name) {
360         $this->name = $name;
361     }
362     
363     /**
364      * Set the container form.
365      * This is called whenever you create a new form and put fields inside it, so that you don't
366      * have to worry about linking the two.
367      */
368     function setForm($form) {
369         $this->form = $form; 
370     }
371     
372     /**
373      * Get the currently used form.
374      *
375      * @return Form
376      */
377     function getForm() {
378         return $this->form; 
379     }
380     
381     /**
382      * Return TRUE if security token protection is enabled on the parent {@link Form}.
383      *
384      * @return bool
385      */
386     public function securityTokenEnabled() {
387         return $this->getForm() && $this->getForm()->securityTokenEnabled();
388     }
389     
390     /**
391      * Sets the error message to be displayed on the form field
392      * Set by php validation of the form
393      */
394     function setError($message,$messageType){
395         $this->message = $message; 
396         $this->messageType = $messageType; 
397     }
398     
399     /**
400      * Set the custom error message to show instead of the default
401      * format of Please Fill In XXX. Different from setError() as
402      * that appends it to the standard error messaging
403      * 
404      * @param String Message for the error
405      */
406     public function setCustomValidationMessage($msg) {
407         $this->customValidationMessage = $msg;
408     }
409     
410     /**
411      * Get the custom error message for this form field. If a custom
412      * message has not been defined then just return blank. The default
413      * error is defined on {@link Validator}.
414      *
415      * @todo Should the default error message be stored here instead
416      * @return String
417      */
418     public function getCustomValidationMessage() {
419         return $this->customValidationMessage;
420     }
421     
422     /**
423      * Returns the form field - used by templates.
424      * Although FieldHolder is generally what is inserted into templates, all of the field holder
425      * templates make use of $Field.  It's expected that FieldHolder will give you the "complete"
426      * representation of the field on the form, whereas Field will give you the core editing widget,
427      * such as an input tag.
428      * 
429      * Our base FormField class just returns a span containing the value.  This should be overridden!
430      */
431     function Field() {
432         if($this->value) $value = $this->dontEscape ? ($this->reserveNL ? Convert::raw2xml($this->value) : $this->value) : Convert::raw2xml($this->value);
433         else $value = '<i>(' . _t('FormField.NONE', 'none') . ')</i>';
434     
435         $attributes = array(
436             'id' => $this->id(),
437             'class' => 'readonly ' . $this->class . ($this->extraClass() ? $this->extraClass() : '')
438         );
439         
440         $hiddenAttributes = array(
441             'type' => 'hidden',
442             'name' => $this->name,
443             'value' => $this->value,
444             'tabindex' => $this->getTabIndex()
445         );
446         
447         $containerSpan = $this->createTag('span', $attributes, $value);
448         $hiddenInput = $this->createTag('input', $hiddenAttributes);
449         
450         return $containerSpan . "\n" . $hiddenInput;
451     }
452     /**
453      * Returns a "Field Holder" for this field - used by templates.
454      * Forms are constructed from by concatenating a number of these field holders.  The default
455      * field holder is a label and form field inside a paragraph tag.
456      * 
457      * Composite fields can override FieldHolder to create whatever visual effects you like.  It's
458      * a good idea to put the actual HTML for field holders into templates.  The default field holder
459      * is the DefaultFieldHolder template.  This lets you override the HTML for specific sites, if it's
460      * necessary.
461      * 
462      * @todo Add "validationError" if needed.
463      */
464     function FieldHolder() {
465         if ($this->Required()) $this->addExtraClass('requiredField');
466 
467         $Title = $this->XML_val('Title');
468         $Message = $this->XML_val('Message');
469         $MessageType = $this->XML_val('MessageType');
470         $RightTitle = $this->XML_val('RightTitle');
471         $Type = $this->XML_val('Type');
472         $extraClass = $this->XML_val('extraClass');
473         $Name = $this->XML_val('Name');
474         $Field = $this->XML_val('Field');
475         
476         // Only of the the following titles should apply
477         $titleBlock = (!empty($Title)) ? "<label class=\"left\" for=\"{$this->id()}\">$Title</label>" : "";
478         $rightTitleBlock = (!empty($RightTitle)) ? "<label class=\"right\" for=\"{$this->id()}\">$RightTitle</label>" : "";
479 
480         // $MessageType is also used in {@link extraClass()} with a "holder-" prefix
481         $messageBlock = (!empty($Message)) ? "<span class=\"message $MessageType\">$Message</span>" : "";
482 
483         return <<<HTML
484 <div id="$Name" class="field $Type $extraClass">$titleBlock<div class="middleColumn">$Field</div>$rightTitleBlock$messageBlock</div>
485 HTML;
486     }
487 
488     /**
489      * Returns a restricted field holder used within things like FieldGroups.
490      */
491     function SmallFieldHolder() {
492        if ($this->Required()) $this->addExtraClass('requiredField');
493        $extraClass = $this->XML_val('extraClass');
494 
495         $result = '';
496         // set label
497         if($title = $this->RightTitle()){
498             $result .= "<label class=\"right $extraClass\" for=\"" . $this->id() . "\">{$title}</label>\n";
499         } elseif($title = $this->LeftTitle()) {
500             $result .= "<label class=\"left $extraClass\" for=\"" . $this->id() . "\">{$title}</label>\n";
501         } elseif($title = $this->Title()) {
502             $result .= "<label class=\"$extraClass\" for=\"" . $this->id() . "\">{$title}</label>\n";
503         }
504         
505         $result .= $this->Field();
506         
507         return $result;
508     }
509 
510     
511     /**
512      * Returns true if this field is a composite field.
513      * To create composite field types, you should subclass {@link CompositeField}.
514      */
515     function isComposite() { return false; }
516     
517     /**
518      * Returns true if this field has its own data.
519      * Some fields, such as titles and composite fields, don't actually have any data.  It doesn't
520      * make sense for data-focused methods to look at them.  By overloading hasData() to return false,
521      * you can prevent any data-focused methods from looking at it.
522      *
523      * @see FieldSet::collateDataFields()
524      */
525     function hasData() { return true; }
526 
527     /**
528      * @return boolean
529      */
530     function isReadonly() { 
531         return $this->readonly; 
532     }
533 
534     /**
535      * Sets readonly-flag on form-field. Please use performReadonlyTransformation()
536      * to actually transform this instance.
537      * @param $bool boolean Setting "false" has no effect on the field-state.
538      */
539     function setReadonly($bool) { 
540         $this->readonly = $bool; 
541     }
542     
543     /**
544      * @return boolean
545      */
546     function isDisabled() { 
547         return $this->disabled; 
548     }
549 
550     /**
551      * Sets disabed-flag on form-field. Please use performDisabledTransformation()
552      * to actually transform this instance.
553      * @param $bool boolean Setting "false" has no effect on the field-state.
554      */
555     function setDisabled($bool) { 
556         $this->disabled = $bool; 
557     }
558     
559     /**
560      * Returns a readonly version of this field
561      */
562     function performReadonlyTransformation() {
563         $field = new ReadonlyField($this->name, $this->title, $this->value);
564         $field->addExtraClass($this->extraClass());
565         $field->setForm($this->form);
566         return $field;
567     }
568     
569     /**
570      * Return a disabled version of this field
571      */
572     function performDisabledTransformation() {
573         $clone = clone $this;
574         $disabledClassName = $clone->class . '_Disabled';
575         if( ClassInfo::exists( $disabledClassName ) )
576             return new $disabledClassName( $this->name, $this->title, $this->value );
577         elseif($clone->hasMethod('setDisabled')){
578             $clone->setDisabled(true);
579             return $clone;
580         }else{
581             return $this->performReadonlyTransformation();
582         }
583     }
584     
585     function transform(FormTransformation $trans) {
586         return $trans->transform($this);
587     }
588     
589     function hasClass($class){
590         $patten = '/'.strtolower($class).'/i';
591         $subject = strtolower($this->class." ".$this->extraClass());
592         return preg_match($patten, $subject);
593     }
594     
595     /**
596      * Returns the field type - used by templates.
597      * The field type is the class name with the word Field dropped off the end, all lowercase.
598      * It's handy for assigning HTML classes.
599      */
600     function Type() {return strtolower(ereg_replace('Field$','',$this->class)); }
601     
602     /**
603      * Construct and return HTML tag.
604      * 
605      * @todo Transform to static helper method.
606      */
607     function createTag($tag, $attributes, $content = null, $useHTML5 = true) {
608         if ($this->Required()) {            
609             $this->setHTML5Attribute('required', 'required');
610         }
611         $preparedAttributes = '';
612         $attributes = array_merge($attributes, $this->extraAttributes);
613         if ($useHTML5 && $this->useHTML5()) {
614             $attributes = array_merge($attributes, $this->html5Attributes);
615         }
616         if ($this->autocomplete) {
617             $attributes = array_merge($attributes, array('autocomplete' => $this->autocomplete));
618         }
619         foreach($attributes as $k => $v) {
620             // Note: as indicated by the $k == value item here; the decisions over what to include in the attributes can sometimes get finicky
621             if(!empty($v) || $v === '0' || $k == 'value') $preparedAttributes .= " $k=\"" . Convert::raw2att($v) . "\"";
622         }
623 
624         if($content || $tag != 'input') return "<$tag$preparedAttributes>$content</$tag>";
625         else return "<$tag$preparedAttributes />";
626     }
627     
628     /**
629      * javascript handler Functions for each field type by default
630      * formfield doesnt have a validation function
631      * 
632      * @todo shouldn't this be an abstract method?
633      */
634     function jsValidation() {
635     }
636     
637     /**
638      * Validation Functions for each field type by default
639      * formfield doesnt have a validation function
640      * 
641      * @todo shouldn't this be an abstract method?
642      */
643     function validate() {
644         return true;
645     }
646 
647     /**
648      * Describe this field, provide help text for it.
649      * The function returns this so it can be used like this:
650      * $action = FormAction::create('submit', 'Submit')->describe("Send your changes to be approved")
651      */
652     function describe($description) {
653         $this->description = $description;
654         return $this;
655     }
656     
657     function debug() {
658         return "$this->class ($this->name: $this->title : <font style='color:red;'>$this->message</font>) = $this->value";
659     }
660     
661     /**
662      * This function is used by the template processor.  If you refer to a field as a $ variable, it
663      * will return the $Field value.
664      */
665     function forTemplate() {
666         return $this->Field();
667     }
668     
669     /**
670      * @uses Validator->fieldIsRequired()
671      * @return boolean
672      */
673     function Required() {
674         if($this->form && ($validator = $this->form->Validator)) {
675             return $validator->fieldIsRequired($this->name);
676         }
677     }
678     
679     /**
680      * Takes a fieldname and converts camelcase to spaced
681      * words. Also resolves combined fieldnames with dot syntax
682      * to spaced words.
683      * 
684      * Examples:
685      * - 'TotalAmount' will return 'Total Amount'
686      * - 'Organisation.ZipCode' will return 'Organisation Zip Code'
687      *
688      * @param string $fieldName
689      * @return string
690      */
691     public function name_to_label($fieldName) {
692         if(strpos($fieldName, '.') !== false) {
693             $parts = explode('.', $fieldName);
694             $label = $parts[count($parts)-2] . ' ' . $parts[count($parts)-1];
695         } else {
696             $label = $fieldName;
697         }
698         $label = preg_replace("/([a-z]+)([A-Z])/","$1 $2", $label);
699         
700         return $label;
701     }
702     
703     /**
704      * Set the fieldset that contains this field. 
705      *
706      * @param FieldSet $containerFieldSet
707      */ 
708     function setContainerFieldSet($containerFieldSet) {
709         $this->containerFieldSet = $containerFieldSet;
710     }
711     
712     function rootFieldSet() {
713         if(is_object($this->containerFieldSet)) return $this->containerFieldSet->rootFieldSet();
714         else user_error("rootFieldSet() called on $this->class object without a containerFieldSet", E_USER_ERROR);
715     }
716     
717 }
718 ?>
[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -
Webylon 3.1 API Docs API documentation generated by ApiGen 2.8.0