MDL-62947 core_form: fix remote code execution exploit in QuickForms
[moodle.git] / lib / pear / HTML / QuickForm / element.php
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4: */
3 // +----------------------------------------------------------------------+
4 // | PHP version 4.0                                                      |
5 // +----------------------------------------------------------------------+
6 // | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
7 // +----------------------------------------------------------------------+
8 // | This source file is subject to version 2.0 of the PHP license,       |
9 // | that is bundled with this package in the file LICENSE, and is        |
10 // | available at through the world-wide-web at                           |
11 // | http://www.php.net/license/2_02.txt.                                 |
12 // | If you did not receive a copy of the PHP license and are unable to   |
13 // | obtain it through the world-wide-web, please send a note to          |
14 // | license@php.net so we can mail you a copy immediately.               |
15 // +----------------------------------------------------------------------+
16 // | Authors: Adam Daniel <adaniel1@eesus.jnj.com>                        |
17 // |          Bertrand Mansion <bmansion@mamasam.com>                     |
18 // +----------------------------------------------------------------------+
19 //
20 // $Id$
22 require_once('HTML/Common.php');
23 /**
24  * Static utility methods.
25  */
26 require_once('HTML/QuickForm/utils.php');
28 /**
29  * Base class for form elements
30  * 
31  * @author       Adam Daniel <adaniel1@eesus.jnj.com>
32  * @author       Bertrand Mansion <bmansion@mamasam.com>
33  * @version      1.3
34  * @since        PHP4.04pl1
35  * @access       public
36  * @abstract
37  */
38 class HTML_QuickForm_element extends HTML_Common
39 {
40     // {{{ properties
42     /**
43      * Label of the field
44      * @var       string
45      * @since     1.3
46      * @access    private
47      */
48     var $_label = '';
50     /**
51      * Form element type
52      * @var       string
53      * @since     1.0
54      * @access    private
55      */
56     var $_type = '';
58     /**
59      * Flag to tell if element is frozen
60      * @var       boolean
61      * @since     1.0
62      * @access    private
63      */
64     var $_flagFrozen = false;
66     /**
67      * Does the element support persistant data when frozen
68      * @var       boolean
69      * @since     1.3
70      * @access    private
71      */
72     var $_persistantFreeze = false;
73     
74     // }}}
75     // {{{ constructor
76     
77     /**
78      * Class constructor
79      * 
80      * @param    string     Name of the element
81      * @param    mixed      Label(s) for the element
82      * @param    mixed      Associative array of tag attributes or HTML attributes name="value" pairs
83      * @since     1.0
84      * @access    public
85      * @return    void
86      */
87     public function __construct($elementName=null, $elementLabel=null, $attributes=null) {
88         parent::__construct($attributes);
89         if (isset($elementName)) {
90             $this->setName($elementName);
91         }
92         if (isset($elementLabel)) {
93             $this->setLabel($elementLabel);
94         }
95     } //end constructor
97     /**
98      * Old syntax of class constructor. Deprecated in PHP7.
99      *
100      * @deprecated since Moodle 3.1
101      */
102     public function HTML_QuickForm_element($elementName=null, $elementLabel=null, $attributes=null) {
103         debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
104         self::__construct($elementName, $elementLabel, $attributes);
105     }
106     
107     // }}}
108     // {{{ apiVersion()
110     /**
111      * Returns the current API version
112      *
113      * @since     1.0
114      * @access    public
115      * @return    float
116      */
117     function apiVersion()
118     {
119         return 2.0;
120     } // end func apiVersion
122     // }}}
123     // {{{ getType()
125     /**
126      * Returns element type
127      *
128      * @since     1.0
129      * @access    public
130      * @return    string
131      */
132     function getType()
133     {
134         return $this->_type;
135     } // end func getType
137     // }}}
138     // {{{ setName()
140     /**
141      * Sets the input field name
142      * 
143      * @param     string    $name   Input field name attribute
144      * @since     1.0
145      * @access    public
146      * @return    void
147      */
148     function setName($name)
149     {
150         // interface method
151     } //end func setName
152     
153     // }}}
154     // {{{ getName()
156     /**
157      * Returns the element name
158      * 
159      * @since     1.0
160      * @access    public
161      * @return    string
162      */
163     function getName()
164     {
165         // interface method
166     } //end func getName
167     
168     // }}}
169     // {{{ setValue()
171     /**
172      * Sets the value of the form element
173      *
174      * @param     string    $value      Default value of the form element
175      * @since     1.0
176      * @access    public
177      * @return    void
178      */
179     function setValue($value)
180     {
181         // interface
182     } // end func setValue
184     // }}}
185     // {{{ getValue()
187     /**
188      * Returns the value of the form element
189      *
190      * @since     1.0
191      * @access    public
192      * @return    mixed
193      */
194     function getValue()
195     {
196         // interface
197         return null;
198     } // end func getValue
199     
200     // }}}
201     // {{{ freeze()
203     /**
204      * Freeze the element so that only its value is returned
205      * 
206      * @access    public
207      * @return    void
208      */
209     function freeze()
210     {
211         $this->_flagFrozen = true;
212     } //end func freeze
214     // }}}
215     // {{{ unfreeze()
217    /**
218     * Unfreezes the element so that it becomes editable
219     *
220     * @access public
221     * @return void
222     * @since  3.2.4
223     */
224     function unfreeze()
225     {
226         $this->_flagFrozen = false;
227     }
229     // }}}
230     // {{{ getFrozenHtml()
232     /**
233      * Returns the value of field without HTML tags
234      * 
235      * @since     1.0
236      * @access    public
237      * @return    string
238      */
239     function getFrozenHtml()
240     {
241         $value = $this->getValue();
242         return ('' != $value? htmlspecialchars($value): '&nbsp;') .
243                $this->_getPersistantData();
244     } //end func getFrozenHtml
245     
246     // }}}
247     // {{{ _getPersistantData()
249    /**
250     * Used by getFrozenHtml() to pass the element's value if _persistantFreeze is on
251     * 
252     * @access private
253     * @return string
254     */
255     function _getPersistantData()
256     {
257         if (!$this->_persistantFreeze) {
258             return '';
259         } else {
260             $id = $this->getAttribute('id');
261             if (isset($id)) {
262                 // Id of persistant input is different then the actual input.
263                 $id = array('id' => $id . '_persistant');
264             } else {
265                 $id = array();
266             }
268             return '<input' . $this->_getAttrString(array(
269                        'type'  => 'hidden',
270                        'name'  => $this->getName(),
271                        'value' => $this->getValue()
272                    ) + $id) . ' />';
273         }
274     }
276     // }}}
277     // {{{ isFrozen()
279     /**
280      * Returns whether or not the element is frozen
281      *
282      * @since     1.3
283      * @access    public
284      * @return    bool
285      */
286     function isFrozen()
287     {
288         return $this->_flagFrozen;
289     } // end func isFrozen
291     // }}}
292     // {{{ setPersistantFreeze()
294     /**
295      * Sets wether an element value should be kept in an hidden field
296      * when the element is frozen or not
297      * 
298      * @param     bool    $persistant   True if persistant value
299      * @since     2.0
300      * @access    public
301      * @return    void
302      */
303     function setPersistantFreeze($persistant=false)
304     {
305         $this->_persistantFreeze = $persistant;
306     } //end func setPersistantFreeze
308     // }}}
309     // {{{ setLabel()
311     /**
312      * Sets display text for the element
313      * 
314      * @param     string    $label  Display text for the element
315      * @since     1.3
316      * @access    public
317      * @return    void
318      */
319     function setLabel($label)
320     {
321         $this->_label = $label;
322     } //end func setLabel
324     // }}}
325     // {{{ getLabel()
327     /**
328      * Returns display text for the element
329      * 
330      * @since     1.3
331      * @access    public
332      * @return    string
333      */
334     function getLabel()
335     {
336         return $this->_label;
337     } //end func getLabel
339     // }}}
340     // {{{ _findValue()
342     /**
343      * Tries to find the element value from the values array
344      * 
345      * @since     2.7
346      * @access    private
347      * @return    mixed
348      */
349     function _findValue(&$values)
350     {
351         if (empty($values)) {
352             return null;
353         }
354         $elementName = $this->getName();
355         if (isset($values[$elementName])) {
356             return $values[$elementName];
357         } elseif (strpos($elementName, '[')) {
358             $keys = str_replace(
359                 array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"),
360                 $elementName
361             );
362             $arrayKeys = explode("']['", $keys);
363             return HTML_QuickForm_utils::recursiveValue($values, $arrayKeys);
364         } else {
365             return null;
366         }
367     } //end func _findValue
369     // }}}
370     // {{{ onQuickFormEvent()
372     /**
373      * Called by HTML_QuickForm whenever form event is made on this element
374      *
375      * @param     string    $event  Name of event
376      * @param     mixed     $arg    event arguments
377      * @param     object    $caller calling object
378      * @since     1.0
379      * @access    public
380      * @return    void
381      */
382     function onQuickFormEvent($event, $arg, &$caller)
383     {
384         switch ($event) {
385             case 'createElement':
386                 static::__construct($arg[0], $arg[1], $arg[2], $arg[3], $arg[4]);
387                 break;
388             case 'addElement':
389                 $this->onQuickFormEvent('createElement', $arg, $caller);
390                 $this->onQuickFormEvent('updateValue', null, $caller);
391                 break;
392             case 'updateValue':
393                 // constant values override both default and submitted ones
394                 // default values are overriden by submitted
395                 $value = $this->_findValue($caller->_constantValues);
396                 if (null === $value) {
397                     $value = $this->_findValue($caller->_submitValues);
398                     if (null === $value) {
399                         $value = $this->_findValue($caller->_defaultValues);
400                     }
401                 }
402                 if (null !== $value) {
403                     $this->setValue($value);
404                 }
405                 break;
406             case 'setGroupValue':
407                 $this->setValue($arg);
408         }
409         return true;
410     } // end func onQuickFormEvent
412     // }}}
413     // {{{ accept()
415    /**
416     * Accepts a renderer
417     *
418     * @param object     An HTML_QuickForm_Renderer object
419     * @param bool       Whether an element is required
420     * @param string     An error message associated with an element
421     * @access public
422     * @return void 
423     */
424     function accept(&$renderer, $required=false, $error=null)
425     {
426         $renderer->renderElement($this, $required, $error);
427     } // end func accept
429     // }}}
430     // {{{ _generateId()
432    /**
433     * Automatically generates and assigns an 'id' attribute for the element.
434     * 
435     * Currently used to ensure that labels work on radio buttons and
436     * checkboxes. Per idea of Alexander Radivanovich.
437     *
438     * @access private
439     * @return void 
440     */
441     function _generateId() {
442         if ($this->getAttribute('id')) {
443             return;
444         }
446         $id = $this->getName();
447         $id = 'id_' . str_replace(array('qf_', '[', ']'), array('', '_', ''), $id);
448         $id = clean_param($id, PARAM_ALPHANUMEXT);
449         $this->updateAttributes(array('id' => $id));
450     }
452     // }}}
453     // {{{ exportValue()
455    /**
456     * Returns a 'safe' element's value
457     *
458     * @param  array   array of submitted values to search
459     * @param  bool    whether to return the value as associative array
460     * @access public
461     * @return mixed
462     */
463     function exportValue(&$submitValues, $assoc = false)
464     {
465         $value = $this->_findValue($submitValues);
466         if (null === $value) {
467             $value = $this->getValue();
468         }
469         return $this->_prepareValue($value, $assoc);
470     }
471     
472     // }}}
473     // {{{ _prepareValue()
475    /**
476     * Used by exportValue() to prepare the value for returning
477     *
478     * @param  mixed   the value found in exportValue()
479     * @param  bool    whether to return the value as associative array
480     * @access private
481     * @return mixed
482     */
483     function _prepareValue($value, $assoc)
484     {
485         if (null === $value) {
486             return null;
487         } elseif (!$assoc) {
488             return $value;
489         } else {
490             $name = $this->getName();
491             if (!strpos($name, '[')) {
492                 return array($name => $value);
493             } else {
494                 $keys = str_replace(
495                     array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"),
496                     $name
497                 );
498                 $keysArray = explode("']['", $keys);
499                 return HTML_QuickForm_utils::recursiveBuild($keysArray, $value);
500             }
501         }
502     }
503     
504     // }}}
505 } // end class HTML_QuickForm_element
506 ?>