diff options
Diffstat (limited to 'platform/www/inc/Form/Form.php')
-rw-r--r-- | platform/www/inc/Form/Form.php | 462 |
1 files changed, 462 insertions, 0 deletions
diff --git a/platform/www/inc/Form/Form.php b/platform/www/inc/Form/Form.php new file mode 100644 index 0000000..c741a69 --- /dev/null +++ b/platform/www/inc/Form/Form.php @@ -0,0 +1,462 @@ +<?php +namespace dokuwiki\Form; + +/** + * Class Form + * + * Represents the whole Form. This is what you work on, and add Elements to + * + * @package dokuwiki\Form + */ +class Form extends Element { + + /** + * @var array name value pairs for hidden values + */ + protected $hidden = array(); + + /** + * @var Element[] the elements of the form + */ + protected $elements = array(); + + /** + * Creates a new, empty form with some default attributes + * + * @param array $attributes + * @param bool $unsafe if true, then the security token is ommited + */ + public function __construct($attributes = array(), $unsafe = false) { + global $ID; + + parent::__construct('form', $attributes); + + // use the current URL as default action + if(!$this->attr('action')) { + $get = $_GET; + if(isset($get['id'])) unset($get['id']); + $self = wl($ID, $get, false, '&'); //attributes are escaped later + $this->attr('action', $self); + } + + // post is default + if(!$this->attr('method')) { + $this->attr('method', 'post'); + } + + // we like UTF-8 + if(!$this->attr('accept-charset')) { + $this->attr('accept-charset', 'utf-8'); + } + + // add the security token by default + if (!$unsafe) { + $this->setHiddenField('sectok', getSecurityToken()); + } + + // identify this as a new form based form in HTML + $this->addClass('doku_form'); + } + + /** + * Sets a hidden field + * + * @param string $name + * @param string $value + * @return $this + */ + public function setHiddenField($name, $value) { + $this->hidden[$name] = $value; + return $this; + } + + #region element query function + + /** + * Returns the numbers of elements in the form + * + * @return int + */ + public function elementCount() { + return count($this->elements); + } + + /** + * Get the position of the element in the form or false if it is not in the form + * + * Warning: This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates + * to FALSE. Please read the section on Booleans for more information. Use the === operator for testing the + * return value of this function. + * + * @param Element $element + * + * @return false|int + */ + public function getElementPosition(Element $element) + { + return array_search($element, $this->elements, true); + } + + /** + * Returns a reference to the element at a position. + * A position out-of-bounds will return either the + * first (underflow) or last (overflow) element. + * + * @param int $pos + * @return Element + */ + public function getElementAt($pos) { + if($pos < 0) $pos = count($this->elements) + $pos; + if($pos < 0) $pos = 0; + if($pos >= count($this->elements)) $pos = count($this->elements) - 1; + return $this->elements[$pos]; + } + + /** + * Gets the position of the first of a type of element + * + * @param string $type Element type to look for. + * @param int $offset search from this position onward + * @return false|int position of element if found, otherwise false + */ + public function findPositionByType($type, $offset = 0) { + $len = $this->elementCount(); + for($pos = $offset; $pos < $len; $pos++) { + if($this->elements[$pos]->getType() == $type) { + return $pos; + } + } + return false; + } + + /** + * Gets the position of the first element matching the attribute + * + * @param string $name Name of the attribute + * @param string $value Value the attribute should have + * @param int $offset search from this position onward + * @return false|int position of element if found, otherwise false + */ + public function findPositionByAttribute($name, $value, $offset = 0) { + $len = $this->elementCount(); + for($pos = $offset; $pos < $len; $pos++) { + if($this->elements[$pos]->attr($name) == $value) { + return $pos; + } + } + return false; + } + + #endregion + + #region Element positioning functions + + /** + * Adds or inserts an element to the form + * + * @param Element $element + * @param int $pos 0-based position in the form, -1 for at the end + * @return Element + */ + public function addElement(Element $element, $pos = -1) { + if(is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException( + 'You can\'t add a form to a form' + ); + if($pos < 0) { + $this->elements[] = $element; + } else { + array_splice($this->elements, $pos, 0, array($element)); + } + return $element; + } + + /** + * Replaces an existing element with a new one + * + * @param Element $element the new element + * @param int $pos 0-based position of the element to replace + */ + public function replaceElement(Element $element, $pos) { + if(is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException( + 'You can\'t add a form to a form' + ); + array_splice($this->elements, $pos, 1, array($element)); + } + + /** + * Remove an element from the form completely + * + * @param int $pos 0-based position of the element to remove + */ + public function removeElement($pos) { + array_splice($this->elements, $pos, 1); + } + + #endregion + + #region Element adding functions + + /** + * Adds a text input field + * + * @param string $name + * @param string $label + * @param int $pos + * @return InputElement + */ + public function addTextInput($name, $label = '', $pos = -1) { + return $this->addElement(new InputElement('text', $name, $label), $pos); + } + + /** + * Adds a password input field + * + * @param string $name + * @param string $label + * @param int $pos + * @return InputElement + */ + public function addPasswordInput($name, $label = '', $pos = -1) { + return $this->addElement(new InputElement('password', $name, $label), $pos); + } + + /** + * Adds a radio button field + * + * @param string $name + * @param string $label + * @param int $pos + * @return CheckableElement + */ + public function addRadioButton($name, $label = '', $pos = -1) { + return $this->addElement(new CheckableElement('radio', $name, $label), $pos); + } + + /** + * Adds a checkbox field + * + * @param string $name + * @param string $label + * @param int $pos + * @return CheckableElement + */ + public function addCheckbox($name, $label = '', $pos = -1) { + return $this->addElement(new CheckableElement('checkbox', $name, $label), $pos); + } + + /** + * Adds a dropdown field + * + * @param string $name + * @param array $options + * @param string $label + * @param int $pos + * @return DropdownElement + */ + public function addDropdown($name, $options, $label = '', $pos = -1) { + return $this->addElement(new DropdownElement($name, $options, $label), $pos); + } + + /** + * Adds a textarea field + * + * @param string $name + * @param string $label + * @param int $pos + * @return TextareaElement + */ + public function addTextarea($name, $label = '', $pos = -1) { + return $this->addElement(new TextareaElement($name, $label), $pos); + } + + /** + * Adds a simple button, escapes the content for you + * + * @param string $name + * @param string $content + * @param int $pos + * @return Element + */ + public function addButton($name, $content, $pos = -1) { + return $this->addElement(new ButtonElement($name, hsc($content)), $pos); + } + + /** + * Adds a simple button, allows HTML for content + * + * @param string $name + * @param string $html + * @param int $pos + * @return Element + */ + public function addButtonHTML($name, $html, $pos = -1) { + return $this->addElement(new ButtonElement($name, $html), $pos); + } + + /** + * Adds a label referencing another input element, escapes the label for you + * + * @param string $label + * @param string $for + * @param int $pos + * @return Element + */ + public function addLabel($label, $for='', $pos = -1) { + return $this->addLabelHTML(hsc($label), $for, $pos); + } + + /** + * Adds a label referencing another input element, allows HTML for content + * + * @param string $content + * @param string|Element $for + * @param int $pos + * @return Element + */ + public function addLabelHTML($content, $for='', $pos = -1) { + $element = new LabelElement(hsc($content)); + + if(is_a($for, '\dokuwiki\Form\Element')) { + /** @var Element $for */ + $for = $for->id(); + } + $for = (string) $for; + if($for !== '') { + $element->attr('for', $for); + } + + return $this->addElement($element, $pos); + } + + /** + * Add fixed HTML to the form + * + * @param string $html + * @param int $pos + * @return HTMLElement + */ + public function addHTML($html, $pos = -1) { + return $this->addElement(new HTMLElement($html), $pos); + } + + /** + * Add a closed HTML tag to the form + * + * @param string $tag + * @param int $pos + * @return TagElement + */ + public function addTag($tag, $pos = -1) { + return $this->addElement(new TagElement($tag), $pos); + } + + /** + * Add an open HTML tag to the form + * + * Be sure to close it again! + * + * @param string $tag + * @param int $pos + * @return TagOpenElement + */ + public function addTagOpen($tag, $pos = -1) { + return $this->addElement(new TagOpenElement($tag), $pos); + } + + /** + * Add a closing HTML tag to the form + * + * Be sure it had been opened before + * + * @param string $tag + * @param int $pos + * @return TagCloseElement + */ + public function addTagClose($tag, $pos = -1) { + return $this->addElement(new TagCloseElement($tag), $pos); + } + + /** + * Open a Fieldset + * + * @param string $legend + * @param int $pos + * @return FieldsetOpenElement + */ + public function addFieldsetOpen($legend = '', $pos = -1) { + return $this->addElement(new FieldsetOpenElement($legend), $pos); + } + + /** + * Close a fieldset + * + * @param int $pos + * @return TagCloseElement + */ + public function addFieldsetClose($pos = -1) { + return $this->addElement(new FieldsetCloseElement(), $pos); + } + + #endregion + + /** + * Adjust the elements so that fieldset open and closes are matching + */ + protected function balanceFieldsets() { + $lastclose = 0; + $isopen = false; + $len = count($this->elements); + + for($pos = 0; $pos < $len; $pos++) { + $type = $this->elements[$pos]->getType(); + if($type == 'fieldsetopen') { + if($isopen) { + //close previous fieldset + $this->addFieldsetClose($pos); + $lastclose = $pos + 1; + $pos++; + $len++; + } + $isopen = true; + } else if($type == 'fieldsetclose') { + if(!$isopen) { + // make sure there was a fieldsetopen + // either right after the last close or at the begining + $this->addFieldsetOpen('', $lastclose); + $len++; + $pos++; + } + $lastclose = $pos; + $isopen = false; + } + } + + // close open fieldset at the end + if($isopen) { + $this->addFieldsetClose(); + } + } + + /** + * The HTML representation of the whole form + * + * @return string + */ + public function toHTML() { + $this->balanceFieldsets(); + + $html = '<form ' . buildAttributes($this->attrs()) . '>'; + + foreach($this->hidden as $name => $value) { + $html .= '<input type="hidden" name="' . $name . '" value="' . formText($value) . '" />'; + } + + foreach($this->elements as $element) { + $html .= $element->toHTML(); + } + + $html .= '</form>'; + + return $html; + } +} |