123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- <?php
- namespace dokuwiki\Form;
- use dokuwiki\Extension\Event;
- /**
- * 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;
- } elseif ($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
- *
- * @param string $eventName (optional) name of the event: FORM_{$name}_OUTPUT
- * @return string
- */
- public function toHTML($eventName = null)
- {
- $this->balanceFieldsets();
- // trigger event to provide an opportunity to modify this form
- if (isset($eventName)) {
- $eventName = 'FORM_'.strtoupper($eventName).'_OUTPUT';
- Event::createAndTrigger($eventName, $this, null, false);
- }
- $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;
- }
- }
|