diff options
-rw-r--r-- | inc/Form/CheckableElement.php | 62 | ||||
-rw-r--r-- | inc/Form/Element.php | 142 | ||||
-rw-r--r-- | inc/Form/Form.php | 167 | ||||
-rw-r--r-- | inc/Form/InputElement.php | 145 | ||||
-rw-r--r-- | inc/Form/Label.php | 46 | ||||
-rw-r--r-- | inc/Form/TextareaElement.php | 50 |
6 files changed, 612 insertions, 0 deletions
diff --git a/inc/Form/CheckableElement.php b/inc/Form/CheckableElement.php new file mode 100644 index 000000000..a57bbc1f6 --- /dev/null +++ b/inc/Form/CheckableElement.php @@ -0,0 +1,62 @@ +<?php +namespace dokuwiki\Form; + +/** + * Class CheckableElement + * + * For Radio- and Checkboxes + * + * @package DokuForm + */ +class CheckableElement extends InputElement { + + /** + * @param string $type The type of this element + * @param string $name The name of this form element + * @param string $label The label text for this element + */ + public function __construct($type, $name, $label) { + parent::__construct($type, $name, $label); + // default value is 1 + $this->attr('value', 1); + } + + /** + * Handles the useInput flag and sets the checked attribute accordingly + */ + protected function prefillInput() { + global $INPUT; + list($name, $key) = $this->getInputName(); + $myvalue = $this->val(); + + if(!$INPUT->has($name)) return; + + if($key === null) { + // no key - single value + $value = $INPUT->str($name); + if($value == $myvalue) { + $this->attr('checked', 'checked'); + } else { + $this->rmattr('checked'); + } + } else { + // we have an array, there might be several values in it + $input = $INPUT->arr($name); + if(isset($input[$key])) { + $this->rmattr('checked'); + + // values seem to be in another sub array + if(is_array($input[$key])) { + $input = $input[$key]; + } + + foreach($input as $value) { + if($value == $myvalue) { + $this->attr('checked', 'checked'); + } + } + } + } + } + +} diff --git a/inc/Form/Element.php b/inc/Form/Element.php new file mode 100644 index 000000000..6f23e2631 --- /dev/null +++ b/inc/Form/Element.php @@ -0,0 +1,142 @@ +<?php +namespace dokuwiki\Form; + + +/** + * Class Element + * + * The basic building block of a form + * + * @package dokuwiki\Form + */ +abstract class Element { + + /** + * @var array the attributes of this element + */ + protected $attributes = array(); + + /** + * @var string The type of this element + */ + protected $type; + + /** + * @param string $type The type of this element + * @param array $attributes + */ + public function __construct($type, $attributes = array()) { + $this->type = $type; + $this->attributes = $attributes; + } + + /** + * Gets or sets an attribute + * + * When no $value is given, the current content of the attribute is returned. + * An empty string is returned for unset attributes. + * + * When a $value is given, the content is set to that value and the Element + * itself is returned for easy chaining + * + * @param string $name Name of the attribute to access + * @param null|string $value New value to set + * @return string|$this + */ + public function attr($name, $value = null) { + // set + if($value !== null) { + $this->attributes[$name] = $value; + return $this; + } + + // get + if(isset($this->attributes[$name])) { + return $this->attributes[$name]; + } else { + return ''; + } + } + + /** + * Removes the given attribute if it exists + * + * @param $name + * @return $this + */ + public function rmattr($name) { + if(isset($this->attributes[$name])) { + unset($this->attributes[$name]); + } + return $this; + } + + /** + * Gets or adds a all given attributes at once + * + * @param array|null $attributes + * @return array|$this + */ + public function attrs($attributes = null) { + // set + if($attributes) { + foreach((array) $attributes as $key => $val) { + $this->attr($key, $val); + } + return $this; + } + // get + return $this->attributes; + } + + /** + * Adds a class to the class attribute + * + * This is the preferred method of setting the element's class + * + * @param string $class the new class to add + * @return $this + */ + public function addClass($class) { + $classes = explode(' ', $this->attr('class')); + $classes[] = $class; + $classes = array_unique($classes); + $this->attr('class', join(' ', $classes)); + return $this; + } + + /** + * Get or set the element's ID + * + * This is the preferred way of setting the element's ID + * + * @param null|string $id + * @return string|$this + */ + public function id($id = null) { + if(strpos($id, '__') === false) { + throw new \InvalidArgumentException('IDs in DokuWiki have to contain two subsequent underscores'); + } + + return $this->attr('id', $id); + } + + /** + * Get or set the element's value + * + * This is the preferred way of setting the element's value + * + * @param null|string $value + * @return string|$this + */ + public function val($value = null) { + return $this->attr('value', $value); + } + + /** + * The HTML representation of this element + * + * @return string + */ + abstract public function toHTML(); +} diff --git a/inc/Form/Form.php b/inc/Form/Form.php new file mode 100644 index 000000000..3a8b590e7 --- /dev/null +++ b/inc/Form/Form.php @@ -0,0 +1,167 @@ +<?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 + */ + public function __construct($attributes = array()) { + 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); + $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 + $this->setHiddenField('sectok', getSecurityToken()); + + // identify this as a form2 based form in HTML + $this->addClass('doku_form2'); + } + + /** + * Sets a hidden field + * + * @param $name + * @param $value + * @return $this + */ + public function setHiddenField($name, $value) { + $this->hidden[$name] = $value; + return $this; + } + + /** + * Adds an element to the end of 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, 'Doku_Form2')) 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; + } + + /** + * Adds a text input field + * + * @param $name + * @param $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 $name + * @param $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 $name + * @param $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 $name + * @param $label + * @param int $pos + * @return CheckableElement + */ + public function addCheckbox($name, $label, $pos = -1) { + return $this->addElement(new CheckableElement('checkbox', $name, $label), $pos); + } + + /** + * Adds a textarea field + * + * @param $name + * @param $label + * @param int $pos + * @return TextareaElement + */ + public function addTextarea($name, $label, $pos = -1) { + return $this->addElement(new TextareaElement($name, $label), $pos); + } + + /** + * The HTML representation of the whole form + * + * @return string + */ + public function toHTML() { + $html = '<form ' . buildAttributes($this->attrs()) . '>' . DOKU_LF; + + foreach($this->hidden as $name => $value) { + $html .= '<input type="hidden" name="' . $name . '" value="' . formText($value) . '" />' . DOKU_LF; + } + + foreach($this->elements as $element) { + $html .= $element->toHTML() . DOKU_LF; + } + + $html .= '</form>' . DOKU_LF; + + return $html; + } +} diff --git a/inc/Form/InputElement.php b/inc/Form/InputElement.php new file mode 100644 index 000000000..59310174e --- /dev/null +++ b/inc/Form/InputElement.php @@ -0,0 +1,145 @@ +<?php +namespace dokuwiki\Form; + +/** + * Class InputElement + * + * Base class for all input elements. Uses a wrapping label. + * + * @todo figure out how to make wrapping or related label configurable + * @package dokuwiki\Form + */ +class InputElement extends Element { + /** + * @var Label + */ + protected $label; + + /** + * @var bool if the element should reflect posted values + */ + protected $useInput = true; + + /** + * @param string $type The type of this element + * @param string $name The name of this form element + * @param string $label The label text for this element + */ + public function __construct($type, $name, $label) { + parent::__construct($type, array('name' => $name)); + $this->attr('name', $name); + } + + /** + * Should the user sent input be used to initialize the input field + * + * The default is true. Any set values will be overwritten by the INPUT + * provided values. + * + * @param bool $useinput + * @return $this + */ + public function useInput($useinput) { + $this->useInput = (bool) $useinput; + return $this; + } + + /** + * Get or set the element's ID + * + * @param null|string $id + * @return string|$this + */ + public function id($id = null) { + $this->label->attr('for', $id); + return parent::id($id); + } + + /** + * Adds a class to the class attribute + * + * This is the preferred method of setting the element's class + * + * @param string $class the new class to add + * @return $this + */ + public function addClass($class) { + $this->label->addClass($class); + return parent::addClass($class); + } + + /** + * Figures out how to access the value for this field from INPUT data + * + * The element's name could have been given as a simple string ('foo') + * or in array notation ('foo[bar]'). + * + * Note: this function only handles one level of arrays. If your data + * is nested deeper, you should call useInput(false) and set the + * correct value yourself + * + * @return array name and array key (null if not an array) + */ + protected function getInputName() { + $name = $this->attr('name'); + parse_str("$name=1", $parsed); + + $name = array_keys($parsed); + $name = array_shift($name); + + if(is_array($parsed[$name])) { + $key = array_keys($parsed[$name]); + $key = array_shift($key); + } else { + $key = null; + } + + return array($name, $key); + } + + /** + * Handles the useInput flag and set the value attribute accordingly + */ + protected function prefillInput() { + global $INPUT; + + list($name, $key) = $this->getInputName(); + if(!$INPUT->has($name)) return; + + if($key === null) { + $value = $INPUT->str($name); + } else { + $value = $INPUT->arr($name); + if(isset($value[$key])) { + $value = $value[$key]; + } else { + $value = ''; + } + } + if($value !== '') { + $this->val($value); + } + } + + /** + * The HTML representation of this element + * + * @return string + */ + protected function mainElementHTML() { + if($this->useInput) $this->prefillInput(); + return '<input ' . buildAttributes($this->attrs()) . ' />'; + } + + /** + * The HTML representation of this element wrapped in a label + * + * @return string + */ + public function toHTML() { + return '<label ' . buildAttributes($this->label->attrs()) . '>' . DOKU_LF . + '<span>' . hsc($this->label->label) . '</span>' . DOKU_LF . + $this->mainElementHTML() . DOKU_LF . + '</label>'; + } +} diff --git a/inc/Form/Label.php b/inc/Form/Label.php new file mode 100644 index 000000000..c8a862613 --- /dev/null +++ b/inc/Form/Label.php @@ -0,0 +1,46 @@ +<?php +namespace dokuwiki\Form; + +/** + * Class Label + * @package dokuwiki\Form + */ +class Label extends Element { + /** + * @var string the actual label text + */ + public $label = ''; + + /** + * Creates a new Label + * + * @param string $label + */ + public function __construct($label) { + parent::__construct('label'); + $this->label = $label; + } + + /** + * Get or set the element's label text + * + * @param null|string $value + * @return string|$this + */ + public function val($value = null) { + if($value !== null) { + $this->label = $value; + return $this; + } + return $this->label; + } + + /** + * The HTML representation of this element + * + * @return string + */ + public function toHTML() { + return '<label ' . buildAttributes($this->attrs()) . '>' . hsc($this->label) . '</label>'; + } +} diff --git a/inc/Form/TextareaElement.php b/inc/Form/TextareaElement.php new file mode 100644 index 000000000..a8486266f --- /dev/null +++ b/inc/Form/TextareaElement.php @@ -0,0 +1,50 @@ +<?php +namespace dokuwiki\Form; + +/** + * Class TextareaElement + * @package dokuwiki\Form + */ +class TextareaElement extends InputElement { + + /** + * @var string the actual text within the area + */ + protected $text; + + /** + * @param string $name The name of this form element + * @param string $label The label text for this element + */ + public function __construct($name, $label) { + parent::__construct('textarea', $name, $label); + } + + /** + * Get or set the element's value + * + * This is the preferred way of setting the element's value + * + * @param null|string $value + * @return string|$this + */ + public function val($value = null) { + if($value !== null) { + $this->text = $value; + return $this; + } + return $this->text; + } + + /** + * The HTML representation of this element + * + * @return string + */ + protected function mainElementHTML() { + if($this->useInput) $this->prefillInput(); + return '<textarea ' . buildAttributes($this->attrs()) . '>' . + formText($this->val()) . '</textarea>'; + } + +} |