summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Gohr <andi@splitbrain.org>2015-05-08 16:15:16 +0200
committerAndreas Gohr <andi@splitbrain.org>2015-05-08 16:15:16 +0200
commit12a4e4d1ed827c59290838d5a11d75ad32aa28f1 (patch)
tree0fa3fe9326391a24c5eef384b4882580bab3a2e1
parentcffb4528cea1b9e7e03dc724aaa9719dbd6e23c9 (diff)
downloadrpg-12a4e4d1ed827c59290838d5a11d75ad32aa28f1.tar.gz
rpg-12a4e4d1ed827c59290838d5a11d75ad32aa28f1.tar.bz2
start of rewriting the form handling
This is jsut a small beginning. Not all elements are there, yet. It's also completely untested so far.
-rw-r--r--inc/Form/CheckableElement.php62
-rw-r--r--inc/Form/Element.php142
-rw-r--r--inc/Form/Form.php167
-rw-r--r--inc/Form/InputElement.php145
-rw-r--r--inc/Form/Label.php46
-rw-r--r--inc/Form/TextareaElement.php50
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>';
+ }
+
+}