From da6f876395695e34d88fababfdc033411da8729a Mon Sep 17 00:00:00 2001 From: jamiesensei Date: Sun, 24 Sep 2006 17:04:51 +0000 Subject: [PATCH] This is a first cut of a new formslib.php a library of classes for creating and securely processing forms in Moodle, based on PEAR QuickForms. Only uses XHTML and CSS and no table tags. This is NOT YET PART OF THE MOODLE API it is here for experimental purposes. --- lang/en_utf8/form.php | 5 + lib/form/checkbox.php | 45 + lib/form/dateselector.php | 159 ++ lib/form/file.php | 45 + lib/form/group.php | 47 + lib/form/htmleditor.php | 49 + lib/form/password.php | 45 + lib/form/radio.php | 45 + lib/form/req.gif | Bin 0 -> 243 bytes lib/form/req.png | Bin 0 -> 30246 bytes lib/form/select.php | 45 + lib/form/text.php | 44 + lib/form/textarea.php | 45 + lib/formslib.php | 279 +++ lib/pear/HTML/Common.php | 428 ++++ lib/pear/HTML/QuickForm.php | 1992 +++++++++++++++++ .../HTML/QuickForm/DHTMLRulesTableless.php | 208 ++ lib/pear/HTML/QuickForm/Renderer.php | 150 ++ lib/pear/HTML/QuickForm/Renderer/Array.php | 319 +++ lib/pear/HTML/QuickForm/Renderer/Default.php | 474 ++++ lib/pear/HTML/QuickForm/Renderer/Object.php | 432 ++++ .../HTML/QuickForm/Renderer/Tableless.php | 313 +++ lib/pear/HTML/QuickForm/Rule.php | 67 + lib/pear/HTML/QuickForm/Rule/Callback.php | 113 + lib/pear/HTML/QuickForm/Rule/Compare.php | 95 + lib/pear/HTML/QuickForm/Rule/Email.php | 61 + lib/pear/HTML/QuickForm/Rule/Range.php | 64 + lib/pear/HTML/QuickForm/Rule/Regex.php | 89 + lib/pear/HTML/QuickForm/Rule/Required.php | 52 + lib/pear/HTML/QuickForm/RuleRegistry.php | 336 +++ lib/pear/HTML/QuickForm/advcheckbox.php | 277 +++ lib/pear/HTML/QuickForm/autocomplete.php | 249 +++ lib/pear/HTML/QuickForm/button.php | 73 + lib/pear/HTML/QuickForm/checkbox.php | 268 +++ lib/pear/HTML/QuickForm/date.php | 496 ++++ lib/pear/HTML/QuickForm/element.php | 479 ++++ lib/pear/HTML/QuickForm/file.php | 347 +++ lib/pear/HTML/QuickForm/group.php | 579 +++++ lib/pear/HTML/QuickForm/header.php | 65 + lib/pear/HTML/QuickForm/hidden.php | 87 + lib/pear/HTML/QuickForm/hiddenselect.php | 107 + lib/pear/HTML/QuickForm/hierselect.php | 578 +++++ lib/pear/HTML/QuickForm/html.php | 67 + lib/pear/HTML/QuickForm/image.php | 119 + lib/pear/HTML/QuickForm/input.php | 202 ++ lib/pear/HTML/QuickForm/link.php | 192 ++ lib/pear/HTML/QuickForm/password.php | 108 + lib/pear/HTML/QuickForm/radio.php | 244 ++ lib/pear/HTML/QuickForm/reset.php | 72 + lib/pear/HTML/QuickForm/select.php | 604 +++++ lib/pear/HTML/QuickForm/static.php | 193 ++ lib/pear/HTML/QuickForm/submit.php | 82 + lib/pear/HTML/QuickForm/text.php | 91 + lib/pear/HTML/QuickForm/textarea.php | 222 ++ lib/pear/HTML/QuickForm/xbutton.php | 145 ++ theme/chameleon/config.php | 2 +- theme/formal_white/config.php | 2 +- theme/metal/config.php | 2 +- theme/orangewhite/config.php | 2 +- theme/orangewhitepda/config.php | 2 +- theme/standard/config.php | 2 +- theme/standard/styles_form.css | 62 + theme/standardblue/config.php | 2 +- theme/standardgreen/config.php | 2 +- theme/standardlogo/config.php | 2 +- theme/standardred/config.php | 2 +- theme/standardwhite/config.php | 2 +- theme/wood/config.php | 2 +- 68 files changed, 12066 insertions(+), 12 deletions(-) create mode 100644 lang/en_utf8/form.php create mode 100644 lib/form/checkbox.php create mode 100644 lib/form/dateselector.php create mode 100644 lib/form/file.php create mode 100644 lib/form/group.php create mode 100644 lib/form/htmleditor.php create mode 100644 lib/form/password.php create mode 100644 lib/form/radio.php create mode 100644 lib/form/req.gif create mode 100644 lib/form/req.png create mode 100644 lib/form/select.php create mode 100644 lib/form/text.php create mode 100644 lib/form/textarea.php create mode 100644 lib/formslib.php create mode 100644 lib/pear/HTML/Common.php create mode 100644 lib/pear/HTML/QuickForm.php create mode 100644 lib/pear/HTML/QuickForm/DHTMLRulesTableless.php create mode 100644 lib/pear/HTML/QuickForm/Renderer.php create mode 100644 lib/pear/HTML/QuickForm/Renderer/Array.php create mode 100644 lib/pear/HTML/QuickForm/Renderer/Default.php create mode 100644 lib/pear/HTML/QuickForm/Renderer/Object.php create mode 100644 lib/pear/HTML/QuickForm/Renderer/Tableless.php create mode 100644 lib/pear/HTML/QuickForm/Rule.php create mode 100644 lib/pear/HTML/QuickForm/Rule/Callback.php create mode 100644 lib/pear/HTML/QuickForm/Rule/Compare.php create mode 100644 lib/pear/HTML/QuickForm/Rule/Email.php create mode 100644 lib/pear/HTML/QuickForm/Rule/Range.php create mode 100644 lib/pear/HTML/QuickForm/Rule/Regex.php create mode 100644 lib/pear/HTML/QuickForm/Rule/Required.php create mode 100644 lib/pear/HTML/QuickForm/RuleRegistry.php create mode 100644 lib/pear/HTML/QuickForm/advcheckbox.php create mode 100644 lib/pear/HTML/QuickForm/autocomplete.php create mode 100644 lib/pear/HTML/QuickForm/button.php create mode 100644 lib/pear/HTML/QuickForm/checkbox.php create mode 100644 lib/pear/HTML/QuickForm/date.php create mode 100644 lib/pear/HTML/QuickForm/element.php create mode 100644 lib/pear/HTML/QuickForm/file.php create mode 100644 lib/pear/HTML/QuickForm/group.php create mode 100644 lib/pear/HTML/QuickForm/header.php create mode 100644 lib/pear/HTML/QuickForm/hidden.php create mode 100644 lib/pear/HTML/QuickForm/hiddenselect.php create mode 100644 lib/pear/HTML/QuickForm/hierselect.php create mode 100644 lib/pear/HTML/QuickForm/html.php create mode 100644 lib/pear/HTML/QuickForm/image.php create mode 100644 lib/pear/HTML/QuickForm/input.php create mode 100644 lib/pear/HTML/QuickForm/link.php create mode 100644 lib/pear/HTML/QuickForm/password.php create mode 100644 lib/pear/HTML/QuickForm/radio.php create mode 100644 lib/pear/HTML/QuickForm/reset.php create mode 100644 lib/pear/HTML/QuickForm/select.php create mode 100644 lib/pear/HTML/QuickForm/static.php create mode 100644 lib/pear/HTML/QuickForm/submit.php create mode 100644 lib/pear/HTML/QuickForm/text.php create mode 100644 lib/pear/HTML/QuickForm/textarea.php create mode 100644 lib/pear/HTML/QuickForm/xbutton.php create mode 100644 theme/standard/styles_form.css diff --git a/lang/en_utf8/form.php b/lang/en_utf8/form.php new file mode 100644 index 0000000000..ecaebdfa70 --- /dev/null +++ b/lang/en_utf8/form.php @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/lib/form/checkbox.php b/lib/form/checkbox.php new file mode 100644 index 0000000000..9a7f6f9ee0 --- /dev/null +++ b/lib/form/checkbox.php @@ -0,0 +1,45 @@ +_helpbutton=call_user_func_array('helpbutton', $helpbuttonargs); + } + /** + * get html for help button + * + * @access public + * @return string html for help button + */ + function getHelpButton(){ + return $this->_helpbutton; + } +} +?> \ No newline at end of file diff --git a/lib/form/dateselector.php b/lib/form/dateselector.php new file mode 100644 index 0000000000..c3cefd0b2f --- /dev/null +++ b/lib/form/dateselector.php @@ -0,0 +1,159 @@ +libdir/form/group.php"; +require_once "$CFG->libdir/formslib.php"; + +/** + * Class for a group of elements used to input a date. + * + * Emulates moodle print_date_selector function + * + * @author Jamie Pratt + * @access public + */ +class moodleform_date_selector extends moodleform_group +{ + /** + * Control the fieldnames for form elements + * + * day => string day fieldname + * month => string month fieldname + * year => string year fieldname + * timezone => float/string timezone + * applydst => apply users daylight savings adjustment? + */ + var $_options = array('startyear'=>1970, 'stopyear'=>2020, + 'timezone'=>99, 'applydst'=>true); + + /** + * These complement separators, they are appended to the resultant HTML + * @access private + * @var array + */ + var $_wrap = array('', ''); + + /** + * Class constructor + * + * @access public + * @param string Element's name + * @param mixed Label(s) for an element + * @param array Options to control the element's display + * @param mixed Either a typical HTML attribute string or an associative array + */ + function moodleform_date_selector($elementName = null, $elementLabel = null, $options = array(), $attributes = null) + { + $this->HTML_QuickForm_element($elementName, $elementLabel, $attributes); + $this->_persistantFreeze = true; + $this->_appendName = true; + $this->_type = 'date_selector'; + // set the options, do not bother setting bogus ones + if (is_array($options)) { + foreach ($options as $name => $value) { + if (isset($this->_options[$name])) { + if (is_array($value) && is_array($this->_options[$name])) { + $this->_options[$name] = @array_merge($this->_options[$name], $value); + } else { + $this->_options[$name] = $value; + } + } + } + } + } + + // }}} + // {{{ _createElements() + + function _createElements() + { + $this->_elements = array(); + for ($i=1; $i<=31; $i++) { + $days[$i] = $i; + } + for ($i=1; $i<=12; $i++) { + $months[$i] = userdate(gmmktime(12,0,0,$i,1,2000), "%B"); + } + for ($i=$this->_options['startyear']; $i<=$this->_options['stopyear']; $i++) { + $years[$i] = $i; + } + $this->_elements[] =& moodleform::createElement('select', 'day', null, $days, $this->getAttributes(), true); + $this->_elements[] =& moodleform::createElement('select','month', null, $months, $this->getAttributes(), true); + $this->_elements[] =& moodleform::createElement('select','year', null, $years, $this->getAttributes(), true); + + } + + // }}} + // {{{ setValue() + + function setValue($value) + { + if (!($value)) { + $value = time(); + } + if (!is_array($value)) { + $currentdate = usergetdate($value); + $value = array( + 'day' => $currentdate['mday'], + 'month' => $currentdate['mon'], + 'year' => $currentdate['year']); + + } + parent::setValue($value); + } + + // }}} + // {{{ toHtml() + + function toHtml() + { + include_once('HTML/QuickForm/Renderer/Default.php'); + $renderer =& new HTML_QuickForm_Renderer_Default(); + $renderer->setElementTemplate('{element}'); + parent::accept($renderer); + return $this->_wrap[0] . $renderer->toHtml() . $this->_wrap[1]; + } + + // }}} + // {{{ accept() + + function accept(&$renderer, $required = false, $error = null) + { + $renderer->renderElement($this, $required, $error); + } + + // }}} + // {{{ onQuickFormEvent() + + function onQuickFormEvent($event, $arg, &$caller) + { + if ('updateValue' == $event) { + return HTML_QuickForm_element::onQuickFormEvent($event, $arg, $caller); + } else { + return parent::onQuickFormEvent($event, $arg, $caller); + } + } + /** + * Output a timestamp. Give it the name of the group. + * + * @param array $submitValues + * @param bool $assoc + * @return array + */ + function exportValue(&$submitValues, $assoc = false) + { + $value = null; + $valuearray = $this->_elements[0]->exportValue($submitValues[$this->getName()], true); + $valuearray +=$this->_elements[1]->exportValue($submitValues[$this->getName()], true); + $valuearray +=$this->_elements[2]->exportValue($submitValues[$this->getName()], true); + $value[$this->getName()]=make_timestamp($valuearray['year'], + $valuearray['month'], + $valuearray['day'], + 0,0,0, + $this->_options['timezone'], + $this->_options['applydst']); + return $value; + } + + // }}} +} +?> \ No newline at end of file diff --git a/lib/form/file.php b/lib/form/file.php new file mode 100644 index 0000000000..f877f101d3 --- /dev/null +++ b/lib/form/file.php @@ -0,0 +1,45 @@ +_helpbutton=call_user_func_array('helpbutton', $helpbuttonargs); + } + /** + * get html for help button + * + * @access public + * @return string html for help button + */ + function getHelpButton(){ + return $this->_helpbutton; + } +} +?> \ No newline at end of file diff --git a/lib/form/group.php b/lib/form/group.php new file mode 100644 index 0000000000..b83a740fd1 --- /dev/null +++ b/lib/form/group.php @@ -0,0 +1,47 @@ + + * @author Bertrand Mansion + * @version 1.0 + * @since PHP4.04pl1 + * @access public + */ +class moodleform_group extends HTML_QuickForm_group{ + /** + * html for help button, if empty then no help + * + * @var string + */ + var $_helpbutton=''; + /** + * set html for help button + * + * @access public + * @param array $help array of arguments to make a help button + */ + function setHelpButton($helpbuttonargs){ + if (!is_array($helpbuttonargs)){ + $helpbuttonargs=array($helpbuttonargs); + }else{ + $helpbuttonargs=$helpbuttonargs; + } + //we do this to to return html instead of printing it + //without having to specify it in every call to make a button. + $defaultargs=array('', '', 'moodle', true, false, '', true); + $helpbuttonargs=$helpbuttonargs + $defaultargs ; + $this->_helpbutton=call_user_func_array('helpbutton', $helpbuttonargs); + } + /** + * get html for help button + * + * @access public + * @return string html for help button + */ + function getHelpButton(){ + return $this->_helpbutton; + } +} \ No newline at end of file diff --git a/lib/form/htmleditor.php b/lib/form/htmleditor.php new file mode 100644 index 0000000000..5882cf4316 --- /dev/null +++ b/lib/form/htmleditor.php @@ -0,0 +1,49 @@ +libdir/form/textarea.php"); + +/** + * HTML class for htmleditor type element + * + * @author Jamie Pratt + * @access public + */ +class moodleform_htmleditor extends moodleform_textarea{ + var $_type = 'htmleditor'; + var $_elementTemplateType='default'; + var $_canUseHtmlEditor; + var $_options=array('course'=>0); + function moodleform_htmleditor($elementName=null, $elementLabel=null, $attributes=null){ + $this->_canUseHtmlEditor=can_use_html_editor(); + if ($this->_canUseHtmlEditor){ + $this->_elementTemplateType='wide'; + }else{ + $this->_elementTemplateType='default'; + } + parent::moodleform_textarea($elementName, $elementLabel, $attributes); + } + function getElementTemplateType(){ + return $this->_elementTemplateType; + } + function toHtml(){ + ob_start(); + use_html_editor($this->getName()); + $script=ob_get_clean(); + if ($this->_flagFrozen) { + return $this->getFrozenHtml(); + } else { + return $this->_getTabs() . + print_textarea($this->_canUseHtmlEditor, + $this->getAttribute('rows'), + $this->getAttribute('cols'), + $this->getAttribute('width'), + $this->getAttribute('height'), + $this->getName(), + preg_replace("/(\r\n|\n|\r)/", ' ',$this->getValue()), + $this->_options['course'], + true).$script; + } + } //end func toHtml + +} +?> \ No newline at end of file diff --git a/lib/form/password.php b/lib/form/password.php new file mode 100644 index 0000000000..d719da8486 --- /dev/null +++ b/lib/form/password.php @@ -0,0 +1,45 @@ +_helpbutton=call_user_func_array('helpbutton', $helpbuttonargs); + } + /** + * get html for help button + * + * @access public + * @return string html for help button + */ + function getHelpButton(){ + return $this->_helpbutton; + } +} +?> \ No newline at end of file diff --git a/lib/form/radio.php b/lib/form/radio.php new file mode 100644 index 0000000000..d9a57dae9b --- /dev/null +++ b/lib/form/radio.php @@ -0,0 +1,45 @@ +_helpbutton=call_user_func_array('helpbutton', $helpbuttonargs); + } + /** + * get html for help button + * + * @access public + * @return string html for help button + */ + function getHelpButton(){ + return $this->_helpbutton; + } +} +?> \ No newline at end of file diff --git a/lib/form/req.gif b/lib/form/req.gif new file mode 100644 index 0000000000000000000000000000000000000000..42e5502cba1e51aec6279d1664c9d3f464f0c5ee GIT binary patch literal 243 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 z?zvlf4X0Tb?Th~$_&fU5_helpbutton=call_user_func_array('helpbutton', $helpbuttonargs); + } + /** + * get html for help button + * + * @access public + * @return string html for help button + */ + function getHelpButton(){ + return $this->_helpbutton; + } +} +?> \ No newline at end of file diff --git a/lib/form/text.php b/lib/form/text.php new file mode 100644 index 0000000000..d1bf0c4d94 --- /dev/null +++ b/lib/form/text.php @@ -0,0 +1,44 @@ +_helpbutton=call_user_func_array('helpbutton', $helpbuttonargs); + } + /** + * get html for help button + * + * @access public + * @return string html for help button + */ + function getHelpButton(){ + return $this->_helpbutton; + } +} \ No newline at end of file diff --git a/lib/form/textarea.php b/lib/form/textarea.php new file mode 100644 index 0000000000..2e3450a288 --- /dev/null +++ b/lib/form/textarea.php @@ -0,0 +1,45 @@ +_helpbutton=call_user_func_array('helpbutton', $helpbuttonargs); + } + /** + * get html for help button + * + * @access public + * @return string html for help button + */ + function getHelpButton(){ + return $this->_helpbutton; + } +} +?> \ No newline at end of file diff --git a/lib/formslib.php b/lib/formslib.php new file mode 100644 index 0000000000..bc1e108fab --- /dev/null +++ b/lib/formslib.php @@ -0,0 +1,279 @@ +libdir.'/pear' )){ + ini_set('include_path', $CFG->libdir.'/pear' . PATH_SEPARATOR . ini_get('include_path')); +} +require_once 'HTML/QuickForm.php'; +require_once 'HTML/QuickForm/DHTMLRulesTableless.php'; +require_once 'HTML/QuickForm/Renderer/Tableless.php'; + +class moodleform extends HTML_QuickForm_DHTMLRulesTableless{ + /** + * Class constructor - same parameters as HTML_QuickForm_DHTMLRulesTableless + * @param string $formName Form's name. + * @param string $method (optional)Form's method defaults to 'POST' + * @param string $action (optional)Form's action + * @param string $target (optional)Form's target defaults to none + * @param mixed $attributes (optional)Extra attributes for
tag + * @param bool $trackSubmit (optional)Whether to track if the form was submitted by adding a special hidden field + * @access public + */ + function moodleform($formName='', $method='post', $action='', $target='', $attributes=null){ + global $CFG; + //we need to override the constructor, we don't call the parent constructor + //at all because it strips slashes depending on the magic quotes setting + //whereas Moodle already has added slashes whether magic quotes is on or not. + + //also added check for sesskey and added sesskey to all forms + //and told class to ask Moodle for the max upload file size + HTML_Common::HTML_Common($attributes); + $method = (strtoupper($method) == 'GET') ? 'get' : 'post'; + $action = ($action == '') ? $_SERVER['PHP_SELF'] : $action; + $target = empty($target) ? array() : array('target' => $target); + //no 'name' atttribute for form in xhtml strict : + $attributes = array('action'=>$action, 'method'=>$method, 'id'=>$formName) + $target; + $this->updateAttributes($attributes); + //check for sesskey for this form + //if it is not present then we don't accept any input + if (isset($_REQUEST['_qf__' . $formName])) { + $this->_submitValues = $this->_recursiveFilter('stripslashes', 'get' == $method? $_GET: $_POST); + foreach ($_FILES as $keyFirst => $valFirst) { + foreach ($valFirst as $keySecond => $valSecond) { + if ('name' == $keySecond) { + $this->_submitFiles[$keyFirst][$keySecond] = $this->_recursiveFilter('stripslashes', $valSecond); + } else { + $this->_submitFiles[$keyFirst][$keySecond] = $valSecond; + } + } + } + + $this->_flagSubmitted = count($this->_submitValues) > 0 || count($this->_submitFiles) > 0; + } + + //check sesskey + if ($this->_flagSubmitted){ + confirm_sesskey($this->_submitValues['_qf__' . $formName]); + } + unset($this->_submitValues['_qf__' . $formName]); + //add sesskey to all forms + $this->addElement('hidden', '_qf__' . $formName, sesskey()); + + if (preg_match('/^([0-9]+)([a-zA-Z]*)$/', get_max_upload_file_size(), $matches)) { + // see http://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes + switch (strtoupper($matches['2'])) { + case 'G': + $this->_maxFileSize = $matches['1'] * 1073741824; + break; + case 'M': + $this->_maxFileSize = $matches['1'] * 1048576; + break; + case 'K': + $this->_maxFileSize = $matches['1'] * 1024; + break; + default: + $this->_maxFileSize = $matches['1']; + } + } + //this is custom stuff for Moodle : + $oldclass= $this->getAttribute('class'); + if (!empty($oldclass)){ + $this->updateAttributes(array('class'=>$oldclass.' mform')); + }else { + $this->updateAttributes(array('class'=>'mform')); + } + $this->_helpImageURL="$CFG->wwwroot/lib/form/req.gif"; + $this->_reqHTML = + helpbutton('requiredelement', get_string('requiredelement', 'form'),'moodle', + true, false, '', true, ''.get_string('requiredelement', 'form').''); + $this->setRequiredNote(get_string('denotesreq', 'form', $this->getReqHTML())); + } + function getReqHTML(){ + return $this->_reqHTML; + } + /** + * Class constructor - same parameters as HTML_QuickForm_DHTMLRulesTableless + * @param array $buttons An associative array representing help button to attach to + * to the form. keys of array correspond to names of elements in form. + * + * @access public + */ + function add_help_buttons($buttons, $suppresscheck=false){ + + foreach ($this->_elements as $no => $element){ + if (array_key_exists($element->getName(), $buttons)){ + + //dynamically create a property so we don't have to modify element + //class : + $this->_elements[$no]->setHelpButton($buttons[$element->getName()]); + unset($buttons[$element->getName()]); + } + + } + if (count($buttons)&& !$suppresscheck){ + print_error('nonexistentformelements', 'form', '', join(', ', array_keys($buttons))); + } + } + /** + * Applies a data filter for the given field(s) + * We can use any PARAM_ + * + * @param mixed $element Form element name or array of such names + * @param mixed $filter Callback, either function name or array(&$object, 'method') or PARAM_ integer + * @since 2.0 + * @access public + */ + function applyFilter($element, $filter){ + if (is_numeric($filter)){ + $filter=create_function('$value', "clean_param(\$value, $filter);"); + } + parent::applyFilter($element, $filter); + } + function exportValue($element, $addslashes=true){ + $unfiltered=parent::exportValue($element); + if ($addslashes){ + return HTML_QuickForm::_recursiveFilter('addslashes',$unfiltered); + } else { + return $unfiltered; + } + } + function exportValues($elementList, $addslashes=true){ + $unfiltered=parent::exportValues($elementList); + if ($addslashes){ + return HTML_QuickForm::_recursiveFilter('addslashes',$unfiltered); + } else { + return $unfiltered; + } + } +} + +/** + * A renderer for moodleform that only uses XHTML and CSS and no + * table tags, extends PEAR class HTML_QuickForm_Renderer_Tableless + * + * Stylesheet is part of standard theme and should be automatically included. + * + * @author Jamie Pratt + * @license gpl license + */ +class moodleform_renderer extends HTML_QuickForm_Renderer_Tableless{ + + /** + * Element template array + * @var array + * @access private + */ + var $_elementTemplates; + var $_htmleditors=array(); + function moodleform_renderer(){ + $this->_elementTemplates=array('default'=>"\n\t\t
error\">{error}
{element}

", + 'wide'=>"\n\t\t

error\">{error}
{element}

"); + + parent::HTML_QuickForm_Renderer_Tableless(); + } + function startForm(&$form){ + $this->_reqHTML=$form->getReqHTML(); + $this->_elementTemplates=str_replace('{req}', $this->_reqHTML, $this->_elementTemplates); + parent::startForm($form); + } + function startGroup(&$group, $required, $error){ + if (method_exists($group, 'getElementTemplateType')){ + $html = $this->_elementTemplates[$element->getElementTemplateType()]; + }else{ + $html = $this->_elementTemplates['default']; + + } + if (method_exists($group, 'getHelpButton')){ + $html =str_replace('{help}', $group->getHelpButton(), $html); + }else{ + $html =str_replace('{help}', '', $html); + + } + $this->_templates[$group->getName()]=$html; + // Fix for bug in tableless quickforms that didn't allow you to stop a + // fieldset before a group of elements. + // if the element name indicates the end of a fieldset, close the fieldset + if ( in_array($group->getName(), $this->_stopFieldsetElements) + && $this->_fieldsetsOpen > 0 + ) { + $this->_html .= $this->_closeFieldsetTemplate; + $this->_fieldsetsOpen--; + } + parent::startGroup($group, $required, $error); + } + function renderElement(&$element, $required, $error){ + if (method_exists($element, 'getElementTemplateType')){ + $html = $this->_elementTemplates[$element->getElementTemplateType()]; + }else{ + $html = $this->_elementTemplates['default']; + + } + if (method_exists($element, 'getHelpButton')){ + $html=str_replace('{help}', $element->getHelpButton(), $html); + }else{ + $html=str_replace('{help}', '', $html); + + } + $this->_templates[$element->getName()]=$html; + + parent::renderElement($element, $required, $error); + } + + +} + +class moodleform_filter{ + var $paramtype; + var $default; + function moodleform_filter($paramtype, $default){ + $this->paramtype=$paramtype; + $this->default=$default; + } + function required_param($value){ + if (isset($value)) { + $param = $value; + } else { + error('A required parameter was missing'); + } + + return $this->clean_param($param); + } + function optional_param($value){ + if (!isset($value)) { + return $this->default; + } + return $this->clean_param($value); + } + function clean_param($value){ + //clean param expects vars with slashes + $value=HTML_QuickForm::_recursiveFilter('addslashes', $value); + $value=clean_param($value, $this->paramtype); + return HTML_QuickForm::_recursiveFilter('stripslashes', $value); + } +} + +$GLOBALS['_HTML_QuickForm_default_renderer']=& new moodleform_renderer(); + +moodleform::registerElementType('checkbox', "$CFG->libdir/form/checkbox.php", 'moodleform_checkbox'); +moodleform::registerElementType('file', "$CFG->libdir/form/file.php", 'moodleform_file'); +moodleform::registerElementType('group', "$CFG->libdir/form/group.php", 'moodleform_group'); +moodleform::registerElementType('password', "$CFG->libdir/form/password.php", 'moodleform_password'); +moodleform::registerElementType('radio', "$CFG->libdir/form/radio.php", 'moodleform_radio'); +moodleform::registerElementType('select', "$CFG->libdir/form/select.php", 'moodleform_select'); +moodleform::registerElementType('text', "$CFG->libdir/form/text.php", 'moodleform_text'); +moodleform::registerElementType('textarea', "$CFG->libdir/form/textarea.php", 'moodleform_textarea'); +moodleform::registerElementType('date_selector', "$CFG->libdir/form/dateselector.php", 'moodleform_date_selector'); +moodleform::registerElementType('htmleditor', "$CFG->libdir/form/htmleditor.php", 'moodleform_htmleditor'); + +if ($CFG->debug >= DEBUG_ALL){ + PEAR::setErrorHandling(PEAR_ERROR_PRINT); +} +?> \ No newline at end of file diff --git a/lib/pear/HTML/Common.php b/lib/pear/HTML/Common.php new file mode 100644 index 0000000000..2c687dd4af --- /dev/null +++ b/lib/pear/HTML/Common.php @@ -0,0 +1,428 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id$ + +/** + * Base class for all HTML classes + * + * @author Adam Daniel + * @category HTML + * @package HTML_Common + * @version 1.2.2 + * @abstract + */ + +/** + * Base class for all HTML classes + * + * @author Adam Daniel + * @version 1.7 + * @since PHP 4.0.3pl1 + * @abstract + */ +class HTML_Common { + + /** + * Associative array of table attributes + * @var array + * @access private + */ + var $_attributes = array(); + + /** + * Tab offset of the table + * @var int + * @access private + */ + var $_tabOffset = 0; + + /** + * Tab string + * @var string + * @since 1.7 + * @access private + */ + var $_tab = "\11"; + + /** + * Contains the line end string + * @var string + * @since 1.7 + * @access private + */ + var $_lineEnd = "\12"; + + /** + * HTML comment on the object + * @var string + * @since 1.5 + * @access private + */ + var $_comment = ''; + + /** + * Class constructor + * @param mixed $attributes Associative array of table tag attributes + * or HTML attributes name="value" pairs + * @param int $tabOffset Indent offset in tabs + * @access public + */ + function HTML_Common($attributes = null, $tabOffset = 0) + { + $this->setAttributes($attributes); + $this->setTabOffset($tabOffset); + } // end constructor + + /** + * Returns the current API version + * @access public + * @returns double + */ + function apiVersion() + { + return 1.7; + } // end func apiVersion + + /** + * Returns the lineEnd + * + * @since 1.7 + * @access private + * @return string + * @throws + */ + function _getLineEnd() + { + return $this->_lineEnd; + } // end func getLineEnd + + /** + * Returns a string containing the unit for indenting HTML + * + * @since 1.7 + * @access private + * @return string + */ + function _getTab() + { + return $this->_tab; + } // end func _getTab + + /** + * Returns a string containing the offset for the whole HTML code + * + * @return string + * @access private + */ + function _getTabs() + { + return str_repeat($this->_getTab(), $this->_tabOffset); + } // end func _getTabs + + /** + * Returns an HTML formatted attribute string + * @param array $attributes + * @return string + * @access private + */ + function _getAttrString($attributes) + { + $strAttr = ''; + + if (is_array($attributes)) { + foreach ($attributes as $key => $value) { + $strAttr .= ' ' . $key . '="' . htmlspecialchars($value) . '"'; + } + } + return $strAttr; + } // end func _getAttrString + + /** + * Returns a valid atrributes array from either a string or array + * @param mixed $attributes Either a typical HTML attribute string or an associative array + * @access private + */ + function _parseAttributes($attributes) + { + if (is_array($attributes)) { + $ret = array(); + foreach ($attributes as $key => $value) { + if (is_int($key)) { + $key = $value = strtolower($value); + } else { + $key = strtolower($key); + } + $ret[$key] = $value; + } + return $ret; + + } elseif (is_string($attributes)) { + $preg = "/(([A-Za-z_:]|[^\\x00-\\x7F])([A-Za-z0-9_:.-]|[^\\x00-\\x7F])*)" . + "([ \\n\\t\\r]+)?(=([ \\n\\t\\r]+)?(\"[^\"]*\"|'[^']*'|[^ \\n\\t\\r]*))?/"; + if (preg_match_all($preg, $attributes, $regs)) { + for ($counter=0; $counter $value) { + $attr1[$key] = $value; + } + } // end func _updateAtrrArray + + /** + * Removes the given attribute from the given array + * + * @param string $attr Attribute name + * @param array $attributes Attribute array + * @since 1.4 + * @access private + * @return void + * @throws + */ + function _removeAttr($attr, &$attributes) + { + $attr = strtolower($attr); + if (isset($attributes[$attr])) { + unset($attributes[$attr]); + } + } //end func _removeAttr + + /** + * Returns the value of the given attribute + * + * @param string $attr Attribute name + * @since 1.5 + * @access public + * @return void + * @throws + */ + function getAttribute($attr) + { + $attr = strtolower($attr); + if (isset($this->_attributes[$attr])) { + return $this->_attributes[$attr]; + } + return null; + } //end func getAttribute + + /** + * Sets the HTML attributes + * @param mixed $attributes Either a typical HTML attribute string or an associative array + * @access public + */ + function setAttributes($attributes) + { + $this->_attributes = $this->_parseAttributes($attributes); + } // end func setAttributes + + /** + * Returns the assoc array (default) or string of attributes + * + * @param bool Whether to return the attributes as string + * @since 1.6 + * @access public + * @return mixed attributes + */ + function getAttributes($asString = false) + { + if ($asString) { + return $this->_getAttrString($this->_attributes); + } else { + return $this->_attributes; + } + } //end func getAttributes + + /** + * Updates the passed attributes without changing the other existing attributes + * @param mixed $attributes Either a typical HTML attribute string or an associative array + * @access public + */ + function updateAttributes($attributes) + { + $this->_updateAttrArray($this->_attributes, $this->_parseAttributes($attributes)); + } // end func updateAttributes + + /** + * Removes an attribute + * + * @param string $attr Attribute name + * @since 1.4 + * @access public + * @return void + * @throws + */ + function removeAttribute($attr) + { + $this->_removeAttr($attr, $this->_attributes); + } //end func removeAttribute + + /** + * Sets the line end style to Windows, Mac, Unix or a custom string. + * + * @param string $style "win", "mac", "unix" or custom string. + * @since 1.7 + * @access public + * @return void + */ + function setLineEnd($style) + { + switch ($style) { + case 'win': + $this->_lineEnd = "\15\12"; + break; + case 'unix': + $this->_lineEnd = "\12"; + break; + case 'mac': + $this->_lineEnd = "\15"; + break; + default: + $this->_lineEnd = $style; + } + } // end func setLineEnd + + /** + * Sets the tab offset + * + * @param int $offset + * @access public + */ + function setTabOffset($offset) + { + $this->_tabOffset = $offset; + } // end func setTabOffset + + /** + * Returns the tabOffset + * + * @since 1.5 + * @access public + * @return int + */ + function getTabOffset() + { + return $this->_tabOffset; + } //end func getTabOffset + + /** + * Sets the string used to indent HTML + * + * @since 1.7 + * @param string $string String used to indent ("\11", "\t", ' ', etc.). + * @access public + * @return void + */ + function setTab($string) + { + $this->_tab = $string; + } // end func setTab + + /** + * Sets the HTML comment to be displayed at the beginning of the HTML string + * + * @param string + * @since 1.4 + * @access public + * @return void + */ + function setComment($comment) + { + $this->_comment = $comment; + } // end func setHtmlComment + + /** + * Returns the HTML comment + * + * @since 1.5 + * @access public + * @return string + */ + function getComment() + { + return $this->_comment; + } //end func getComment + + /** + * Abstract method. Must be extended to return the objects HTML + * + * @access public + * @return string + * @abstract + */ + function toHtml() + { + return ''; + } // end func toHtml + + /** + * Displays the HTML to the screen + * + * @access public + */ + function display() + { + print $this->toHtml(); + } // end func display + +} // end class HTML_Common +?> diff --git a/lib/pear/HTML/QuickForm.php b/lib/pear/HTML/QuickForm.php new file mode 100644 index 0000000000..b16ef0b1c7 --- /dev/null +++ b/lib/pear/HTML/QuickForm.php @@ -0,0 +1,1992 @@ + | +// | Bertrand Mansion | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once('PEAR.php'); +require_once('HTML/Common.php'); + +$GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'] = + array( + 'group' =>array('HTML/QuickForm/group.php','HTML_QuickForm_group'), + 'hidden' =>array('HTML/QuickForm/hidden.php','HTML_QuickForm_hidden'), + 'reset' =>array('HTML/QuickForm/reset.php','HTML_QuickForm_reset'), + 'checkbox' =>array('HTML/QuickForm/checkbox.php','HTML_QuickForm_checkbox'), + 'file' =>array('HTML/QuickForm/file.php','HTML_QuickForm_file'), + 'image' =>array('HTML/QuickForm/image.php','HTML_QuickForm_image'), + 'password' =>array('HTML/QuickForm/password.php','HTML_QuickForm_password'), + 'radio' =>array('HTML/QuickForm/radio.php','HTML_QuickForm_radio'), + 'button' =>array('HTML/QuickForm/button.php','HTML_QuickForm_button'), + 'submit' =>array('HTML/QuickForm/submit.php','HTML_QuickForm_submit'), + 'select' =>array('HTML/QuickForm/select.php','HTML_QuickForm_select'), + 'hiddenselect' =>array('HTML/QuickForm/hiddenselect.php','HTML_QuickForm_hiddenselect'), + 'text' =>array('HTML/QuickForm/text.php','HTML_QuickForm_text'), + 'textarea' =>array('HTML/QuickForm/textarea.php','HTML_QuickForm_textarea'), + 'link' =>array('HTML/QuickForm/link.php','HTML_QuickForm_link'), + 'advcheckbox' =>array('HTML/QuickForm/advcheckbox.php','HTML_QuickForm_advcheckbox'), + 'date' =>array('HTML/QuickForm/date.php','HTML_QuickForm_date'), + 'static' =>array('HTML/QuickForm/static.php','HTML_QuickForm_static'), + 'header' =>array('HTML/QuickForm/header.php', 'HTML_QuickForm_header'), + 'html' =>array('HTML/QuickForm/html.php', 'HTML_QuickForm_html'), + 'hierselect' =>array('HTML/QuickForm/hierselect.php', 'HTML_QuickForm_hierselect'), + 'autocomplete' =>array('HTML/QuickForm/autocomplete.php', 'HTML_QuickForm_autocomplete'), + 'xbutton' =>array('HTML/QuickForm/xbutton.php','HTML_QuickForm_xbutton') + ); + +$GLOBALS['_HTML_QuickForm_registered_rules'] = array( + 'required' => array('html_quickform_rule_required', 'HTML/QuickForm/Rule/Required.php'), + 'maxlength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'), + 'minlength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'), + 'rangelength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'), + 'email' => array('html_quickform_rule_email', 'HTML/QuickForm/Rule/Email.php'), + 'regex' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'), + 'lettersonly' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'), + 'alphanumeric' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'), + 'numeric' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'), + 'nopunctuation' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'), + 'nonzero' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'), + 'callback' => array('html_quickform_rule_callback', 'HTML/QuickForm/Rule/Callback.php'), + 'compare' => array('html_quickform_rule_compare', 'HTML/QuickForm/Rule/Compare.php') +); + +// {{{ error codes + +/* + * Error codes for the QuickForm interface, which will be mapped to textual messages + * in the QuickForm::errorMessage() function. If you are to add a new error code, be + * sure to add the textual messages to the QuickForm::errorMessage() function as well + */ + +define('QUICKFORM_OK', 1); +define('QUICKFORM_ERROR', -1); +define('QUICKFORM_INVALID_RULE', -2); +define('QUICKFORM_NONEXIST_ELEMENT', -3); +define('QUICKFORM_INVALID_FILTER', -4); +define('QUICKFORM_UNREGISTERED_ELEMENT', -5); +define('QUICKFORM_INVALID_ELEMENT_NAME', -6); +define('QUICKFORM_INVALID_PROCESS', -7); +define('QUICKFORM_DEPRECATED', -8); +define('QUICKFORM_INVALID_DATASOURCE', -9); + +// }}} + +/** +* Create, validate and process HTML forms +* +* @author Adam Daniel +* @author Bertrand Mansion +* @version 2.0 +* @since PHP 4.0.3pl1 +*/ +class HTML_QuickForm extends HTML_Common { + // {{{ properties + + /** + * Array containing the form fields + * @since 1.0 + * @var array + * @access private + */ + var $_elements = array(); + + /** + * Array containing element name to index map + * @since 1.1 + * @var array + * @access private + */ + var $_elementIndex = array(); + + /** + * Array containing indexes of duplicate elements + * @since 2.10 + * @var array + * @access private + */ + var $_duplicateIndex = array(); + + /** + * Array containing required field IDs + * @since 1.0 + * @var array + * @access private + */ + var $_required = array(); + + /** + * Prefix message in javascript alert if error + * @since 1.0 + * @var string + * @access public + */ + var $_jsPrefix = 'Invalid information entered.'; + + /** + * Postfix message in javascript alert if error + * @since 1.0 + * @var string + * @access public + */ + var $_jsPostfix = 'Please correct these fields.'; + + /** + * Datasource object implementing the informal + * datasource protocol + * @since 3.3 + * @var object + * @access private + */ + var $_datasource; + + /** + * Array of default form values + * @since 2.0 + * @var array + * @access private + */ + var $_defaultValues = array(); + + /** + * Array of constant form values + * @since 2.0 + * @var array + * @access private + */ + var $_constantValues = array(); + + /** + * Array of submitted form values + * @since 1.0 + * @var array + * @access private + */ + var $_submitValues = array(); + + /** + * Array of submitted form files + * @since 1.0 + * @var integer + * @access public + */ + var $_submitFiles = array(); + + /** + * Value for maxfilesize hidden element if form contains file input + * @since 1.0 + * @var integer + * @access public + */ + var $_maxFileSize = 1048576; // 1 Mb = 1048576 + + /** + * Flag to know if all fields are frozen + * @since 1.0 + * @var boolean + * @access private + */ + var $_freezeAll = false; + + /** + * Array containing the form rules + * @since 1.0 + * @var array + * @access private + */ + var $_rules = array(); + + /** + * Form rules, global variety + * @var array + * @access private + */ + var $_formRules = array(); + + /** + * Array containing the validation errors + * @since 1.0 + * @var array + * @access private + */ + var $_errors = array(); + + /** + * Note for required fields in the form + * @var string + * @since 1.0 + * @access private + */ + var $_requiredNote = '* denotes required field'; + + /** + * Whether the form was submitted + * @var boolean + * @access private + */ + var $_flagSubmitted = false; + + // }}} + // {{{ constructor + + /** + * Class constructor + * @param string $formName Form's name. + * @param string $method (optional)Form's method defaults to 'POST' + * @param string $action (optional)Form's action + * @param string $target (optional)Form's target defaults to '_self' + * @param mixed $attributes (optional)Extra attributes for tag + * @param bool $trackSubmit (optional)Whether to track if the form was submitted by adding a special hidden field + * @access public + */ + function HTML_QuickForm($formName='', $method='post', $action='', $target='', $attributes=null, $trackSubmit = false) + { + HTML_Common::HTML_Common($attributes); + $method = (strtoupper($method) == 'GET') ? 'get' : 'post'; + $action = ($action == '') ? $_SERVER['PHP_SELF'] : $action; + $target = empty($target) ? array() : array('target' => $target); + $attributes = array('action'=>$action, 'method'=>$method, 'name'=>$formName, 'id'=>$formName) + $target; + $this->updateAttributes($attributes); + if (!$trackSubmit || isset($_REQUEST['_qf__' . $formName])) { + if (1 == get_magic_quotes_gpc()) { + $this->_submitValues = $this->_recursiveFilter('stripslashes', 'get' == $method? $_GET: $_POST); + foreach ($_FILES as $keyFirst => $valFirst) { + foreach ($valFirst as $keySecond => $valSecond) { + if ('name' == $keySecond) { + $this->_submitFiles[$keyFirst][$keySecond] = $this->_recursiveFilter('stripslashes', $valSecond); + } else { + $this->_submitFiles[$keyFirst][$keySecond] = $valSecond; + } + } + } + } else { + $this->_submitValues = 'get' == $method? $_GET: $_POST; + $this->_submitFiles = $_FILES; + } + $this->_flagSubmitted = count($this->_submitValues) > 0 || count($this->_submitFiles) > 0; + } + if ($trackSubmit) { + unset($this->_submitValues['_qf__' . $formName]); + $this->addElement('hidden', '_qf__' . $formName, null); + } + if (preg_match('/^([0-9]+)([a-zA-Z]*)$/', ini_get('upload_max_filesize'), $matches)) { + // see http://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes + switch (strtoupper($matches['2'])) { + case 'G': + $this->_maxFileSize = $matches['1'] * 1073741824; + break; + case 'M': + $this->_maxFileSize = $matches['1'] * 1048576; + break; + case 'K': + $this->_maxFileSize = $matches['1'] * 1024; + break; + default: + $this->_maxFileSize = $matches['1']; + } + } + } // end constructor + + // }}} + // {{{ apiVersion() + + /** + * Returns the current API version + * + * @since 1.0 + * @access public + * @return float + */ + function apiVersion() + { + return 3.2; + } // end func apiVersion + + // }}} + // {{{ registerElementType() + + /** + * Registers a new element type + * + * @param string $typeName Name of element type + * @param string $include Include path for element type + * @param string $className Element class name + * @since 1.0 + * @access public + * @return void + */ + function registerElementType($typeName, $include, $className) + { + $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($typeName)] = array($include, $className); + } // end func registerElementType + + // }}} + // {{{ registerRule() + + /** + * Registers a new validation rule + * + * @param string $ruleName Name of validation rule + * @param string $type Either: 'regex', 'function' or 'rule' for an HTML_QuickForm_Rule object + * @param string $data1 Name of function, regular expression or HTML_QuickForm_Rule classname + * @param string $data2 Object parent of above function or HTML_QuickForm_Rule file path + * @since 1.0 + * @access public + * @return void + */ + function registerRule($ruleName, $type, $data1, $data2 = null) + { + include_once('HTML/QuickForm/RuleRegistry.php'); + $registry =& HTML_QuickForm_RuleRegistry::singleton(); + $registry->registerRule($ruleName, $type, $data1, $data2); + } // end func registerRule + + // }}} + // {{{ elementExists() + + /** + * Returns true if element is in the form + * + * @param string $element form name of element to check + * @since 1.0 + * @access public + * @return boolean + */ + function elementExists($element=null) + { + return isset($this->_elementIndex[$element]); + } // end func elementExists + + // }}} + // {{{ setDatasource() + + /** + * Sets a datasource object for this form object + * + * Datasource default and constant values will feed the QuickForm object if + * the datasource implements defaultValues() and constantValues() methods. + * + * @param object $datasource datasource object implementing the informal datasource protocol + * @param mixed $defaultsFilter string or array of filter(s) to apply to default values + * @param mixed $constantsFilter string or array of filter(s) to apply to constants values + * @since 3.3 + * @access public + * @return void + */ + function setDatasource(&$datasource, $defaultsFilter = null, $constantsFilter = null) + { + if (is_object($datasource)) { + $this->_datasource =& $datasource; + if (is_callable(array($datasource, 'defaultValues'))) { + $this->setDefaults($datasource->defaultValues($this), $defaultsFilter); + } + if (is_callable(array($datasource, 'constantValues'))) { + $this->setConstants($datasource->constantValues($this), $constantsFilter); + } + } else { + return PEAR::raiseError(null, QUICKFORM_INVALID_DATASOURCE, null, E_USER_WARNING, "Datasource is not an object in QuickForm::setDatasource()", 'HTML_QuickForm_Error', true); + } + } // end func setDatasource + + // }}} + // {{{ setDefaults() + + /** + * Initializes default form values + * + * @param array $defaultValues values used to fill the form + * @param mixed $filter (optional) filter(s) to apply to all default values + * @since 1.0 + * @access public + * @return void + */ + function setDefaults($defaultValues = null, $filter = null) + { + if (is_array($defaultValues)) { + if (isset($filter)) { + if (is_array($filter) && (2 != count($filter) || !is_callable($filter))) { + foreach ($filter as $val) { + if (!is_callable($val)) { + return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true); + } else { + $defaultValues = $this->_recursiveFilter($val, $defaultValues); + } + } + } elseif (!is_callable($filter)) { + return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true); + } else { + $defaultValues = $this->_recursiveFilter($filter, $defaultValues); + } + } + $this->_defaultValues = HTML_QuickForm::arrayMerge($this->_defaultValues, $defaultValues); + foreach (array_keys($this->_elements) as $key) { + $this->_elements[$key]->onQuickFormEvent('updateValue', null, $this); + } + } + } // end func setDefaults + + // }}} + // {{{ setConstants() + + /** + * Initializes constant form values. + * These values won't get overridden by POST or GET vars + * + * @param array $constantValues values used to fill the form + * @param mixed $filter (optional) filter(s) to apply to all default values + * + * @since 2.0 + * @access public + * @return void + */ + function setConstants($constantValues = null, $filter = null) + { + if (is_array($constantValues)) { + if (isset($filter)) { + if (is_array($filter) && (2 != count($filter) || !is_callable($filter))) { + foreach ($filter as $val) { + if (!is_callable($val)) { + return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true); + } else { + $constantValues = $this->_recursiveFilter($val, $constantValues); + } + } + } elseif (!is_callable($filter)) { + return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true); + } else { + $constantValues = $this->_recursiveFilter($filter, $constantValues); + } + } + $this->_constantValues = HTML_QuickForm::arrayMerge($this->_constantValues, $constantValues); + foreach (array_keys($this->_elements) as $key) { + $this->_elements[$key]->onQuickFormEvent('updateValue', null, $this); + } + } + } // end func setConstants + + // }}} + // {{{ setMaxFileSize() + + /** + * Sets the value of MAX_FILE_SIZE hidden element + * + * @param int $bytes Size in bytes + * @since 3.0 + * @access public + * @return void + */ + function setMaxFileSize($bytes = 0) + { + if ($bytes > 0) { + $this->_maxFileSize = $bytes; + } + if (!$this->elementExists('MAX_FILE_SIZE')) { + $this->addElement('hidden', 'MAX_FILE_SIZE', $this->_maxFileSize); + } else { + $el =& $this->getElement('MAX_FILE_SIZE'); + $el->updateAttributes(array('value' => $this->_maxFileSize)); + } + } // end func setMaxFileSize + + // }}} + // {{{ getMaxFileSize() + + /** + * Returns the value of MAX_FILE_SIZE hidden element + * + * @since 3.0 + * @access public + * @return int max file size in bytes + */ + function getMaxFileSize() + { + return $this->_maxFileSize; + } // end func getMaxFileSize + + // }}} + // {{{ &createElement() + + /** + * Creates a new form element of the given type. + * + * This method accepts variable number of parameters, their + * meaning and count depending on $elementType + * + * @param string $elementType type of element to add (text, textarea, file...) + * @since 1.0 + * @access public + * @return object extended class of HTML_element + * @throws HTML_QuickForm_Error + */ + function &createElement($elementType) + { + $args = func_get_args(); + $element =& HTML_QuickForm::_loadElement('createElement', $elementType, array_slice($args, 1)); + return $element; + } // end func createElement + + // }}} + // {{{ _loadElement() + + /** + * Returns a form element of the given type + * + * @param string $event event to send to newly created element ('createElement' or 'addElement') + * @param string $type element type + * @param array $args arguments for event + * @since 2.0 + * @access private + * @return object a new element + * @throws HTML_QuickForm_Error + */ + function &_loadElement($event, $type, $args) + { + $type = strtolower($type); + if (!HTML_QuickForm::isTypeRegistered($type)) { + $error = PEAR::raiseError(null, QUICKFORM_UNREGISTERED_ELEMENT, null, E_USER_WARNING, "Element '$type' does not exist in HTML_QuickForm::_loadElement()", 'HTML_QuickForm_Error', true); + return $error; + } + $className = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][1]; + $includeFile = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][0]; + include_once($includeFile); + $elementObject =& new $className(); + for ($i = 0; $i < 5; $i++) { + if (!isset($args[$i])) { + $args[$i] = null; + } + } + $err = $elementObject->onQuickFormEvent($event, $args, $this); + if ($err !== true) { + return $err; + } + return $elementObject; + } // end func _loadElement + + // }}} + // {{{ addElement() + + /** + * Adds an element into the form + * + * If $element is a string representing element type, then this + * method accepts variable number of parameters, their meaning + * and count depending on $element + * + * @param mixed $element element object or type of element to add (text, textarea, file...) + * @since 1.0 + * @return object reference to element + * @access public + * @throws HTML_QuickForm_Error + */ + function &addElement($element) + { + if (is_object($element) && is_subclass_of($element, 'html_quickform_element')) { + $elementObject = &$element; + $elementObject->onQuickFormEvent('updateValue', null, $this); + } else { + $args = func_get_args(); + $elementObject =& $this->_loadElement('addElement', $element, array_slice($args, 1)); + if (PEAR::isError($elementObject)) { + return $elementObject; + } + } + $elementName = $elementObject->getName(); + + // Add the element if it is not an incompatible duplicate + if (!empty($elementName) && isset($this->_elementIndex[$elementName])) { + if ($this->_elements[$this->_elementIndex[$elementName]]->getType() == + $elementObject->getType()) { + $this->_elements[] =& $elementObject; + $elKeys = array_keys($this->_elements); + $this->_duplicateIndex[$elementName][] = end($elKeys); + } else { + $error = PEAR::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, "Element '$elementName' already exists in HTML_QuickForm::addElement()", 'HTML_QuickForm_Error', true); + return $error; + } + } else { + $this->_elements[] =& $elementObject; + $elKeys = array_keys($this->_elements); + $this->_elementIndex[$elementName] = end($elKeys); + } + if ($this->_freezeAll) { + $elementObject->freeze(); + } + + return $elementObject; + } // end func addElement + + // }}} + // {{{ insertElementBefore() + + /** + * Inserts a new element right before the other element + * + * Warning: it is not possible to check whether the $element is already + * added to the form, therefore if you want to move the existing form + * element to a new position, you'll have to use removeElement(): + * $form->insertElementBefore($form->removeElement('foo', false), 'bar'); + * + * @access public + * @since 3.2.4 + * @param object HTML_QuickForm_element Element to insert + * @param string Name of the element before which the new one is inserted + * @return object HTML_QuickForm_element reference to inserted element + * @throws HTML_QuickForm_Error + */ + function &insertElementBefore(&$element, $nameAfter) + { + if (!empty($this->_duplicateIndex[$nameAfter])) { + $error = PEAR::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, 'Several elements named "' . $nameAfter . '" exist in HTML_QuickForm::insertElementBefore().', 'HTML_QuickForm_Error', true); + return $error; + } elseif (!$this->elementExists($nameAfter)) { + $error = PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$nameAfter' does not exist in HTML_QuickForm::insertElementBefore()", 'HTML_QuickForm_Error', true); + return $error; + } + $elementName = $element->getName(); + $targetIdx = $this->_elementIndex[$nameAfter]; + $duplicate = false; + // Like in addElement(), check that it's not an incompatible duplicate + if (!empty($elementName) && isset($this->_elementIndex[$elementName])) { + if ($this->_elements[$this->_elementIndex[$elementName]]->getType() != $element->getType()) { + $error = PEAR::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, "Element '$elementName' already exists in HTML_QuickForm::insertElementBefore()", 'HTML_QuickForm_Error', true); + return $error; + } + $duplicate = true; + } + // Move all the elements after added back one place, reindex _elementIndex and/or _duplicateIndex + $elKeys = array_keys($this->_elements); + for ($i = end($elKeys); $i >= $targetIdx; $i--) { + if (isset($this->_elements[$i])) { + $currentName = $this->_elements[$i]->getName(); + $this->_elements[$i + 1] =& $this->_elements[$i]; + if ($this->_elementIndex[$currentName] == $i) { + $this->_elementIndex[$currentName] = $i + 1; + } else { + $dupIdx = array_search($i, $this->_duplicateIndex[$currentName]); + $this->_duplicateIndex[$currentName][$dupIdx] = $i + 1; + } + unset($this->_elements[$i]); + } + } + // Put the element in place finally + $this->_elements[$targetIdx] =& $element; + if (!$duplicate) { + $this->_elementIndex[$elementName] = $targetIdx; + } else { + $this->_duplicateIndex[$elementName][] = $targetIdx; + } + $element->onQuickFormEvent('updateValue', null, $this); + if ($this->_freezeAll) { + $element->freeze(); + } + // If not done, the elements will appear in reverse order + ksort($this->_elements); + return $element; + } + + // }}} + // {{{ addGroup() + + /** + * Adds an element group + * @param array $elements array of elements composing the group + * @param string $name (optional)group name + * @param string $groupLabel (optional)group label + * @param string $separator (optional)string to separate elements + * @param string $appendName (optional)specify whether the group name should be + * used in the form element name ex: group[element] + * @return object reference to added group of elements + * @since 2.8 + * @access public + * @throws PEAR_Error + */ + function &addGroup($elements, $name=null, $groupLabel='', $separator=null, $appendName = true) + { + static $anonGroups = 1; + + if (0 == strlen($name)) { + $name = 'qf_group_' . $anonGroups++; + $appendName = false; + } + $group =& $this->addElement('group', $name, $groupLabel, $elements, $separator, $appendName); + return $group; + } // end func addGroup + + // }}} + // {{{ &getElement() + + /** + * Returns a reference to the element + * + * @param string $element Element name + * @since 2.0 + * @access public + * @return object reference to element + * @throws HTML_QuickForm_Error + */ + function &getElement($element) + { + if (isset($this->_elementIndex[$element])) { + return $this->_elements[$this->_elementIndex[$element]]; + } else { + $error = PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElement()", 'HTML_QuickForm_Error', true); + return $error; + } + } // end func getElement + + // }}} + // {{{ &getElementValue() + + /** + * Returns the element's raw value + * + * This returns the value as submitted by the form (not filtered) + * or set via setDefaults() or setConstants() + * + * @param string $element Element name + * @since 2.0 + * @access public + * @return mixed element value + * @throws HTML_QuickForm_Error + */ + function &getElementValue($element) + { + if (!isset($this->_elementIndex[$element])) { + $error = PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true); + return $error; + } + $value = $this->_elements[$this->_elementIndex[$element]]->getValue(); + if (isset($this->_duplicateIndex[$element])) { + foreach ($this->_duplicateIndex[$element] as $index) { + if (null !== ($v = $this->_elements[$index]->getValue())) { + if (is_array($value)) { + $value[] = $v; + } else { + $value = (null === $value)? $v: array($value, $v); + } + } + } + } + return $value; + } // end func getElementValue + + // }}} + // {{{ getSubmitValue() + + /** + * Returns the elements value after submit and filter + * + * @param string Element name + * @since 2.0 + * @access public + * @return mixed submitted element value or null if not set + */ + function getSubmitValue($elementName) + { + $value = null; + if (isset($this->_submitValues[$elementName]) || isset($this->_submitFiles[$elementName])) { + $value = isset($this->_submitValues[$elementName])? $this->_submitValues[$elementName]: array(); + if (is_array($value) && isset($this->_submitFiles[$elementName])) { + foreach ($this->_submitFiles[$elementName] as $k => $v) { + $value = HTML_QuickForm::arrayMerge($value, $this->_reindexFiles($this->_submitFiles[$elementName][$k], $k)); + } + } + + } elseif ('file' == $this->getElementType($elementName)) { + return $this->getElementValue($elementName); + + } elseif (false !== ($pos = strpos($elementName, '['))) { + $base = substr($elementName, 0, $pos); + $idx = "['" . str_replace(array(']', '['), array('', "']['"), substr($elementName, $pos + 1, -1)) . "']"; + if (isset($this->_submitValues[$base])) { + $value = eval("return (isset(\$this->_submitValues['{$base}']{$idx})) ? \$this->_submitValues['{$base}']{$idx} : null;"); + } + + if ((is_array($value) || null === $value) && isset($this->_submitFiles[$base])) { + $props = array('name', 'type', 'size', 'tmp_name', 'error'); + $code = "if (!isset(\$this->_submitFiles['{$base}']['name']{$idx})) {\n" . + " return null;\n" . + "} else {\n" . + " \$v = array();\n"; + foreach ($props as $prop) { + $code .= " \$v = HTML_QuickForm::arrayMerge(\$v, \$this->_reindexFiles(\$this->_submitFiles['{$base}']['{$prop}']{$idx}, '{$prop}'));\n"; + } + $fileValue = eval($code . " return \$v;\n}\n"); + if (null !== $fileValue) { + $value = null === $value? $fileValue: HTML_QuickForm::arrayMerge($value, $fileValue); + } + } + } + + // This is only supposed to work for groups with appendName = false + if (null === $value && 'group' == $this->getElementType($elementName)) { + $group =& $this->getElement($elementName); + $elements =& $group->getElements(); + foreach (array_keys($elements) as $key) { + $name = $group->getElementName($key); + // prevent endless recursion in case of radios and such + if ($name != $elementName) { + if (null !== ($v = $this->getSubmitValue($name))) { + $value[$name] = $v; + } + } + } + } + return $value; + } // end func getSubmitValue + + // }}} + // {{{ _reindexFiles() + + /** + * A helper function to change the indexes in $_FILES array + * + * @param mixed Some value from the $_FILES array + * @param string The key from the $_FILES array that should be appended + * @return array + */ + function _reindexFiles($value, $key) + { + if (!is_array($value)) { + return array($key => $value); + } else { + $ret = array(); + foreach ($value as $k => $v) { + $ret[$k] = $this->_reindexFiles($v, $key); + } + return $ret; + } + } + + // }}} + // {{{ getElementError() + + /** + * Returns error corresponding to validated element + * + * @param string $element Name of form element to check + * @since 1.0 + * @access public + * @return string error message corresponding to checked element + */ + function getElementError($element) + { + if (isset($this->_errors[$element])) { + return $this->_errors[$element]; + } + } // end func getElementError + + // }}} + // {{{ setElementError() + + /** + * Set error message for a form element + * + * @param string $element Name of form element to set error for + * @param string $message Error message, if empty then removes the current error message + * @since 1.0 + * @access public + * @return void + */ + function setElementError($element, $message = null) + { + if (!empty($message)) { + $this->_errors[$element] = $message; + } else { + unset($this->_errors[$element]); + } + } // end func setElementError + + // }}} + // {{{ getElementType() + + /** + * Returns the type of the given element + * + * @param string $element Name of form element + * @since 1.1 + * @access public + * @return string Type of the element, false if the element is not found + */ + function getElementType($element) + { + if (isset($this->_elementIndex[$element])) { + return $this->_elements[$this->_elementIndex[$element]]->getType(); + } + return false; + } // end func getElementType + + // }}} + // {{{ updateElementAttr() + + /** + * Updates Attributes for one or more elements + * + * @param mixed $elements Array of element names/objects or string of elements to be updated + * @param mixed $attrs Array or sting of html attributes + * @since 2.10 + * @access public + * @return void + */ + function updateElementAttr($elements, $attrs) + { + if (is_string($elements)) { + $elements = split('[ ]?,[ ]?', $elements); + } + foreach (array_keys($elements) as $key) { + if (is_object($elements[$key]) && is_a($elements[$key], 'HTML_QuickForm_element')) { + $elements[$key]->updateAttributes($attrs); + } elseif (isset($this->_elementIndex[$elements[$key]])) { + $this->_elements[$this->_elementIndex[$elements[$key]]]->updateAttributes($attrs); + if (isset($this->_duplicateIndex[$elements[$key]])) { + foreach ($this->_duplicateIndex[$elements[$key]] as $index) { + $this->_elements[$index]->updateAttributes($attrs); + } + } + } + } + } // end func updateElementAttr + + // }}} + // {{{ removeElement() + + /** + * Removes an element + * + * The method "unlinks" an element from the form, returning the reference + * to the element object. If several elements named $elementName exist, + * it removes the first one, leaving the others intact. + * + * @param string $elementName The element name + * @param boolean $removeRules True if rules for this element are to be removed too + * @access public + * @since 2.0 + * @return object HTML_QuickForm_element a reference to the removed element + * @throws HTML_QuickForm_Error + */ + function &removeElement($elementName, $removeRules = true) + { + if (!isset($this->_elementIndex[$elementName])) { + $error = PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$elementName' does not exist in HTML_QuickForm::removeElement()", 'HTML_QuickForm_Error', true); + return $error; + } + $el =& $this->_elements[$this->_elementIndex[$elementName]]; + unset($this->_elements[$this->_elementIndex[$elementName]]); + if (empty($this->_duplicateIndex[$elementName])) { + unset($this->_elementIndex[$elementName]); + } else { + $this->_elementIndex[$elementName] = array_shift($this->_duplicateIndex[$elementName]); + } + if ($removeRules) { + unset($this->_rules[$elementName], $this->_errors[$elementName]); + } + return $el; + } // end func removeElement + + // }}} + // {{{ addRule() + + /** + * Adds a validation rule for the given field + * + * If the element is in fact a group, it will be considered as a whole. + * To validate grouped elements as separated entities, + * use addGroupRule instead of addRule. + * + * @param string $element Form element name + * @param string $message Message to display for invalid data + * @param string $type Rule type, use getRegisteredRules() to get types + * @param string $format (optional)Required for extra rule data + * @param string $validation (optional)Where to perform validation: "server", "client" + * @param boolean $reset Client-side validation: reset the form element to its original value if there is an error? + * @param boolean $force Force the rule to be applied, even if the target form element does not exist + * @since 1.0 + * @access public + * @throws HTML_QuickForm_Error + */ + function addRule($element, $message, $type, $format=null, $validation='server', $reset = false, $force = false) + { + if (!$force) { + if (!is_array($element) && !$this->elementExists($element)) { + return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true); + } elseif (is_array($element)) { + foreach ($element as $el) { + if (!$this->elementExists($el)) { + return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$el' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true); + } + } + } + } + if (false === ($newName = $this->isRuleRegistered($type, true))) { + return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true); + } elseif (is_string($newName)) { + $type = $newName; + } + if (is_array($element)) { + $dependent = $element; + $element = array_shift($dependent); + } else { + $dependent = null; + } + if ($type == 'required' || $type == 'uploadedfile') { + $this->_required[] = $element; + } + if (!isset($this->_rules[$element])) { + $this->_rules[$element] = array(); + } + if ($validation == 'client') { + $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);')); + } + $this->_rules[$element][] = array( + 'type' => $type, + 'format' => $format, + 'message' => $message, + 'validation' => $validation, + 'reset' => $reset, + 'dependent' => $dependent + ); + } // end func addRule + + // }}} + // {{{ addGroupRule() + + /** + * Adds a validation rule for the given group of elements + * + * Only groups with a name can be assigned a validation rule + * Use addGroupRule when you need to validate elements inside the group. + * Use addRule if you need to validate the group as a whole. In this case, + * the same rule will be applied to all elements in the group. + * Use addRule if you need to validate the group against a function. + * + * @param string $group Form group name + * @param mixed $arg1 Array for multiple elements or error message string for one element + * @param string $type (optional)Rule type use getRegisteredRules() to get types + * @param string $format (optional)Required for extra rule data + * @param int $howmany (optional)How many valid elements should be in the group + * @param string $validation (optional)Where to perform validation: "server", "client" + * @param bool $reset Client-side: whether to reset the element's value to its original state if validation failed. + * @since 2.5 + * @access public + * @throws HTML_QuickForm_Error + */ + function addGroupRule($group, $arg1, $type='', $format=null, $howmany=0, $validation = 'server', $reset = false) + { + if (!$this->elementExists($group)) { + return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Group '$group' does not exist in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true); + } + + $groupObj =& $this->getElement($group); + if (is_array($arg1)) { + $required = 0; + foreach ($arg1 as $elementIndex => $rules) { + $elementName = $groupObj->getElementName($elementIndex); + foreach ($rules as $rule) { + $format = (isset($rule[2])) ? $rule[2] : null; + $validation = (isset($rule[3]) && 'client' == $rule[3])? 'client': 'server'; + $reset = isset($rule[4]) && $rule[4]; + $type = $rule[1]; + if (false === ($newName = $this->isRuleRegistered($type, true))) { + return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true); + } elseif (is_string($newName)) { + $type = $newName; + } + + $this->_rules[$elementName][] = array( + 'type' => $type, + 'format' => $format, + 'message' => $rule[0], + 'validation' => $validation, + 'reset' => $reset, + 'group' => $group); + + if ('required' == $type || 'uploadedfile' == $type) { + $groupObj->_required[] = $elementName; + $this->_required[] = $elementName; + $required++; + } + if ('client' == $validation) { + $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);')); + } + } + } + if ($required > 0 && count($groupObj->getElements()) == $required) { + $this->_required[] = $group; + } + } elseif (is_string($arg1)) { + if (false === ($newName = $this->isRuleRegistered($type, true))) { + return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true); + } elseif (is_string($newName)) { + $type = $newName; + } + + // addGroupRule() should also handle 's value for anything + * (because of security implications) we implement file's value as a + * read-only property with a special meaning. + * + * @param mixed Value for file element + * @since 3.0 + * @access public + */ + function setValue($value) + { + return null; + } //end func setValue + + // }}} + // {{{ getValue() + + /** + * Returns information about the uploaded file + * + * @since 3.0 + * @access public + * @return array + */ + function getValue() + { + return $this->_value; + } // end func getValue + + // }}} + // {{{ onQuickFormEvent() + + /** + * Called by HTML_QuickForm whenever form event is made on this element + * + * @param string Name of event + * @param mixed event arguments + * @param object calling object + * @since 1.0 + * @access public + * @return bool + */ + function onQuickFormEvent($event, $arg, &$caller) + { + switch ($event) { + case 'updateValue': + if ($caller->getAttribute('method') == 'get') { + return PEAR::raiseError('Cannot add a file upload field to a GET method form'); + } + $this->_value = $this->_findValue(); + $caller->updateAttributes(array('enctype' => 'multipart/form-data')); + $caller->setMaxFileSize(); + break; + case 'addElement': + $this->onQuickFormEvent('createElement', $arg, $caller); + return $this->onQuickFormEvent('updateValue', null, $caller); + break; + case 'createElement': + $className = get_class($this); + $this->$className($arg[0], $arg[1], $arg[2]); + break; + } + return true; + } // end func onQuickFormEvent + + // }}} + // {{{ moveUploadedFile() + + /** + * Moves an uploaded file into the destination + * + * @param string Destination directory path + * @param string New file name + * @access public + * @return bool Whether the file was moved successfully + */ + function moveUploadedFile($dest, $fileName = '') + { + if ($dest != '' && substr($dest, -1) != '/') { + $dest .= '/'; + } + $fileName = ($fileName != '') ? $fileName : basename($this->_value['name']); + if (move_uploaded_file($this->_value['tmp_name'], $dest . $fileName)) { + return true; + } else { + return false; + } + } // end func moveUploadedFile + + // }}} + // {{{ isUploadedFile() + + /** + * Checks if the element contains an uploaded file + * + * @access public + * @return bool true if file has been uploaded, false otherwise + */ + function isUploadedFile() + { + return $this->_ruleIsUploadedFile($this->_value); + } // end func isUploadedFile + + // }}} + // {{{ _ruleIsUploadedFile() + + /** + * Checks if the given element contains an uploaded file + * + * @param array Uploaded file info (from $_FILES) + * @access private + * @return bool true if file has been uploaded, false otherwise + */ + function _ruleIsUploadedFile($elementValue) + { + if ((isset($elementValue['error']) && $elementValue['error'] == 0) || + (!empty($elementValue['tmp_name']) && $elementValue['tmp_name'] != 'none')) { + return is_uploaded_file($elementValue['tmp_name']); + } else { + return false; + } + } // end func _ruleIsUploadedFile + + // }}} + // {{{ _ruleCheckMaxFileSize() + + /** + * Checks that the file does not exceed the max file size + * + * @param array Uploaded file info (from $_FILES) + * @param int Max file size + * @access private + * @return bool true if filesize is lower than maxsize, false otherwise + */ + function _ruleCheckMaxFileSize($elementValue, $maxSize) + { + if (!empty($elementValue['error']) && + (UPLOAD_ERR_FORM_SIZE == $elementValue['error'] || UPLOAD_ERR_INI_SIZE == $elementValue['error'])) { + return false; + } + if (!HTML_QuickForm_file::_ruleIsUploadedFile($elementValue)) { + return true; + } + return ($maxSize >= @filesize($elementValue['tmp_name'])); + } // end func _ruleCheckMaxFileSize + + // }}} + // {{{ _ruleCheckMimeType() + + /** + * Checks if the given element contains an uploaded file of the right mime type + * + * @param array Uploaded file info (from $_FILES) + * @param mixed Mime Type (can be an array of allowed types) + * @access private + * @return bool true if mimetype is correct, false otherwise + */ + function _ruleCheckMimeType($elementValue, $mimeType) + { + if (!HTML_QuickForm_file::_ruleIsUploadedFile($elementValue)) { + return true; + } + if (is_array($mimeType)) { + return in_array($elementValue['type'], $mimeType); + } + return $elementValue['type'] == $mimeType; + } // end func _ruleCheckMimeType + + // }}} + // {{{ _ruleCheckFileName() + + /** + * Checks if the given element contains an uploaded file of the filename regex + * + * @param array Uploaded file info (from $_FILES) + * @param string Regular expression + * @access private + * @return bool true if name matches regex, false otherwise + */ + function _ruleCheckFileName($elementValue, $regex) + { + if (!HTML_QuickForm_file::_ruleIsUploadedFile($elementValue)) { + return true; + } + return preg_match($regex, $elementValue['name']); + } // end func _ruleCheckFileName + + // }}} + // {{{ _findValue() + + /** + * Tries to find the element value from the values array + * + * Needs to be redefined here as $_FILES is populated differently from + * other arrays when element name is of the form foo[bar] + * + * @access private + * @return mixed + */ + function _findValue() + { + if (empty($_FILES)) { + return null; + } + $elementName = $this->getName(); + if (isset($_FILES[$elementName])) { + return $_FILES[$elementName]; + } elseif (false !== ($pos = strpos($elementName, '['))) { + $base = substr($elementName, 0, $pos); + $idx = "['" . str_replace(array(']', '['), array('', "']['"), substr($elementName, $pos + 1, -1)) . "']"; + $props = array('name', 'type', 'size', 'tmp_name', 'error'); + $code = "if (!isset(\$_FILES['{$base}']['name']{$idx})) {\n" . + " return null;\n" . + "} else {\n" . + " \$value = array();\n"; + foreach ($props as $prop) { + $code .= " \$value['{$prop}'] = \$_FILES['{$base}']['{$prop}']{$idx};\n"; + } + return eval($code . " return \$value;\n}\n"); + } else { + return null; + } + } + + // }}} +} // end class HTML_QuickForm_file +?> diff --git a/lib/pear/HTML/QuickForm/group.php b/lib/pear/HTML/QuickForm/group.php new file mode 100644 index 0000000000..f24fe37cc0 --- /dev/null +++ b/lib/pear/HTML/QuickForm/group.php @@ -0,0 +1,579 @@ + | +// | Bertrand Mansion | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once("HTML/QuickForm/element.php"); + +/** + * HTML class for a form element group + * + * @author Adam Daniel + * @author Bertrand Mansion + * @version 1.0 + * @since PHP4.04pl1 + * @access public + */ +class HTML_QuickForm_group extends HTML_QuickForm_element +{ + // {{{ properties + + /** + * Name of the element + * @var string + * @since 1.0 + * @access private + */ + var $_name = ''; + + /** + * Array of grouped elements + * @var array + * @since 1.0 + * @access private + */ + var $_elements = array(); + + /** + * String to separate elements + * @var mixed + * @since 2.5 + * @access private + */ + var $_separator = null; + + /** + * Required elements in this group + * @var array + * @since 2.5 + * @access private + */ + var $_required = array(); + + /** + * Whether to change elements' names to $groupName[$elementName] or leave them as is + * @var bool + * @since 3.0 + * @access private + */ + var $_appendName = true; + + // }}} + // {{{ constructor + + /** + * Class constructor + * + * @param string $elementName (optional)Group name + * @param array $elementLabel (optional)Group label + * @param array $elements (optional)Group elements + * @param mixed $separator (optional)Use a string for one separator, + * use an array to alternate the separators. + * @param bool $appendName (optional)whether to change elements' names to + * the form $groupName[$elementName] or leave + * them as is. + * @since 1.0 + * @access public + * @return void + */ + function HTML_QuickForm_group($elementName=null, $elementLabel=null, $elements=null, $separator=null, $appendName = true) + { + $this->HTML_QuickForm_element($elementName, $elementLabel); + $this->_type = 'group'; + if (isset($elements) && is_array($elements)) { + $this->setElements($elements); + } + if (isset($separator)) { + $this->_separator = $separator; + } + if (isset($appendName)) { + $this->_appendName = $appendName; + } + } //end constructor + + // }}} + // {{{ setName() + + /** + * Sets the group name + * + * @param string $name Group name + * @since 1.0 + * @access public + * @return void + */ + function setName($name) + { + $this->_name = $name; + } //end func setName + + // }}} + // {{{ getName() + + /** + * Returns the group name + * + * @since 1.0 + * @access public + * @return string + */ + function getName() + { + return $this->_name; + } //end func getName + + // }}} + // {{{ setValue() + + /** + * Sets values for group's elements + * + * @param mixed Values for group's elements + * @since 1.0 + * @access public + * @return void + */ + function setValue($value) + { + $this->_createElementsIfNotExist(); + foreach (array_keys($this->_elements) as $key) { + if (!$this->_appendName) { + $v = $this->_elements[$key]->_findValue($value); + if (null !== $v) { + $this->_elements[$key]->onQuickFormEvent('setGroupValue', $v, $this); + } + + } else { + $elementName = $this->_elements[$key]->getName(); + $index = strlen($elementName) ? $elementName : $key; + if (is_array($value)) { + if (isset($value[$index])) { + $this->_elements[$key]->onQuickFormEvent('setGroupValue', $value[$index], $this); + } + } elseif (isset($value)) { + $this->_elements[$key]->onQuickFormEvent('setGroupValue', $value, $this); + } + } + } + } //end func setValue + + // }}} + // {{{ getValue() + + /** + * Returns the value of the group + * + * @since 1.0 + * @access public + * @return mixed + */ + function getValue() + { + $value = null; + foreach (array_keys($this->_elements) as $key) { + $element =& $this->_elements[$key]; + switch ($element->getType()) { + case 'radio': + $v = $element->getChecked()? $element->getValue(): null; + break; + case 'checkbox': + $v = $element->getChecked()? true: null; + break; + default: + $v = $element->getValue(); + } + if (null !== $v) { + $elementName = $element->getName(); + if (is_null($elementName)) { + $value = $v; + } else { + if (!is_array($value)) { + $value = is_null($value)? array(): array($value); + } + if ('' === $elementName) { + $value[] = $v; + } else { + $value[$elementName] = $v; + } + } + } + } + return $value; + } // end func getValue + + // }}} + // {{{ setElements() + + /** + * Sets the grouped elements + * + * @param array $elements Array of elements + * @since 1.1 + * @access public + * @return void + */ + function setElements($elements) + { + $this->_elements = array_values($elements); + if ($this->_flagFrozen) { + $this->freeze(); + } + } // end func setElements + + // }}} + // {{{ getElements() + + /** + * Gets the grouped elements + * + * @since 2.4 + * @access public + * @return array + */ + function &getElements() + { + $this->_createElementsIfNotExist(); + return $this->_elements; + } // end func getElements + + // }}} + // {{{ getGroupType() + + /** + * Gets the group type based on its elements + * Will return 'mixed' if elements contained in the group + * are of different types. + * + * @access public + * @return string group elements type + */ + function getGroupType() + { + $this->_createElementsIfNotExist(); + $prevType = ''; + foreach (array_keys($this->_elements) as $key) { + $type = $this->_elements[$key]->getType(); + if ($type != $prevType && $prevType != '') { + return 'mixed'; + } + $prevType = $type; + } + return $type; + } // end func getGroupType + + // }}} + // {{{ toHtml() + + /** + * Returns Html for the group + * + * @since 1.0 + * @access public + * @return string + */ + function toHtml() + { + include_once('HTML/QuickForm/Renderer/Default.php'); + $renderer =& new HTML_QuickForm_Renderer_Default(); + $renderer->setElementTemplate('{element}'); + $this->accept($renderer); + return $renderer->toHtml(); + } //end func toHtml + + // }}} + // {{{ getElementName() + + /** + * Returns the element name inside the group such as found in the html form + * + * @param mixed $index Element name or element index in the group + * @since 3.0 + * @access public + * @return mixed string with element name, false if not found + */ + function getElementName($index) + { + $this->_createElementsIfNotExist(); + $elementName = false; + if (is_int($index) && isset($this->_elements[$index])) { + $elementName = $this->_elements[$index]->getName(); + if (isset($elementName) && $elementName == '') { + $elementName = $index; + } + if ($this->_appendName) { + if (is_null($elementName)) { + $elementName = $this->getName(); + } else { + $elementName = $this->getName().'['.$elementName.']'; + } + } + + } elseif (is_string($index)) { + foreach (array_keys($this->_elements) as $key) { + $elementName = $this->_elements[$key]->getName(); + if ($index == $elementName) { + if ($this->_appendName) { + $elementName = $this->getName().'['.$elementName.']'; + } + break; + } elseif ($this->_appendName && $this->getName().'['.$elementName.']' == $index) { + break; + } + } + } + return $elementName; + } //end func getElementName + + // }}} + // {{{ getFrozenHtml() + + /** + * Returns the value of field without HTML tags + * + * @since 1.3 + * @access public + * @return string + */ + function getFrozenHtml() + { + $flags = array(); + $this->_createElementsIfNotExist(); + foreach (array_keys($this->_elements) as $key) { + if (false === ($flags[$key] = $this->_elements[$key]->isFrozen())) { + $this->_elements[$key]->freeze(); + } + } + $html = $this->toHtml(); + foreach (array_keys($this->_elements) as $key) { + if (!$flags[$key]) { + $this->_elements[$key]->unfreeze(); + } + } + return $html; + } //end func getFrozenHtml + + // }}} + // {{{ onQuickFormEvent() + + /** + * Called by HTML_QuickForm whenever form event is made on this element + * + * @param string $event Name of event + * @param mixed $arg event arguments + * @param object $caller calling object + * @since 1.0 + * @access public + * @return void + */ + function onQuickFormEvent($event, $arg, &$caller) + { + switch ($event) { + case 'updateValue': + $this->_createElementsIfNotExist(); + foreach (array_keys($this->_elements) as $key) { + if ($this->_appendName) { + $elementName = $this->_elements[$key]->getName(); + if (is_null($elementName)) { + $this->_elements[$key]->setName($this->getName()); + } elseif ('' === $elementName) { + $this->_elements[$key]->setName($this->getName() . '[' . $key . ']'); + } else { + $this->_elements[$key]->setName($this->getName() . '[' . $elementName . ']'); + } + } + $this->_elements[$key]->onQuickFormEvent('updateValue', $arg, $caller); + if ($this->_appendName) { + $this->_elements[$key]->setName($elementName); + } + } + break; + + default: + parent::onQuickFormEvent($event, $arg, $caller); + } + return true; + } // end func onQuickFormEvent + + // }}} + // {{{ accept() + + /** + * Accepts a renderer + * + * @param object An HTML_QuickForm_Renderer object + * @param bool Whether a group is required + * @param string An error message associated with a group + * @access public + * @return void + */ + function accept(&$renderer, $required = false, $error = null) + { + $this->_createElementsIfNotExist(); + $renderer->startGroup($this, $required, $error); + $name = $this->getName(); + foreach (array_keys($this->_elements) as $key) { + $element =& $this->_elements[$key]; + + if ($this->_appendName) { + $elementName = $element->getName(); + if (isset($elementName)) { + $element->setName($name . '['. (strlen($elementName)? $elementName: $key) .']'); + } else { + $element->setName($name); + } + } + + $required = !$element->isFrozen() && in_array($element->getName(), $this->_required); + + $element->accept($renderer, $required); + + // restore the element's name + if ($this->_appendName) { + $element->setName($elementName); + } + } + $renderer->finishGroup($this); + } // end func accept + + // }}} + // {{{ exportValue() + + /** + * As usual, to get the group's value we access its elements and call + * their exportValue() methods + */ + function exportValue(&$submitValues, $assoc = false) + { + $value = null; + foreach (array_keys($this->_elements) as $key) { + $elementName = $this->_elements[$key]->getName(); + if ($this->_appendName) { + if (is_null($elementName)) { + $this->_elements[$key]->setName($this->getName()); + } elseif ('' === $elementName) { + $this->_elements[$key]->setName($this->getName() . '[' . $key . ']'); + } else { + $this->_elements[$key]->setName($this->getName() . '[' . $elementName . ']'); + } + } + $v = $this->_elements[$key]->exportValue($submitValues, $assoc); + if ($this->_appendName) { + $this->_elements[$key]->setName($elementName); + } + if (null !== $v) { + // Make $value an array, we will use it like one + if (null === $value) { + $value = array(); + } + if ($assoc) { + // just like HTML_QuickForm::exportValues() + $value = HTML_QuickForm::arrayMerge($value, $v); + } else { + // just like getValue(), but should work OK every time here + if (is_null($elementName)) { + $value = $v; + } elseif ('' === $elementName) { + $value[] = $v; + } else { + $value[$elementName] = $v; + } + } + } + } + // do not pass the value through _prepareValue, we took care of this already + return $value; + } + + // }}} + // {{{ _createElements() + + /** + * Creates the group's elements. + * + * This should be overriden by child classes that need to create their + * elements. The method will be called automatically when needed, calling + * it from the constructor is discouraged as the constructor is usually + * called _twice_ on element creation, first time with _no_ parameters. + * + * @access private + * @abstract + */ + function _createElements() + { + // abstract + } + + // }}} + // {{{ _createElementsIfNotExist() + + /** + * A wrapper around _createElements() + * + * This method calls _createElements() if the group's _elements array + * is empty. It also performs some updates, e.g. freezes the created + * elements if the group is already frozen. + * + * @access private + */ + function _createElementsIfNotExist() + { + if (empty($this->_elements)) { + $this->_createElements(); + if ($this->_flagFrozen) { + $this->freeze(); + } + } + } + + // }}} + // {{{ freeze() + + function freeze() + { + parent::freeze(); + foreach (array_keys($this->_elements) as $key) { + $this->_elements[$key]->freeze(); + } + } + + // }}} + // {{{ unfreeze() + + function unfreeze() + { + parent::unfreeze(); + foreach (array_keys($this->_elements) as $key) { + $this->_elements[$key]->unfreeze(); + } + } + + // }}} + // {{{ setPersistantFreeze() + + function setPersistantFreeze($persistant = false) + { + parent::setPersistantFreeze($persistant); + foreach (array_keys($this->_elements) as $key) { + $this->_elements[$key]->setPersistantFreeze($persistant); + } + } + + // }}} +} //end class HTML_QuickForm_group +?> \ No newline at end of file diff --git a/lib/pear/HTML/QuickForm/header.php b/lib/pear/HTML/QuickForm/header.php new file mode 100644 index 0000000000..b1745ab3d1 --- /dev/null +++ b/lib/pear/HTML/QuickForm/header.php @@ -0,0 +1,65 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once 'HTML/QuickForm/static.php'; + +/** + * A pseudo-element used for adding headers to form + * + * @author Alexey Borzov + * @access public + */ +class HTML_QuickForm_header extends HTML_QuickForm_static +{ + // {{{ constructor + + /** + * Class constructor + * + * @param string $elementName Header name + * @param string $text Header text + * @access public + * @return void + */ + function HTML_QuickForm_header($elementName = null, $text = null) + { + $this->HTML_QuickForm_static($elementName, null, $text); + $this->_type = 'header'; + } + + // }}} + // {{{ accept() + + /** + * Accepts a renderer + * + * @param object An HTML_QuickForm_Renderer object + * @access public + * @return void + */ + function accept(&$renderer) + { + $renderer->renderHeader($this); + } // end func accept + + // }}} + +} //end class HTML_QuickForm_header +?> diff --git a/lib/pear/HTML/QuickForm/hidden.php b/lib/pear/HTML/QuickForm/hidden.php new file mode 100644 index 0000000000..acd4a48aa2 --- /dev/null +++ b/lib/pear/HTML/QuickForm/hidden.php @@ -0,0 +1,87 @@ + | +// | Bertrand Mansion | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once("HTML/QuickForm/input.php"); + +/** + * HTML class for a hidden type element + * + * @author Adam Daniel + * @author Bertrand Mansion + * @version 1.0 + * @since PHP4.04pl1 + * @access public + */ +class HTML_QuickForm_hidden extends HTML_QuickForm_input +{ + // {{{ constructor + + /** + * Class constructor + * + * @param string $elementName (optional)Input field name attribute + * @param string $value (optional)Input field value + * @param mixed $attributes (optional)Either a typical HTML attribute string + * or an associative array + * @since 1.0 + * @access public + * @return void + */ + function HTML_QuickForm_hidden($elementName=null, $value='', $attributes=null) + { + HTML_QuickForm_input::HTML_QuickForm_input($elementName, null, $attributes); + $this->setType('hidden'); + $this->setValue($value); + } //end constructor + + // }}} + // {{{ freeze() + + /** + * Freeze the element so that only its value is returned + * + * @access public + * @return void + */ + function freeze() + { + return false; + } //end func freeze + + // }}} + // {{{ accept() + + /** + * Accepts a renderer + * + * @param object An HTML_QuickForm_Renderer object + * @access public + * @return void + */ + function accept(&$renderer) + { + $renderer->renderHidden($this); + } // end func accept + + // }}} + +} //end class HTML_QuickForm_hidden +?> diff --git a/lib/pear/HTML/QuickForm/hiddenselect.php b/lib/pear/HTML/QuickForm/hiddenselect.php new file mode 100644 index 0000000000..3fb26b2cb6 --- /dev/null +++ b/lib/pear/HTML/QuickForm/hiddenselect.php @@ -0,0 +1,107 @@ + | +// | Bertrand Mansion | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once('HTML/QuickForm/select.php'); + +/** + * This class takes the same arguments as a select element, but instead + * of creating a select ring it creates hidden elements for all values + * already selected with setDefault or setConstant. This is useful if + * you have a select ring that you don't want visible, but you need all + * selected values to be passed. + * + * @author Isaac Shepard + * + * @version 1.0 + * @since 2.1 + * @access public + */ +class HTML_QuickForm_hiddenselect extends HTML_QuickForm_select +{ + // {{{ constructor + + /** + * Class constructor + * + * @param string Select name attribute + * @param mixed Label(s) for the select (not used) + * @param mixed Data to be used to populate options + * @param mixed Either a typical HTML attribute string or an associative array (not used) + * @since 1.0 + * @access public + * @return void + */ + function HTML_QuickForm_hiddenselect($elementName=null, $elementLabel=null, $options=null, $attributes=null) + { + HTML_QuickForm_element::HTML_QuickForm_element($elementName, $elementLabel, $attributes); + $this->_persistantFreeze = true; + $this->_type = 'hiddenselect'; + if (isset($options)) { + $this->load($options); + } + } //end constructor + + // }}} + // {{{ toHtml() + + /** + * Returns the SELECT in HTML + * + * @since 1.0 + * @access public + * @return string + * @throws + */ + function toHtml() + { + $tabs = $this->_getTabs(); + $name = $this->getPrivateName(); + $strHtml = ''; + + foreach ($this->_values as $key => $val) { + for ($i = 0, $optCount = count($this->_options); $i < $optCount; $i++) { + if ($val == $this->_options[$i]['attr']['value']) { + $strHtml .= $tabs . '_getAttrString(array( + 'type' => 'hidden', + 'name' => $name, + 'value' => $val + )) . " />\n" ; + } + } + } + + return $strHtml; + } //end func toHtml + + // }}} + // {{{ accept() + + /** + * This is essentially a hidden element and should be rendered as one + */ + function accept(&$renderer) + { + $renderer->renderHidden($this); + } + + // }}} +} //end class HTML_QuickForm_hiddenselect +?> diff --git a/lib/pear/HTML/QuickForm/hierselect.php b/lib/pear/HTML/QuickForm/hierselect.php new file mode 100644 index 0000000000..8c0f31eb24 --- /dev/null +++ b/lib/pear/HTML/QuickForm/hierselect.php @@ -0,0 +1,578 @@ + | +// | Bertrand Mansion | +// | Alexey Borzov +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once('HTML/QuickForm/group.php'); +require_once('HTML/QuickForm/select.php'); + +/** + * Class to dynamically create two or more HTML Select elements + * The first select changes the content of the second select and so on. + * This element is considered as a group. Selects will be named + * groupName[0], groupName[1], groupName[2]... + * + * @author Herim Vasquez + * @author Bertrand Mansion + * @version 1.0 + * @since PHP4.04pl1 + * @access public + */ +class HTML_QuickForm_hierselect extends HTML_QuickForm_group +{ + // {{{ properties + + /** + * Options for all the select elements + * + * Format is a bit more complex as we need to know which options + * are related to the ones in the previous select: + * + * Ex: + * // first select + * $select1[0] = 'Pop'; + * $select1[1] = 'Classical'; + * $select1[2] = 'Funeral doom'; + * + * // second select + * $select2[0][0] = 'Red Hot Chil Peppers'; + * $select2[0][1] = 'The Pixies'; + * $select2[1][0] = 'Wagner'; + * $select2[1][1] = 'Strauss'; + * $select2[2][0] = 'Pantheist'; + * $select2[2][1] = 'Skepticism'; + * + * // If only need two selects + * // - and using the depracated functions + * $sel =& $form->addElement('hierselect', 'cds', 'Choose CD:'); + * $sel->setMainOptions($select1); + * $sel->setSecOptions($select2); + * + * // - and using the new setOptions function + * $sel =& $form->addElement('hierselect', 'cds', 'Choose CD:'); + * $sel->setOptions(array($select1, $select2)); + * + * // If you have a third select with prices for the cds + * $select3[0][0][0] = '15.00$'; + * $select3[0][0][1] = '17.00$'; + * etc + * + * // You can now use + * $sel =& $form->addElement('hierselect', 'cds', 'Choose CD:'); + * $sel->setOptions(array($select1, $select2, $select3)); + * + * @var array + * @access private + */ + var $_options = array(); + + /** + * Number of select elements on this group + * + * @var int + * @access private + */ + var $_nbElements = 0; + + /** + * The javascript used to set and change the options + * + * @var string + * @access private + */ + var $_js = ''; + + // }}} + // {{{ constructor + + /** + * Class constructor + * + * @param string $elementName (optional)Input field name attribute + * @param string $elementLabel (optional)Input field label in form + * @param mixed $attributes (optional)Either a typical HTML attribute string + * or an associative array. Date format is passed along the attributes. + * @param mixed $separator (optional)Use a string for one separator, + * use an array to alternate the separators. + * @access public + * @return void + */ + function HTML_QuickForm_hierselect($elementName=null, $elementLabel=null, $attributes=null, $separator=null) + { + $this->HTML_QuickForm_element($elementName, $elementLabel, $attributes); + $this->_persistantFreeze = true; + if (isset($separator)) { + $this->_separator = $separator; + } + $this->_type = 'hierselect'; + $this->_appendName = true; + } //end constructor + + // }}} + // {{{ setOptions() + + /** + * Initialize the array structure containing the options for each select element. + * Call the functions that actually do the magic. + * + * @param array $options Array of options defining each element + * + * @access public + * @return void + */ + function setOptions($options) + { + $this->_options = $options; + + if (empty($this->_elements)) { + $this->_nbElements = count($this->_options); + $this->_createElements(); + } else { + // setDefaults has probably been called before this function + // check if all elements have been created + $totalNbElements = count($this->_options); + for ($i = $this->_nbElements; $i < $totalNbElements; $i ++) { + $this->_elements[] =& new HTML_QuickForm_select($i, null, array(), $this->getAttributes()); + $this->_nbElements++; + } + } + + $this->_setOptions(); + } // end func setMainOptions + + // }}} + // {{{ setMainOptions() + + /** + * Sets the options for the first select element. Deprecated. setOptions() should be used. + * + * @param array $array Options for the first select element + * + * @access public + * @deprecated Deprecated since release 3.2.2 + * @return void + */ + function setMainOptions($array) + { + $this->_options[0] = $array; + + if (empty($this->_elements)) { + $this->_nbElements = 2; + $this->_createElements(); + } + } // end func setMainOptions + + // }}} + // {{{ setSecOptions() + + /** + * Sets the options for the second select element. Deprecated. setOptions() should be used. + * The main _options array is initialized and the _setOptions function is called. + * + * @param array $array Options for the second select element + * + * @access public + * @deprecated Deprecated since release 3.2.2 + * @return void + */ + function setSecOptions($array) + { + $this->_options[1] = $array; + + if (empty($this->_elements)) { + $this->_nbElements = 2; + $this->_createElements(); + } else { + // setDefaults has probably been called before this function + // check if all elements have been created + $totalNbElements = 2; + for ($i = $this->_nbElements; $i < $totalNbElements; $i ++) { + $this->_elements[] =& new HTML_QuickForm_select($i, null, array(), $this->getAttributes()); + $this->_nbElements++; + } + } + + $this->_setOptions(); + } // end func setSecOptions + + // }}} + // {{{ _setOptions() + + /** + * Sets the options for each select element + * + * @access private + * @return void + */ + function _setOptions() + { + $toLoad = ''; + foreach (array_keys($this->_elements) AS $key) { + $array = eval("return isset(\$this->_options[{$key}]{$toLoad})? \$this->_options[{$key}]{$toLoad}: null;"); + if (is_array($array)) { + $select =& $this->_elements[$key]; + $select->_options = array(); + $select->loadArray($array); + + $value = is_array($v = $select->getValue()) ? $v[0] : key($array); + $toLoad .= '[\'' . str_replace(array('\\', '\''), array('\\\\', '\\\''), $value) . '\']'; + } + } + } // end func _setOptions + + // }}} + // {{{ setValue() + + /** + * Sets values for group's elements + * + * @param array $value An array of 2 or more values, for the first, + * the second, the third etc. select + * + * @access public + * @return void + */ + function setValue($value) + { + // fix for bug #6766. Hope this doesn't break anything more + // after bug #7961. Forgot that _nbElements was used in + // _createElements() called in several places... + $this->_nbElements = max($this->_nbElements, count($value)); + parent::setValue($value); + $this->_setOptions(); + } // end func setValue + + // }}} + // {{{ _createElements() + + /** + * Creates all the elements for the group + * + * @access private + * @return void + */ + function _createElements() + { + for ($i = 0; $i < $this->_nbElements; $i++) { + $this->_elements[] =& new HTML_QuickForm_select($i, null, array(), $this->getAttributes()); + } + } // end func _createElements + + // }}} + // {{{ toHtml() + + function toHtml() + { + $this->_js = ''; + if (!$this->_flagFrozen) { + // set the onchange attribute for each element except last + $keys = array_keys($this->_elements); + $onChange = array(); + for ($i = 0; $i < count($keys) - 1; $i++) { + $select =& $this->_elements[$keys[$i]]; + $onChange[$i] = $select->getAttribute('onchange'); + $select->updateAttributes( + array('onchange' => '_hs_swapOptions(this.form, \'' . $this->_escapeString($this->getName()) . '\', ' . $keys[$i] . ');' . $onChange[$i]) + ); + } + + // create the js function to call + if (!defined('HTML_QUICKFORM_HIERSELECT_EXISTS')) { + $this->_js .= <<_nbElements; $i++) { + $jsParts[] = $this->_convertArrayToJavascript($this->_options[$i]); + } + $this->_js .= "\n_hs_options['" . $this->_escapeString($this->getName()) . "'] = [\n" . + implode(",\n", $jsParts) . + "\n];\n"; + // default value; if we don't actually have any values yet just use + // the first option (for single selects) or empty array (for multiple) + $values = array(); + foreach (array_keys($this->_elements) as $key) { + if (is_array($v = $this->_elements[$key]->getValue())) { + $values[] = count($v) > 1? $v: $v[0]; + } else { + // XXX: accessing the supposedly private _options array + $values[] = $this->_elements[$key]->getMultiple() || empty($this->_elements[$key]->_options[0])? + array(): + $this->_elements[$key]->_options[0]['attr']['value']; + } + } + $this->_js .= "_hs_defaults['" . $this->_escapeString($this->getName()) . "'] = " . + $this->_convertArrayToJavascript($values, false) . ";\n"; + } + include_once('HTML/QuickForm/Renderer/Default.php'); + $renderer =& new HTML_QuickForm_Renderer_Default(); + $renderer->setElementTemplate('{element}'); + parent::accept($renderer); + + if (!empty($onChange)) { + $keys = array_keys($this->_elements); + for ($i = 0; $i < count($keys) - 1; $i++) { + $this->_elements[$keys[$i]]->updateAttributes(array('onchange' => $onChange[$i])); + } + } + return (empty($this->_js)? '': "") . + $renderer->toHtml(); + } // end func toHtml + + // }}} + // {{{ accept() + + function accept(&$renderer, $required = false, $error = null) + { + $renderer->renderElement($this, $required, $error); + } // end func accept + + // }}} + // {{{ onQuickFormEvent() + + function onQuickFormEvent($event, $arg, &$caller) + { + if ('updateValue' == $event) { + // we need to call setValue() so that the secondary option + // matches the main option + return HTML_QuickForm_element::onQuickFormEvent($event, $arg, $caller); + } else { + $ret = parent::onQuickFormEvent($event, $arg, $caller); + // add onreset handler to form to properly reset hierselect (see bug #2970) + if ('addElement' == $event) { + $onReset = $caller->getAttribute('onreset'); + if (strlen($onReset)) { + if (strpos($onReset, '_hs_setupOnReset')) { + $caller->updateAttributes(array('onreset' => str_replace('_hs_setupOnReset(this, [', "_hs_setupOnReset(this, ['" . $this->_escapeString($this->getName()) . "', ", $onReset))); + } else { + $caller->updateAttributes(array('onreset' => "var temp = function() { {$onReset} } ; if (!temp()) { return false; } ; if (typeof _hs_setupOnReset != 'undefined') { return _hs_setupOnReset(this, ['" . $this->_escapeString($this->getName()) . "']); } ")); + } + } else { + $caller->updateAttributes(array('onreset' => "if (typeof _hs_setupOnReset != 'undefined') { return _hs_setupOnReset(this, ['" . $this->_escapeString($this->getName()) . "']); } ")); + } + } + return $ret; + } + } // end func onQuickFormEvent + + // }}} + // {{{ _convertArrayToJavascript() + + /** + * Converts PHP array to its Javascript analog + * + * @access private + * @param array PHP array to convert + * @param bool Generate Javascript object literal (default, works like PHP's associative array) or array literal + * @return string Javascript representation of the value + */ + function _convertArrayToJavascript($array, $assoc = true) + { + if (!is_array($array)) { + return $this->_convertScalarToJavascript($array); + } else { + $items = array(); + foreach ($array as $key => $val) { + $item = $assoc? "'" . $this->_escapeString($key) . "': ": ''; + if (is_array($val)) { + $item .= $this->_convertArrayToJavascript($val, $assoc); + } else { + $item .= $this->_convertScalarToJavascript($val); + } + $items[] = $item; + } + } + $js = implode(', ', $items); + return $assoc? '{ ' . $js . ' }': '[' . $js . ']'; + } + + // }}} + // {{{ _convertScalarToJavascript() + + /** + * Converts PHP's scalar value to its Javascript analog + * + * @access private + * @param mixed PHP value to convert + * @return string Javascript representation of the value + */ + function _convertScalarToJavascript($val) + { + if (is_bool($val)) { + return $val ? 'true' : 'false'; + } elseif (is_int($val) || is_double($val)) { + return $val; + } elseif (is_string($val)) { + return "'" . $this->_escapeString($val) . "'"; + } elseif (is_null($val)) { + return 'null'; + } else { + // don't bother + return '{}'; + } + } + + // }}} + // {{{ _escapeString() + + /** + * Quotes the string so that it can be used in Javascript string constants + * + * @access private + * @param string + * @return string + */ + function _escapeString($str) + { + return strtr($str,array( + "\r" => '\r', + "\n" => '\n', + "\t" => '\t', + "'" => "\\'", + '"' => '\"', + '\\' => '\\\\' + )); + } + + // }}} +} // end class HTML_QuickForm_hierselect +?> \ No newline at end of file diff --git a/lib/pear/HTML/QuickForm/html.php b/lib/pear/HTML/QuickForm/html.php new file mode 100644 index 0000000000..6d96e4f644 --- /dev/null +++ b/lib/pear/HTML/QuickForm/html.php @@ -0,0 +1,67 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once 'HTML/QuickForm/static.php'; + +/** + * A pseudo-element used for adding raw HTML to form + * + * Intended for use with the default renderer only, template-based + * ones may (and probably will) completely ignore this + * + * @author Alexey Borzov + * @access public + */ +class HTML_QuickForm_html extends HTML_QuickForm_static +{ + // {{{ constructor + + /** + * Class constructor + * + * @param string $text raw HTML to add + * @access public + * @return void + */ + function HTML_QuickForm_html($text = null) + { + $this->HTML_QuickForm_static(null, null, $text); + $this->_type = 'html'; + } + + // }}} + // {{{ accept() + + /** + * Accepts a renderer + * + * @param object An HTML_QuickForm_Renderer object + * @access public + * @return void + */ + function accept(&$renderer) + { + $renderer->renderHtml($this); + } // end func accept + + // }}} + +} //end class HTML_QuickForm_header +?> diff --git a/lib/pear/HTML/QuickForm/image.php b/lib/pear/HTML/QuickForm/image.php new file mode 100644 index 0000000000..a3cc2e37c2 --- /dev/null +++ b/lib/pear/HTML/QuickForm/image.php @@ -0,0 +1,119 @@ + | +// | Bertrand Mansion | +// +----------------------------------------------------------------------+ +// +// $Id$ +require_once("HTML/QuickForm/input.php"); + +/** + * HTML class for a image type element + * + * @author Adam Daniel + * @author Bertrand Mansion + * @version 1.0 + * @since PHP4.04pl1 + * @access public + */ +class HTML_QuickForm_image extends HTML_QuickForm_input +{ + // {{{ constructor + + /** + * Class constructor + * + * @param string $elementName (optional)Element name attribute + * @param string $src (optional)Image source + * @param mixed $attributes (optional)Either a typical HTML attribute string + * or an associative array + * @since 1.0 + * @access public + * @return void + */ + function HTML_QuickForm_image($elementName=null, $src='', $attributes=null) + { + HTML_QuickForm_input::HTML_QuickForm_input($elementName, null, $attributes); + $this->setType('image'); + $this->setSource($src); + } // end class constructor + + // }}} + // {{{ setSource() + + /** + * Sets source for image element + * + * @param string $src source for image element + * @since 1.0 + * @access public + * @return void + */ + function setSource($src) + { + $this->updateAttributes(array('src' => $src)); + } // end func setSource + + // }}} + // {{{ setBorder() + + /** + * Sets border size for image element + * + * @param string $border border for image element + * @since 1.0 + * @access public + * @return void + */ + function setBorder($border) + { + $this->updateAttributes(array('border' => $border)); + } // end func setBorder + + // }}} + // {{{ setAlign() + + /** + * Sets alignment for image element + * + * @param string $align alignment for image element + * @since 1.0 + * @access public + * @return void + */ + function setAlign($align) + { + $this->updateAttributes(array('align' => $align)); + } // end func setAlign + + // }}} + // {{{ freeze() + + /** + * Freeze the element so that only its value is returned + * + * @access public + * @return void + */ + function freeze() + { + return false; + } //end func freeze + + // }}} + +} // end class HTML_QuickForm_image +?> diff --git a/lib/pear/HTML/QuickForm/input.php b/lib/pear/HTML/QuickForm/input.php new file mode 100644 index 0000000000..f182e7e4dd --- /dev/null +++ b/lib/pear/HTML/QuickForm/input.php @@ -0,0 +1,202 @@ + | +// | Bertrand Mansion | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once("HTML/QuickForm/element.php"); + +/** + * Base class for input form elements + * + * @author Adam Daniel + * @author Bertrand Mansion + * @version 1.0 + * @since PHP4.04pl1 + * @access public + * @abstract + */ +class HTML_QuickForm_input extends HTML_QuickForm_element +{ + // {{{ constructor + + /** + * Class constructor + * + * @param string Input field name attribute + * @param mixed Label(s) for the input field + * @param mixed Either a typical HTML attribute string or an associative array + * @since 1.0 + * @access public + * @return void + */ + function HTML_QuickForm_input($elementName=null, $elementLabel=null, $attributes=null) + { + $this->HTML_QuickForm_element($elementName, $elementLabel, $attributes); + } //end constructor + + // }}} + // {{{ setType() + + /** + * Sets the element type + * + * @param string $type Element type + * @since 1.0 + * @access public + * @return void + */ + function setType($type) + { + $this->_type = $type; + $this->updateAttributes(array('type'=>$type)); + } // end func setType + + // }}} + // {{{ setName() + + /** + * Sets the input field name + * + * @param string $name Input field name attribute + * @since 1.0 + * @access public + * @return void + */ + function setName($name) + { + $this->updateAttributes(array('name'=>$name)); + } //end func setName + + // }}} + // {{{ getName() + + /** + * Returns the element name + * + * @since 1.0 + * @access public + * @return string + */ + function getName() + { + return $this->getAttribute('name'); + } //end func getName + + // }}} + // {{{ setValue() + + /** + * Sets the value of the form element + * + * @param string $value Default value of the form element + * @since 1.0 + * @access public + * @return void + */ + function setValue($value) + { + $this->updateAttributes(array('value'=>$value)); + } // end func setValue + + // }}} + // {{{ getValue() + + /** + * Returns the value of the form element + * + * @since 1.0 + * @access public + * @return string + */ + function getValue() + { + return $this->getAttribute('value'); + } // end func getValue + + // }}} + // {{{ toHtml() + + /** + * Returns the input field in HTML + * + * @since 1.0 + * @access public + * @return string + */ + function toHtml() + { + if ($this->_flagFrozen) { + return $this->getFrozenHtml(); + } else { + return $this->_getTabs() . '_getAttrString($this->_attributes) . ' />'; + } + } //end func toHtml + + // }}} + // {{{ onQuickFormEvent() + + /** + * Called by HTML_QuickForm whenever form event is made on this element + * + * @param string $event Name of event + * @param mixed $arg event arguments + * @param object $caller calling object + * @since 1.0 + * @access public + * @return void + * @throws + */ + function onQuickFormEvent($event, $arg, &$caller) + { + // do not use submit values for button-type elements + $type = $this->getType(); + if (('updateValue' != $event) || + ('submit' != $type && 'reset' != $type && 'image' != $type && 'button' != $type)) { + parent::onQuickFormEvent($event, $arg, $caller); + } else { + $value = $this->_findValue($caller->_constantValues); + if (null === $value) { + $value = $this->_findValue($caller->_defaultValues); + } + if (null !== $value) { + $this->setValue($value); + } + } + return true; + } // end func onQuickFormEvent + + // }}} + // {{{ exportValue() + + /** + * We don't need values from button-type elements (except submit) and files + */ + function exportValue(&$submitValues, $assoc = false) + { + $type = $this->getType(); + if ('reset' == $type || 'image' == $type || 'button' == $type || 'file' == $type) { + return null; + } else { + return parent::exportValue($submitValues, $assoc); + } + } + + // }}} +} // end class HTML_QuickForm_element +?> diff --git a/lib/pear/HTML/QuickForm/link.php b/lib/pear/HTML/QuickForm/link.php new file mode 100644 index 0000000000..947f76c32b --- /dev/null +++ b/lib/pear/HTML/QuickForm/link.php @@ -0,0 +1,192 @@ + | +// | Bertrand Mansion | +// +----------------------------------------------------------------------+ +// + +require_once 'HTML/QuickForm/static.php'; + +/** + * HTML class for a link type field + * + * @author Adam Daniel + * @author Bertrand Mansion + * @version 1.0 + * @since PHP4.04pl1 + * @access public + */ +class HTML_QuickForm_link extends HTML_QuickForm_static +{ + // {{{ properties + + /** + * Link display text + * @var string + * @since 1.0 + * @access private + */ + var $_text = ""; + + // }}} + // {{{ constructor + + /** + * Class constructor + * + * @param string $elementLabel (optional)Link label + * @param string $href (optional)Link href + * @param string $text (optional)Link display text + * @param mixed $attributes (optional)Either a typical HTML attribute string + * or an associative array + * @since 1.0 + * @access public + * @return void + * @throws + */ + function HTML_QuickForm_link($elementName=null, $elementLabel=null, $href=null, $text=null, $attributes=null) + { + HTML_QuickForm_element::HTML_QuickForm_element($elementName, $elementLabel, $attributes); + $this->_persistantFreeze = false; + $this->_type = 'link'; + $this->setHref($href); + $this->_text = $text; + } //end constructor + + // }}} + // {{{ setName() + + /** + * Sets the input field name + * + * @param string $name Input field name attribute + * @since 1.0 + * @access public + * @return void + * @throws + */ + function setName($name) + { + $this->updateAttributes(array('name'=>$name)); + } //end func setName + + // }}} + // {{{ getName() + + /** + * Returns the element name + * + * @since 1.0 + * @access public + * @return string + * @throws + */ + function getName() + { + return $this->getAttribute('name'); + } //end func getName + + // }}} + // {{{ setValue() + + /** + * Sets value for textarea element + * + * @param string $value Value for password element + * @since 1.0 + * @access public + * @return void + * @throws + */ + function setValue($value) + { + return; + } //end func setValue + + // }}} + // {{{ getValue() + + /** + * Returns the value of the form element + * + * @since 1.0 + * @access public + * @return void + * @throws + */ + function getValue() + { + return; + } // end func getValue + + + // }}} + // {{{ setHref() + + /** + * Sets the links href + * + * @param string $href + * @since 1.0 + * @access public + * @return void + * @throws + */ + function setHref($href) + { + $this->updateAttributes(array('href'=>$href)); + } // end func setHref + + // }}} + // {{{ toHtml() + + /** + * Returns the textarea element in HTML + * + * @since 1.0 + * @access public + * @return string + * @throws + */ + function toHtml() + { + $tabs = $this->_getTabs(); + $html = "$tabs_getAttrString($this->_attributes).">"; + $html .= $this->_text; + $html .= "
"; + return $html; + } //end func toHtml + + // }}} + // {{{ getFrozenHtml() + + /** + * Returns the value of field without HTML tags (in this case, value is changed to a mask) + * + * @since 1.0 + * @access public + * @return string + * @throws + */ + function getFrozenHtml() + { + return; + } //end func getFrozenHtml + + // }}} + +} //end class HTML_QuickForm_textarea +?> diff --git a/lib/pear/HTML/QuickForm/password.php b/lib/pear/HTML/QuickForm/password.php new file mode 100644 index 0000000000..a83d5053e3 --- /dev/null +++ b/lib/pear/HTML/QuickForm/password.php @@ -0,0 +1,108 @@ + | +// | Bertrand Mansion | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once("HTML/QuickForm/input.php"); + +/** + * HTML class for a password type field + * + * @author Adam Daniel + * @author Bertrand Mansion + * @version 1.1 + * @since PHP4.04pl1 + * @access public + */ +class HTML_QuickForm_password extends HTML_QuickForm_input +{ + // {{{ constructor + + /** + * Class constructor + * + * @param string $elementName (optional)Input field name attribute + * @param string $elementLabel (optional)Input field label + * @param mixed $attributes (optional)Either a typical HTML attribute string + * or an associative array + * @since 1.0 + * @access public + * @return void + * @throws + */ + function HTML_QuickForm_password($elementName=null, $elementLabel=null, $attributes=null) + { + HTML_QuickForm_input::HTML_QuickForm_input($elementName, $elementLabel, $attributes); + $this->setType('password'); + } //end constructor + + // }}} + // {{{ setSize() + + /** + * Sets size of password element + * + * @param string $size Size of password field + * @since 1.0 + * @access public + * @return void + */ + function setSize($size) + { + $this->updateAttributes(array('size'=>$size)); + } //end func setSize + + // }}} + // {{{ setMaxlength() + + /** + * Sets maxlength of password element + * + * @param string $maxlength Maximum length of password field + * @since 1.0 + * @access public + * @return void + */ + function setMaxlength($maxlength) + { + $this->updateAttributes(array('maxlength'=>$maxlength)); + } //end func setMaxlength + + // }}} + // {{{ getFrozenHtml() + + /** + * Returns the value of field without HTML tags (in this case, value is changed to a mask) + * + * @since 1.0 + * @access public + * @return string + * @throws + */ + function getFrozenHtml() + { + $value = $this->getValue(); + return ('' != $value? '**********': ' ') . + $this->_getPersistantData(); + } //end func getFrozenHtml + + // }}} + +} //end class HTML_QuickForm_password +?> diff --git a/lib/pear/HTML/QuickForm/radio.php b/lib/pear/HTML/QuickForm/radio.php new file mode 100644 index 0000000000..963168b06a --- /dev/null +++ b/lib/pear/HTML/QuickForm/radio.php @@ -0,0 +1,244 @@ + | +// | Bertrand Mansion | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once('HTML/QuickForm/input.php'); + +/** + * HTML class for a radio type element + * + * @author Adam Daniel + * @author Bertrand Mansion + * @version 1.1 + * @since PHP4.04pl1 + * @access public + */ +class HTML_QuickForm_radio extends HTML_QuickForm_input +{ + // {{{ properties + + /** + * Radio display text + * @var string + * @since 1.1 + * @access private + */ + var $_text = ''; + + // }}} + // {{{ constructor + + /** + * Class constructor + * + * @param string Input field name attribute + * @param mixed Label(s) for a field + * @param string Text to display near the radio + * @param string Input field value + * @param mixed Either a typical HTML attribute string or an associative array + * @since 1.0 + * @access public + * @return void + */ + function HTML_QuickForm_radio($elementName=null, $elementLabel=null, $text=null, $value=null, $attributes=null) + { + $this->HTML_QuickForm_element($elementName, $elementLabel, $attributes); + if (isset($value)) { + $this->setValue($value); + } + $this->_persistantFreeze = true; + $this->setType('radio'); + $this->_text = $text; + $this->_generateId(); + } //end constructor + + // }}} + // {{{ setChecked() + + /** + * Sets whether radio button is checked + * + * @param bool $checked Whether the field is checked or not + * @since 1.0 + * @access public + * @return void + */ + function setChecked($checked) + { + if (!$checked) { + $this->removeAttribute('checked'); + } else { + $this->updateAttributes(array('checked'=>'checked')); + } + } //end func setChecked + + // }}} + // {{{ getChecked() + + /** + * Returns whether radio button is checked + * + * @since 1.0 + * @access public + * @return string + */ + function getChecked() + { + return $this->getAttribute('checked'); + } //end func getChecked + + // }}} + // {{{ toHtml() + + /** + * Returns the radio element in HTML + * + * @since 1.0 + * @access public + * @return string + */ + function toHtml() + { + if (0 == strlen($this->_text)) { + $label = ''; + } elseif ($this->_flagFrozen) { + $label = $this->_text; + } else { + $label = ''; + } + return HTML_QuickForm_input::toHtml() . $label; + } //end func toHtml + + // }}} + // {{{ getFrozenHtml() + + /** + * Returns the value of field without HTML tags + * + * @since 1.0 + * @access public + * @return string + */ + function getFrozenHtml() + { + if ($this->getChecked()) { + return '(x)' . + $this->_getPersistantData(); + } else { + return '( )'; + } + } //end func getFrozenHtml + + // }}} + // {{{ setText() + + /** + * Sets the radio text + * + * @param string $text Text to display near the radio button + * @since 1.1 + * @access public + * @return void + */ + function setText($text) + { + $this->_text = $text; + } //end func setText + + // }}} + // {{{ getText() + + /** + * Returns the radio text + * + * @since 1.1 + * @access public + * @return string + */ + function getText() + { + return $this->_text; + } //end func getText + + // }}} + // {{{ onQuickFormEvent() + + /** + * Called by HTML_QuickForm whenever form event is made on this element + * + * @param string $event Name of event + * @param mixed $arg event arguments + * @param object $caller calling object + * @since 1.0 + * @access public + * @return void + */ + function onQuickFormEvent($event, $arg, &$caller) + { + switch ($event) { + case 'updateValue': + // constant values override both default and submitted ones + // default values are overriden by submitted + $value = $this->_findValue($caller->_constantValues); + if (null === $value) { + $value = $this->_findValue($caller->_submitValues); + if (null === $value) { + $value = $this->_findValue($caller->_defaultValues); + } + } + if ($value == $this->getValue()) { + $this->setChecked(true); + } else { + $this->setChecked(false); + } + break; + case 'setGroupValue': + if ($arg == $this->getValue()) { + $this->setChecked(true); + } else { + $this->setChecked(false); + } + break; + default: + parent::onQuickFormEvent($event, $arg, $caller); + } + return true; + } // end func onQuickFormLoad + + // }}} + // {{{ exportValue() + + /** + * Returns the value attribute if the radio is checked, null if it is not + */ + function exportValue(&$submitValues, $assoc = false) + { + $value = $this->_findValue($submitValues); + if (null === $value) { + $value = $this->getChecked()? $this->getValue(): null; + } elseif ($value != $this->getValue()) { + $value = null; + } + return $this->_prepareValue($value, $assoc); + } + + // }}} +} //end class HTML_QuickForm_radio +?> diff --git a/lib/pear/HTML/QuickForm/reset.php b/lib/pear/HTML/QuickForm/reset.php new file mode 100644 index 0000000000..3533653086 --- /dev/null +++ b/lib/pear/HTML/QuickForm/reset.php @@ -0,0 +1,72 @@ + | +// | Bertrand Mansion | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once("HTML/QuickForm/input.php"); + +/** + * HTML class for a reset type element + * + * @author Adam Daniel + * @author Bertrand Mansion + * @version 1.1 + * @since PHP4.04pl1 + * @access public + */ +class HTML_QuickForm_reset extends HTML_QuickForm_input +{ + // {{{ constructor + + /** + * Class constructor + * + * @param string $elementName (optional)Input field name attribute + * @param string $value (optional)Input field value + * @param mixed $attributes (optional)Either a typical HTML attribute string + * or an associative array + * @since 1.0 + * @access public + * @return void + */ + function HTML_QuickForm_reset($elementName=null, $value=null, $attributes=null) + { + HTML_QuickForm_input::HTML_QuickForm_input($elementName, null, $attributes); + $this->setValue($value); + $this->setType('reset'); + } //end constructor + + // }}} + // {{{ freeze() + + /** + * Freeze the element so that only its value is returned + * + * @access public + * @return void + */ + function freeze() + { + return false; + } //end func freeze + + // }}} + +} //end class HTML_QuickForm_reset +?> diff --git a/lib/pear/HTML/QuickForm/select.php b/lib/pear/HTML/QuickForm/select.php new file mode 100644 index 0000000000..eec2b16403 --- /dev/null +++ b/lib/pear/HTML/QuickForm/select.php @@ -0,0 +1,604 @@ + | +// | Bertrand Mansion | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once('HTML/QuickForm/element.php'); + +/** + * Class to dynamically create an HTML SELECT + * + * @author Adam Daniel + * @author Bertrand Mansion + * @version 1.0 + * @since PHP4.04pl1 + * @access public + */ +class HTML_QuickForm_select extends HTML_QuickForm_element { + + // {{{ properties + + /** + * Contains the select options + * + * @var array + * @since 1.0 + * @access private + */ + var $_options = array(); + + /** + * Default values of the SELECT + * + * @var string + * @since 1.0 + * @access private + */ + var $_values = null; + + // }}} + // {{{ constructor + + /** + * Class constructor + * + * @param string Select name attribute + * @param mixed Label(s) for the select + * @param mixed Data to be used to populate options + * @param mixed Either a typical HTML attribute string or an associative array + * @since 1.0 + * @access public + * @return void + */ + function HTML_QuickForm_select($elementName=null, $elementLabel=null, $options=null, $attributes=null) + { + HTML_QuickForm_element::HTML_QuickForm_element($elementName, $elementLabel, $attributes); + $this->_persistantFreeze = true; + $this->_type = 'select'; + if (isset($options)) { + $this->load($options); + } + } //end constructor + + // }}} + // {{{ apiVersion() + + /** + * Returns the current API version + * + * @since 1.0 + * @access public + * @return double + */ + function apiVersion() + { + return 2.3; + } //end func apiVersion + + // }}} + // {{{ setSelected() + + /** + * Sets the default values of the select box + * + * @param mixed $values Array or comma delimited string of selected values + * @since 1.0 + * @access public + * @return void + */ + function setSelected($values) + { + if (is_string($values) && $this->getMultiple()) { + $values = split("[ ]?,[ ]?", $values); + } + if (is_array($values)) { + $this->_values = array_values($values); + } else { + $this->_values = array($values); + } + } //end func setSelected + + // }}} + // {{{ getSelected() + + /** + * Returns an array of the selected values + * + * @since 1.0 + * @access public + * @return array of selected values + */ + function getSelected() + { + return $this->_values; + } // end func getSelected + + // }}} + // {{{ setName() + + /** + * Sets the input field name + * + * @param string $name Input field name attribute + * @since 1.0 + * @access public + * @return void + */ + function setName($name) + { + $this->updateAttributes(array('name' => $name)); + } //end func setName + + // }}} + // {{{ getName() + + /** + * Returns the element name + * + * @since 1.0 + * @access public + * @return string + */ + function getName() + { + return $this->getAttribute('name'); + } //end func getName + + // }}} + // {{{ getPrivateName() + + /** + * Returns the element name (possibly with brackets appended) + * + * @since 1.0 + * @access public + * @return string + */ + function getPrivateName() + { + if ($this->getAttribute('multiple')) { + return $this->getName() . '[]'; + } else { + return $this->getName(); + } + } //end func getPrivateName + + // }}} + // {{{ setValue() + + /** + * Sets the value of the form element + * + * @param mixed $values Array or comma delimited string of selected values + * @since 1.0 + * @access public + * @return void + */ + function setValue($value) + { + $this->setSelected($value); + } // end func setValue + + // }}} + // {{{ getValue() + + /** + * Returns an array of the selected values + * + * @since 1.0 + * @access public + * @return array of selected values + */ + function getValue() + { + return $this->_values; + } // end func getValue + + // }}} + // {{{ setSize() + + /** + * Sets the select field size, only applies to 'multiple' selects + * + * @param int $size Size of select field + * @since 1.0 + * @access public + * @return void + */ + function setSize($size) + { + $this->updateAttributes(array('size' => $size)); + } //end func setSize + + // }}} + // {{{ getSize() + + /** + * Returns the select field size + * + * @since 1.0 + * @access public + * @return int + */ + function getSize() + { + return $this->getAttribute('size'); + } //end func getSize + + // }}} + // {{{ setMultiple() + + /** + * Sets the select mutiple attribute + * + * @param bool $multiple Whether the select supports multi-selections + * @since 1.2 + * @access public + * @return void + */ + function setMultiple($multiple) + { + if ($multiple) { + $this->updateAttributes(array('multiple' => 'multiple')); + } else { + $this->removeAttribute('multiple'); + } + } //end func setMultiple + + // }}} + // {{{ getMultiple() + + /** + * Returns the select mutiple attribute + * + * @since 1.2 + * @access public + * @return bool true if multiple select, false otherwise + */ + function getMultiple() + { + return (bool)$this->getAttribute('multiple'); + } //end func getMultiple + + // }}} + // {{{ addOption() + + /** + * Adds a new OPTION to the SELECT + * + * @param string $text Display text for the OPTION + * @param string $value Value for the OPTION + * @param mixed $attributes Either a typical HTML attribute string + * or an associative array + * @since 1.0 + * @access public + * @return void + */ + function addOption($text, $value, $attributes=null) + { + if (null === $attributes) { + $attributes = array('value' => $value); + } else { + $attributes = $this->_parseAttributes($attributes); + if (isset($attributes['selected'])) { + // the 'selected' attribute will be set in toHtml() + $this->_removeAttr('selected', $attributes); + if (is_null($this->_values)) { + $this->_values = array($value); + } elseif (!in_array($value, $this->_values)) { + $this->_values[] = $value; + } + } + $this->_updateAttrArray($attributes, array('value' => $value)); + } + $this->_options[] = array('text' => $text, 'attr' => $attributes); + } // end func addOption + + // }}} + // {{{ loadArray() + + /** + * Loads the options from an associative array + * + * @param array $arr Associative array of options + * @param mixed $values (optional) Array or comma delimited string of selected values + * @since 1.0 + * @access public + * @return PEAR_Error on error or true + * @throws PEAR_Error + */ + function loadArray($arr, $values=null) + { + if (!is_array($arr)) { + return PEAR::raiseError('Argument 1 of HTML_Select::loadArray is not a valid array'); + } + if (isset($values)) { + $this->setSelected($values); + } + foreach ($arr as $key => $val) { + // Warning: new API since release 2.3 + $this->addOption($val, $key); + } + return true; + } // end func loadArray + + // }}} + // {{{ loadDbResult() + + /** + * Loads the options from DB_result object + * + * If no column names are specified the first two columns of the result are + * used as the text and value columns respectively + * @param object $result DB_result object + * @param string $textCol (optional) Name of column to display as the OPTION text + * @param string $valueCol (optional) Name of column to use as the OPTION value + * @param mixed $values (optional) Array or comma delimited string of selected values + * @since 1.0 + * @access public + * @return PEAR_Error on error or true + * @throws PEAR_Error + */ + function loadDbResult(&$result, $textCol=null, $valueCol=null, $values=null) + { + if (!is_object($result) || !is_a($result, 'db_result')) { + return PEAR::raiseError('Argument 1 of HTML_Select::loadDbResult is not a valid DB_result'); + } + if (isset($values)) { + $this->setValue($values); + } + $fetchMode = ($textCol && $valueCol) ? DB_FETCHMODE_ASSOC : DB_FETCHMODE_ORDERED; + while (is_array($row = $result->fetchRow($fetchMode)) ) { + if ($fetchMode == DB_FETCHMODE_ASSOC) { + $this->addOption($row[$textCol], $row[$valueCol]); + } else { + $this->addOption($row[0], $row[1]); + } + } + return true; + } // end func loadDbResult + + // }}} + // {{{ loadQuery() + + /** + * Queries a database and loads the options from the results + * + * @param mixed $conn Either an existing DB connection or a valid dsn + * @param string $sql SQL query string + * @param string $textCol (optional) Name of column to display as the OPTION text + * @param string $valueCol (optional) Name of column to use as the OPTION value + * @param mixed $values (optional) Array or comma delimited string of selected values + * @since 1.1 + * @access public + * @return void + * @throws PEAR_Error + */ + function loadQuery(&$conn, $sql, $textCol=null, $valueCol=null, $values=null) + { + if (is_string($conn)) { + require_once('DB.php'); + $dbConn = &DB::connect($conn, true); + if (DB::isError($dbConn)) { + return $dbConn; + } + } elseif (is_subclass_of($conn, "db_common")) { + $dbConn = &$conn; + } else { + return PEAR::raiseError('Argument 1 of HTML_Select::loadQuery is not a valid type'); + } + $result = $dbConn->query($sql); + if (DB::isError($result)) { + return $result; + } + $this->loadDbResult($result, $textCol, $valueCol, $values); + $result->free(); + if (is_string($conn)) { + $dbConn->disconnect(); + } + return true; + } // end func loadQuery + + // }}} + // {{{ load() + + /** + * Loads options from different types of data sources + * + * This method is a simulated overloaded method. The arguments, other than the + * first are optional and only mean something depending on the type of the first argument. + * If the first argument is an array then all arguments are passed in order to loadArray. + * If the first argument is a db_result then all arguments are passed in order to loadDbResult. + * If the first argument is a string or a DB connection then all arguments are + * passed in order to loadQuery. + * @param mixed $options Options source currently supports assoc array or DB_result + * @param mixed $param1 (optional) See function detail + * @param mixed $param2 (optional) See function detail + * @param mixed $param3 (optional) See function detail + * @param mixed $param4 (optional) See function detail + * @since 1.1 + * @access public + * @return PEAR_Error on error or true + * @throws PEAR_Error + */ + function load(&$options, $param1=null, $param2=null, $param3=null, $param4=null) + { + switch (true) { + case is_array($options): + return $this->loadArray($options, $param1); + break; + case (is_a($options, 'db_result')): + return $this->loadDbResult($options, $param1, $param2, $param3); + break; + case (is_string($options) && !empty($options) || is_subclass_of($options, "db_common")): + return $this->loadQuery($options, $param1, $param2, $param3, $param4); + break; + } + } // end func load + + // }}} + // {{{ toHtml() + + /** + * Returns the SELECT in HTML + * + * @since 1.0 + * @access public + * @return string + */ + function toHtml() + { + if ($this->_flagFrozen) { + return $this->getFrozenHtml(); + } else { + $tabs = $this->_getTabs(); + $strHtml = ''; + + if ($this->getComment() != '') { + $strHtml .= $tabs . '\n"; + } + + if (!$this->getMultiple()) { + $attrString = $this->_getAttrString($this->_attributes); + } else { + $myName = $this->getName(); + $this->setName($myName . '[]'); + $attrString = $this->_getAttrString($this->_attributes); + $this->setName($myName); + } + $strHtml .= $tabs . '\n"; + + foreach ($this->_options as $option) { + if (is_array($this->_values) && in_array((string)$option['attr']['value'], $this->_values)) { + $this->_updateAttrArray($option['attr'], array('selected' => 'selected')); + } + $strHtml .= $tabs . "\t_getAttrString($option['attr']) . '>' . + $option['text'] . "\n"; + } + + return $strHtml . $tabs . ''; + } + } //end func toHtml + + // }}} + // {{{ getFrozenHtml() + + /** + * Returns the value of field without HTML tags + * + * @since 1.0 + * @access public + * @return string + */ + function getFrozenHtml() + { + $value = array(); + if (is_array($this->_values)) { + foreach ($this->_values as $key => $val) { + for ($i = 0, $optCount = count($this->_options); $i < $optCount; $i++) { + if ((string)$val == (string)$this->_options[$i]['attr']['value']) { + $value[$key] = $this->_options[$i]['text']; + break; + } + } + } + } + $html = empty($value)? ' ': join('
', $value); + if ($this->_persistantFreeze) { + $name = $this->getPrivateName(); + // Only use id attribute if doing single hidden input + if (1 == count($value)) { + $id = $this->getAttribute('id'); + $idAttr = isset($id)? array('id' => $id): array(); + } else { + $idAttr = array(); + } + foreach ($value as $key => $item) { + $html .= '_getAttrString(array( + 'type' => 'hidden', + 'name' => $name, + 'value' => $this->_values[$key] + ) + $idAttr) . ' />'; + } + } + return $html; + } //end func getFrozenHtml + + // }}} + // {{{ exportValue() + + /** + * We check the options and return only the values that _could_ have been + * selected. We also return a scalar value if select is not "multiple" + */ + function exportValue(&$submitValues, $assoc = false) + { + $value = $this->_findValue($submitValues); + if (is_null($value)) { + $value = $this->getValue(); + } elseif(!is_array($value)) { + $value = array($value); + } + if (is_array($value) && !empty($this->_options)) { + $cleanValue = null; + foreach ($value as $v) { + for ($i = 0, $optCount = count($this->_options); $i < $optCount; $i++) { + if ($v == $this->_options[$i]['attr']['value']) { + $cleanValue[] = $v; + break; + } + } + } + } else { + $cleanValue = $value; + } + if (is_array($cleanValue) && !$this->getMultiple()) { + return $this->_prepareValue($cleanValue[0], $assoc); + } else { + return $this->_prepareValue($cleanValue, $assoc); + } + } + + // }}} + // {{{ onQuickFormEvent() + + function onQuickFormEvent($event, $arg, &$caller) + { + if ('updateValue' == $event) { + $value = $this->_findValue($caller->_constantValues); + if (null === $value) { + $value = $this->_findValue($caller->_submitValues); + // Fix for bug #4465 & #5269 + // XXX: should we push this to element::onQuickFormEvent()? + if (null === $value && (!$caller->isSubmitted() || !$this->getMultiple())) { + $value = $this->_findValue($caller->_defaultValues); + } + } + if (null !== $value) { + $this->setValue($value); + } + return true; + } else { + return parent::onQuickFormEvent($event, $arg, $caller); + } + } + + // }}} +} //end class HTML_QuickForm_select +?> diff --git a/lib/pear/HTML/QuickForm/static.php b/lib/pear/HTML/QuickForm/static.php new file mode 100644 index 0000000000..69879312af --- /dev/null +++ b/lib/pear/HTML/QuickForm/static.php @@ -0,0 +1,193 @@ + | +// | Bertrand Mansion | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once("HTML/QuickForm/element.php"); + +/** + * HTML class for static data + * + * @author Wojciech Gdela + * @access public + */ +class HTML_QuickForm_static extends HTML_QuickForm_element { + + // {{{ properties + + /** + * Display text + * @var string + * @access private + */ + var $_text = null; + + // }}} + // {{{ constructor + + /** + * Class constructor + * + * @param string $elementLabel (optional)Label + * @param string $text (optional)Display text + * @access public + * @return void + */ + function HTML_QuickForm_static($elementName=null, $elementLabel=null, $text=null) + { + HTML_QuickForm_element::HTML_QuickForm_element($elementName, $elementLabel); + $this->_persistantFreeze = false; + $this->_type = 'static'; + $this->_text = $text; + } //end constructor + + // }}} + // {{{ setName() + + /** + * Sets the element name + * + * @param string $name Element name + * @access public + * @return void + */ + function setName($name) + { + $this->updateAttributes(array('name'=>$name)); + } //end func setName + + // }}} + // {{{ getName() + + /** + * Returns the element name + * + * @access public + * @return string + */ + function getName() + { + return $this->getAttribute('name'); + } //end func getName + + // }}} + // {{{ setText() + + /** + * Sets the text + * + * @param string $text + * @access public + * @return void + */ + function setText($text) + { + $this->_text = $text; + } // end func setText + + // }}} + // {{{ setValue() + + /** + * Sets the text (uses the standard setValue call to emulate a form element. + * + * @param string $text + * @access public + * @return void + */ + function setValue($text) + { + $this->setText($text); + } // end func setValue + + // }}} + // {{{ toHtml() + + /** + * Returns the static text element in HTML + * + * @access public + * @return string + */ + function toHtml() + { + return $this->_getTabs() . $this->_text; + } //end func toHtml + + // }}} + // {{{ getFrozenHtml() + + /** + * Returns the value of field without HTML tags + * + * @access public + * @return string + */ + function getFrozenHtml() + { + return $this->toHtml(); + } //end func getFrozenHtml + + // }}} + // {{{ onQuickFormEvent() + + /** + * Called by HTML_QuickForm whenever form event is made on this element + * + * @param string $event Name of event + * @param mixed $arg event arguments + * @param object $caller calling object + * @since 1.0 + * @access public + * @return void + * @throws + */ + function onQuickFormEvent($event, $arg, &$caller) + { + switch ($event) { + case 'updateValue': + // do NOT use submitted values for static elements + $value = $this->_findValue($caller->_constantValues); + if (null === $value) { + $value = $this->_findValue($caller->_defaultValues); + } + if (null !== $value) { + $this->setValue($value); + } + break; + default: + parent::onQuickFormEvent($event, $arg, $caller); + } + return true; + } // end func onQuickFormEvent + + // }}} + // {{{ exportValue() + + /** + * We override this here because we don't want any values from static elements + */ + function exportValue(&$submitValues, $assoc = false) + { + return null; + } + + // }}} +} //end class HTML_QuickForm_static +?> diff --git a/lib/pear/HTML/QuickForm/submit.php b/lib/pear/HTML/QuickForm/submit.php new file mode 100644 index 0000000000..e1d3e7d26e --- /dev/null +++ b/lib/pear/HTML/QuickForm/submit.php @@ -0,0 +1,82 @@ + | +// | Bertrand Mansion | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once("HTML/QuickForm/input.php"); + +/** + * HTML class for a submit type element + * + * @author Adam Daniel + * @author Bertrand Mansion + * @version 1.0 + * @since PHP4.04pl1 + * @access public + */ +class HTML_QuickForm_submit extends HTML_QuickForm_input +{ + // {{{ constructor + + /** + * Class constructor + * + * @param string Input field name attribute + * @param string Input field value + * @param mixed Either a typical HTML attribute string or an associative array + * @since 1.0 + * @access public + * @return void + */ + function HTML_QuickForm_submit($elementName=null, $value=null, $attributes=null) + { + HTML_QuickForm_input::HTML_QuickForm_input($elementName, null, $attributes); + $this->setValue($value); + $this->setType('submit'); + } //end constructor + + // }}} + // {{{ freeze() + + /** + * Freeze the element so that only its value is returned + * + * @access public + * @return void + */ + function freeze() + { + return false; + } //end func freeze + + // }}} + // {{{ exportValue() + + /** + * Only return the value if it is found within $submitValues (i.e. if + * this particular submit button was clicked) + */ + function exportValue(&$submitValues, $assoc = false) + { + return $this->_prepareValue($this->_findValue($submitValues), $assoc); + } + + // }}} +} //end class HTML_QuickForm_submit +?> diff --git a/lib/pear/HTML/QuickForm/text.php b/lib/pear/HTML/QuickForm/text.php new file mode 100644 index 0000000000..9d6d9dea4f --- /dev/null +++ b/lib/pear/HTML/QuickForm/text.php @@ -0,0 +1,91 @@ + | +// | Bertrand Mansion | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once("HTML/QuickForm/input.php"); + +/** + * HTML class for a text field + * + * @author Adam Daniel + * @author Bertrand Mansion + * @version 1.0 + * @since PHP4.04pl1 + * @access public + */ +class HTML_QuickForm_text extends HTML_QuickForm_input +{ + + // {{{ constructor + + /** + * Class constructor + * + * @param string $elementName (optional)Input field name attribute + * @param string $elementLabel (optional)Input field label + * @param mixed $attributes (optional)Either a typical HTML attribute string + * or an associative array + * @since 1.0 + * @access public + * @return void + */ + function HTML_QuickForm_text($elementName=null, $elementLabel=null, $attributes=null) + { + HTML_QuickForm_input::HTML_QuickForm_input($elementName, $elementLabel, $attributes); + $this->_persistantFreeze = true; + $this->setType('text'); + } //end constructor + + // }}} + // {{{ setSize() + + /** + * Sets size of text field + * + * @param string $size Size of text field + * @since 1.3 + * @access public + * @return void + */ + function setSize($size) + { + $this->updateAttributes(array('size'=>$size)); + } //end func setSize + + // }}} + // {{{ setMaxlength() + + /** + * Sets maxlength of text field + * + * @param string $maxlength Maximum length of text field + * @since 1.3 + * @access public + * @return void + */ + function setMaxlength($maxlength) + { + $this->updateAttributes(array('maxlength'=>$maxlength)); + } //end func setMaxlength + + // }}} + +} //end class HTML_QuickForm_text +?> diff --git a/lib/pear/HTML/QuickForm/textarea.php b/lib/pear/HTML/QuickForm/textarea.php new file mode 100644 index 0000000000..7aa0af2ad6 --- /dev/null +++ b/lib/pear/HTML/QuickForm/textarea.php @@ -0,0 +1,222 @@ + | +// | Bertrand Mansion | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once("HTML/QuickForm/element.php"); + +/** + * HTML class for a textarea type field + * + * @author Adam Daniel + * @author Bertrand Mansion + * @version 1.0 + * @since PHP4.04pl1 + * @access public + */ +class HTML_QuickForm_textarea extends HTML_QuickForm_element +{ + // {{{ properties + + /** + * Field value + * @var string + * @since 1.0 + * @access private + */ + var $_value = null; + + // }}} + // {{{ constructor + + /** + * Class constructor + * + * @param string Input field name attribute + * @param mixed Label(s) for a field + * @param mixed Either a typical HTML attribute string or an associative array + * @since 1.0 + * @access public + * @return void + */ + function HTML_QuickForm_textarea($elementName=null, $elementLabel=null, $attributes=null) + { + HTML_QuickForm_element::HTML_QuickForm_element($elementName, $elementLabel, $attributes); + $this->_persistantFreeze = true; + $this->_type = 'textarea'; + } //end constructor + + // }}} + // {{{ setName() + + /** + * Sets the input field name + * + * @param string $name Input field name attribute + * @since 1.0 + * @access public + * @return void + */ + function setName($name) + { + $this->updateAttributes(array('name'=>$name)); + } //end func setName + + // }}} + // {{{ getName() + + /** + * Returns the element name + * + * @since 1.0 + * @access public + * @return string + */ + function getName() + { + return $this->getAttribute('name'); + } //end func getName + + // }}} + // {{{ setValue() + + /** + * Sets value for textarea element + * + * @param string $value Value for textarea element + * @since 1.0 + * @access public + * @return void + */ + function setValue($value) + { + $this->_value = $value; + } //end func setValue + + // }}} + // {{{ getValue() + + /** + * Returns the value of the form element + * + * @since 1.0 + * @access public + * @return string + */ + function getValue() + { + return $this->_value; + } // end func getValue + + // }}} + // {{{ setWrap() + + /** + * Sets wrap type for textarea element + * + * @param string $wrap Wrap type + * @since 1.0 + * @access public + * @return void + */ + function setWrap($wrap) + { + $this->updateAttributes(array('wrap' => $wrap)); + } //end func setWrap + + // }}} + // {{{ setRows() + + /** + * Sets height in rows for textarea element + * + * @param string $rows Height expressed in rows + * @since 1.0 + * @access public + * @return void + */ + function setRows($rows) + { + $this->updateAttributes(array('rows' => $rows)); + } //end func setRows + + // }}} + // {{{ setCols() + + /** + * Sets width in cols for textarea element + * + * @param string $cols Width expressed in cols + * @since 1.0 + * @access public + * @return void + */ + function setCols($cols) + { + $this->updateAttributes(array('cols' => $cols)); + } //end func setCols + + // }}} + // {{{ toHtml() + + /** + * Returns the textarea element in HTML + * + * @since 1.0 + * @access public + * @return string + */ + function toHtml() + { + if ($this->_flagFrozen) { + return $this->getFrozenHtml(); + } else { + return $this->_getTabs() . + '_getAttrString($this->_attributes) . '>' . + // because we wrap the form later we don't want the text indented + preg_replace("/(\r\n|\n|\r)/", ' ', htmlspecialchars($this->_value)) . + ''; + } + } //end func toHtml + + // }}} + // {{{ getFrozenHtml() + + /** + * Returns the value of field without HTML tags (in this case, value is changed to a mask) + * + * @since 1.0 + * @access public + * @return string + */ + function getFrozenHtml() + { + $value = htmlspecialchars($this->getValue()); + if ($this->getAttribute('wrap') == 'off') { + $html = $this->_getTabs() . '
' . $value."
\n"; + } else { + $html = nl2br($value)."\n"; + } + return $html . $this->_getPersistantData(); + } //end func getFrozenHtml + + // }}} + +} //end class HTML_QuickForm_textarea +?> diff --git a/lib/pear/HTML/QuickForm/xbutton.php b/lib/pear/HTML/QuickForm/xbutton.php new file mode 100644 index 0000000000..dd01995b21 --- /dev/null +++ b/lib/pear/HTML/QuickForm/xbutton.php @@ -0,0 +1,145 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once 'HTML/QuickForm/element.php'; + +/** + * Class for HTML 4.0 tags) + * @param mixed Either a typical HTML attribute string or an associative array + * @access public + */ + function HTML_QuickForm_xbutton($elementName = null, $elementContent = null, $attributes = null) + { + $this->HTML_QuickForm_element($elementName, null, $attributes); + $this->setContent($elementContent); + $this->setPersistantFreeze(false); + $this->_type = 'xbutton'; + } + + + function toHtml() + { + return 'getAttributes(true) . '>' . $this->_content . ''; + } + + + function getFrozenHtml() + { + return $this->toHtml(); + } + + + function freeze() + { + return false; + } + + + function setName($name) + { + $this->updateAttributes(array( + 'name' => $name + )); + } + + + function getName() + { + return $this->getAttribute('name'); + } + + + function setValue($value) + { + $this->updateAttributes(array( + 'value' => $value + )); + } + + + function getValue() + { + return $this->getAttribute('value'); + } + + + /** + * Sets the contents of the button element + * + * @param string Button content (HTML to add between tags) + */ + function setContent($content) + { + $this->_content = $content; + } + + + function onQuickFormEvent($event, $arg, &$caller) + { + if ('updateValue' != $event) { + return parent::onQuickFormEvent($event, $arg, $caller); + } else { + $value = $this->_findValue($caller->_constantValues); + if (null === $value) { + $value = $this->_findValue($caller->_defaultValues); + } + if (null !== $value) { + $this->setValue($value); + } + } + return true; + } + + + /** + * Returns a 'safe' element's value + * + * The value is only returned if the button's type is "submit" and if this + * particlular button was clicked + */ + function exportValue(&$submitValues, $assoc = false) + { + if ('submit' == $this->getAttribute('type')) { + return $this->_prepareValue($this->_findValue($submitValues), $assoc); + } else { + return null; + } + } +} +?> diff --git a/theme/chameleon/config.php b/theme/chameleon/config.php index cff6731992..638888890e 100644 --- a/theme/chameleon/config.php +++ b/theme/chameleon/config.php @@ -13,7 +13,7 @@ $THEME->sheets = array('user_styles'); //////////////////////////////////////////////////////////////////////////////// -$THEME->standardsheets = array('styles_layout'); +$THEME->standardsheets = array('styles_layout','styles_form'); /// This variable can be set to an array containing /// filenames from the *STANDARD* theme. If the diff --git a/theme/formal_white/config.php b/theme/formal_white/config.php index 5360e9a4a7..2f60222e9f 100644 --- a/theme/formal_white/config.php +++ b/theme/formal_white/config.php @@ -12,7 +12,7 @@ $THEME->sheets = array('fw_layout','fw_color','fw_fonts'); /// stylesheet files you want included in this theme, and in what order //////////////////////////////////////////////////////////////////////////////// -$THEME->standardsheets = array('styles_layout'); +$THEME->standardsheets = array('styles_layout','styles_form'); ///$THEME->standardsheets = true; /// This variable can be set to an array containing diff --git a/theme/metal/config.php b/theme/metal/config.php index de75eb0a9e..50c3b70d1d 100644 --- a/theme/metal/config.php +++ b/theme/metal/config.php @@ -13,7 +13,7 @@ $THEME->sheets = array('gradients', 'colors', 'fonts'); //////////////////////////////////////////////////////////////////////////////// -$THEME->standardsheets = array('styles_layout','styles_fonts','styles_color'); +$THEME->standardsheets = array('styles_layout','styles_fonts','styles_color','styles_form'); /// This variable can be set to an array containing /// filenames from the *STANDARD* theme. If the diff --git a/theme/orangewhite/config.php b/theme/orangewhite/config.php index d133053741..486acc64f1 100644 --- a/theme/orangewhite/config.php +++ b/theme/orangewhite/config.php @@ -13,7 +13,7 @@ $THEME->sheets = array('styles_layout', 'styles_fonts', 'styles_color', 'styles_ //////////////////////////////////////////////////////////////////////////////// -$THEME->standardsheets = array('styles_layout'); +$THEME->standardsheets = array('styles_layout','styles_form'); /// This variable can be set to an array containing /// filenames from the *STANDARD* theme. If the diff --git a/theme/orangewhitepda/config.php b/theme/orangewhitepda/config.php index ef3afa19f8..312d5a1f15 100644 --- a/theme/orangewhitepda/config.php +++ b/theme/orangewhitepda/config.php @@ -13,7 +13,7 @@ $THEME->sheets = array('styles_pda'); //////////////////////////////////////////////////////////////////////////////// -$THEME->standardsheets = array('styles_layout'); +$THEME->standardsheets = array('styles_layout','styles_form'); /// This variable can be set to an array containing /// filenames from the *STANDARD* theme. If the diff --git a/theme/standard/config.php b/theme/standard/config.php index 8628ae95b9..473b6aa012 100644 --- a/theme/standard/config.php +++ b/theme/standard/config.php @@ -6,7 +6,7 @@ //////////////////////////////////////////////////////////////////////////////// -$THEME->sheets = array('styles_layout', 'styles_fonts', 'styles_color'); +$THEME->sheets = array('styles_layout', 'styles_fonts', 'styles_color','styles_form'); /// This variable is an array containing the names of all the /// stylesheet files you want included in this theme, and in what order diff --git a/theme/standard/styles_form.css b/theme/standard/styles_form.css new file mode 100644 index 0000000000..3f7118991a --- /dev/null +++ b/theme/standard/styles_form.css @@ -0,0 +1,62 @@ +/******************************************************************* + styles_form.css + + This CSS file contains all css required for new css and xhtml only + moodleforms. +*/ +form.mform { + margin: 0; + padding: 0; + width: 100%; +} +form.mform fieldset { + border: 1px solid black; + padding: 10px 0; + margin: 0; + width:100%; +} +form.mform fieldset.hidden { + border: 0; +} +form.mform fieldset legend { + font-weight: bold; +} +form.mform label { + margin: 0 0 0 5px; +} +form.mform label.qflabel { + display: block; + float: left; + width: 40%; + padding: 0; + margin: 5px 0 0 0; + text-align: right; +} + +form.mform input, form.mform select { + width: auto; +} +form.mform textarea { + } +form.mform br { + clear: left; +} +form.mform div.qfelement { + display: inline; + float: left; + margin: 5px 0 0 10px; + padding: 0; +} + +form.mform div.qfelementwide { + margin: 0 10% 10px 10%; +} + +form.mform span.error, form.mform span.required { + color: red; +} +form.mform div.error { + border: 1px solid red; + padding: 5px; + color: inherit; +} \ No newline at end of file diff --git a/theme/standardblue/config.php b/theme/standardblue/config.php index 68f9af7b47..842e1707e1 100644 --- a/theme/standardblue/config.php +++ b/theme/standardblue/config.php @@ -13,7 +13,7 @@ $THEME->sheets = array('gradients'); //////////////////////////////////////////////////////////////////////////////// -$THEME->standardsheets = array('styles_layout','styles_fonts','styles_color','styles_moz'); +$THEME->standardsheets = array('styles_layout','styles_fonts','styles_color','styles_moz','styles_form'); /// This variable can be set to an array containing /// filenames from the *STANDARD* theme. If the diff --git a/theme/standardgreen/config.php b/theme/standardgreen/config.php index 68f9af7b47..842e1707e1 100644 --- a/theme/standardgreen/config.php +++ b/theme/standardgreen/config.php @@ -13,7 +13,7 @@ $THEME->sheets = array('gradients'); //////////////////////////////////////////////////////////////////////////////// -$THEME->standardsheets = array('styles_layout','styles_fonts','styles_color','styles_moz'); +$THEME->standardsheets = array('styles_layout','styles_fonts','styles_color','styles_moz','styles_form'); /// This variable can be set to an array containing /// filenames from the *STANDARD* theme. If the diff --git a/theme/standardlogo/config.php b/theme/standardlogo/config.php index ba954c3c4b..54e17b2d8f 100644 --- a/theme/standardlogo/config.php +++ b/theme/standardlogo/config.php @@ -13,7 +13,7 @@ $THEME->sheets = array('gradients'); //////////////////////////////////////////////////////////////////////////////// -$THEME->standardsheets = array('styles_layout','styles_fonts','styles_color','styles_moz'); +$THEME->standardsheets = array('styles_layout','styles_fonts','styles_color','styles_moz','styles_form'); /// This variable can be set to an array containing /// filenames from the *STANDARD* theme. If the diff --git a/theme/standardred/config.php b/theme/standardred/config.php index 68f9af7b47..842e1707e1 100644 --- a/theme/standardred/config.php +++ b/theme/standardred/config.php @@ -13,7 +13,7 @@ $THEME->sheets = array('gradients'); //////////////////////////////////////////////////////////////////////////////// -$THEME->standardsheets = array('styles_layout','styles_fonts','styles_color','styles_moz'); +$THEME->standardsheets = array('styles_layout','styles_fonts','styles_color','styles_moz','styles_form'); /// This variable can be set to an array containing /// filenames from the *STANDARD* theme. If the diff --git a/theme/standardwhite/config.php b/theme/standardwhite/config.php index 68f9af7b47..842e1707e1 100644 --- a/theme/standardwhite/config.php +++ b/theme/standardwhite/config.php @@ -13,7 +13,7 @@ $THEME->sheets = array('gradients'); //////////////////////////////////////////////////////////////////////////////// -$THEME->standardsheets = array('styles_layout','styles_fonts','styles_color','styles_moz'); +$THEME->standardsheets = array('styles_layout','styles_fonts','styles_color','styles_moz','styles_form'); /// This variable can be set to an array containing /// filenames from the *STANDARD* theme. If the diff --git a/theme/wood/config.php b/theme/wood/config.php index ac4a4fa427..7fce930211 100644 --- a/theme/wood/config.php +++ b/theme/wood/config.php @@ -13,7 +13,7 @@ $THEME->sheets = array('styles_color'); //////////////////////////////////////////////////////////////////////////////// -$THEME->standardsheets = array('styles_layout','styles_fonts','styles_color'); +$THEME->standardsheets = array('styles_layout','styles_fonts','styles_color','styles_form'); /// This variable can be set to an array containing /// filenames from the *STANDARD* theme. If the -- 2.39.5