OpenApi Gen: add toString method for easier testing
[dokuwiki.git] / inc / Form / Form.php
blobae3145f65828d6f1b655a9097985ccd31f1ec9b7
1 <?php
3 namespace dokuwiki\Form;
5 use dokuwiki\Extension\Event;
7 /**
8 * Class Form
10 * Represents the whole Form. This is what you work on, and add Elements to
12 * @package dokuwiki\Form
14 class Form extends Element
16 /**
17 * @var array name value pairs for hidden values
19 protected $hidden = [];
21 /**
22 * @var Element[] the elements of the form
24 protected $elements = [];
26 /**
27 * Creates a new, empty form with some default attributes
29 * @param array $attributes
30 * @param bool $unsafe if true, then the security token is ommited
32 public function __construct($attributes = [], $unsafe = false)
34 global $ID;
36 parent::__construct('form', $attributes);
38 // use the current URL as default action
39 if (!$this->attr('action')) {
40 $get = $_GET;
41 if (isset($get['id'])) unset($get['id']);
42 $self = wl($ID, $get, false, '&'); //attributes are escaped later
43 $this->attr('action', $self);
46 // post is default
47 if (!$this->attr('method')) {
48 $this->attr('method', 'post');
51 // we like UTF-8
52 if (!$this->attr('accept-charset')) {
53 $this->attr('accept-charset', 'utf-8');
56 // add the security token by default
57 if (!$unsafe) {
58 $this->setHiddenField('sectok', getSecurityToken());
61 // identify this as a new form based form in HTML
62 $this->addClass('doku_form');
65 /**
66 * Sets a hidden field
68 * @param string $name
69 * @param string $value
70 * @return $this
72 public function setHiddenField($name, $value)
74 $this->hidden[$name] = $value;
75 return $this;
78 #region element query function
80 /**
81 * Returns the numbers of elements in the form
83 * @return int
85 public function elementCount()
87 return count($this->elements);
90 /**
91 * Get the position of the element in the form or false if it is not in the form
93 * Warning: This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates
94 * to FALSE. Please read the section on Booleans for more information. Use the === operator for testing the
95 * return value of this function.
97 * @param Element $element
99 * @return false|int
101 public function getElementPosition(Element $element)
103 return array_search($element, $this->elements, true);
107 * Returns a reference to the element at a position.
108 * A position out-of-bounds will return either the
109 * first (underflow) or last (overflow) element.
111 * @param int $pos
112 * @return Element
114 public function getElementAt($pos)
116 if ($pos < 0) $pos = count($this->elements) + $pos;
117 if ($pos < 0) $pos = 0;
118 if ($pos >= count($this->elements)) $pos = count($this->elements) - 1;
119 return $this->elements[$pos];
123 * Gets the position of the first of a type of element
125 * @param string $type Element type to look for.
126 * @param int $offset search from this position onward
127 * @return false|int position of element if found, otherwise false
129 public function findPositionByType($type, $offset = 0)
131 $len = $this->elementCount();
132 for ($pos = $offset; $pos < $len; $pos++) {
133 if ($this->elements[$pos]->getType() == $type) {
134 return $pos;
137 return false;
141 * Gets the position of the first element matching the attribute
143 * @param string $name Name of the attribute
144 * @param string $value Value the attribute should have
145 * @param int $offset search from this position onward
146 * @return false|int position of element if found, otherwise false
148 public function findPositionByAttribute($name, $value, $offset = 0)
150 $len = $this->elementCount();
151 for ($pos = $offset; $pos < $len; $pos++) {
152 if ($this->elements[$pos]->attr($name) == $value) {
153 return $pos;
156 return false;
159 #endregion
161 #region Element positioning functions
164 * Adds or inserts an element to the form
166 * @param Element $element
167 * @param int $pos 0-based position in the form, -1 for at the end
168 * @return Element
170 public function addElement(Element $element, $pos = -1)
172 if (is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException(
173 'You can\'t add a form to a form'
175 if ($pos < 0) {
176 $this->elements[] = $element;
177 } else {
178 array_splice($this->elements, $pos, 0, [$element]);
180 return $element;
184 * Replaces an existing element with a new one
186 * @param Element $element the new element
187 * @param int $pos 0-based position of the element to replace
189 public function replaceElement(Element $element, $pos)
191 if (is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException(
192 'You can\'t add a form to a form'
194 array_splice($this->elements, $pos, 1, [$element]);
198 * Remove an element from the form completely
200 * @param int $pos 0-based position of the element to remove
202 public function removeElement($pos)
204 array_splice($this->elements, $pos, 1);
207 #endregion
209 #region Element adding functions
212 * Adds a text input field
214 * @param string $name
215 * @param string $label
216 * @param int $pos
217 * @return InputElement
219 public function addTextInput($name, $label = '', $pos = -1)
221 return $this->addElement(new InputElement('text', $name, $label), $pos);
225 * Adds a password input field
227 * @param string $name
228 * @param string $label
229 * @param int $pos
230 * @return InputElement
232 public function addPasswordInput($name, $label = '', $pos = -1)
234 return $this->addElement(new InputElement('password', $name, $label), $pos);
238 * Adds a radio button field
240 * @param string $name
241 * @param string $label
242 * @param int $pos
243 * @return CheckableElement
245 public function addRadioButton($name, $label = '', $pos = -1)
247 return $this->addElement(new CheckableElement('radio', $name, $label), $pos);
251 * Adds a checkbox field
253 * @param string $name
254 * @param string $label
255 * @param int $pos
256 * @return CheckableElement
258 public function addCheckbox($name, $label = '', $pos = -1)
260 return $this->addElement(new CheckableElement('checkbox', $name, $label), $pos);
264 * Adds a dropdown field
266 * @param string $name
267 * @param array $options
268 * @param string $label
269 * @param int $pos
270 * @return DropdownElement
272 public function addDropdown($name, $options, $label = '', $pos = -1)
274 return $this->addElement(new DropdownElement($name, $options, $label), $pos);
278 * Adds a textarea field
280 * @param string $name
281 * @param string $label
282 * @param int $pos
283 * @return TextareaElement
285 public function addTextarea($name, $label = '', $pos = -1)
287 return $this->addElement(new TextareaElement($name, $label), $pos);
291 * Adds a simple button, escapes the content for you
293 * @param string $name
294 * @param string $content
295 * @param int $pos
296 * @return Element
298 public function addButton($name, $content, $pos = -1)
300 return $this->addElement(new ButtonElement($name, hsc($content)), $pos);
304 * Adds a simple button, allows HTML for content
306 * @param string $name
307 * @param string $html
308 * @param int $pos
309 * @return Element
311 public function addButtonHTML($name, $html, $pos = -1)
313 return $this->addElement(new ButtonElement($name, $html), $pos);
317 * Adds a label referencing another input element, escapes the label for you
319 * @param string $label
320 * @param string $for
321 * @param int $pos
322 * @return Element
324 public function addLabel($label, $for = '', $pos = -1)
326 return $this->addLabelHTML(hsc($label), $for, $pos);
330 * Adds a label referencing another input element, allows HTML for content
332 * @param string $content
333 * @param string|Element $for
334 * @param int $pos
335 * @return Element
337 public function addLabelHTML($content, $for = '', $pos = -1)
339 $element = new LabelElement($content);
341 if (is_a($for, '\dokuwiki\Form\Element')) {
342 /** @var Element $for */
343 $for = $for->id();
345 $for = (string) $for;
346 if ($for !== '') {
347 $element->attr('for', $for);
350 return $this->addElement($element, $pos);
354 * Add fixed HTML to the form
356 * @param string $html
357 * @param int $pos
358 * @return HTMLElement
360 public function addHTML($html, $pos = -1)
362 return $this->addElement(new HTMLElement($html), $pos);
366 * Add a closed HTML tag to the form
368 * @param string $tag
369 * @param int $pos
370 * @return TagElement
372 public function addTag($tag, $pos = -1)
374 return $this->addElement(new TagElement($tag), $pos);
378 * Add an open HTML tag to the form
380 * Be sure to close it again!
382 * @param string $tag
383 * @param int $pos
384 * @return TagOpenElement
386 public function addTagOpen($tag, $pos = -1)
388 return $this->addElement(new TagOpenElement($tag), $pos);
392 * Add a closing HTML tag to the form
394 * Be sure it had been opened before
396 * @param string $tag
397 * @param int $pos
398 * @return TagCloseElement
400 public function addTagClose($tag, $pos = -1)
402 return $this->addElement(new TagCloseElement($tag), $pos);
406 * Open a Fieldset
408 * @param string $legend
409 * @param int $pos
410 * @return FieldsetOpenElement
412 public function addFieldsetOpen($legend = '', $pos = -1)
414 return $this->addElement(new FieldsetOpenElement($legend), $pos);
418 * Close a fieldset
420 * @param int $pos
421 * @return TagCloseElement
423 public function addFieldsetClose($pos = -1)
425 return $this->addElement(new FieldsetCloseElement(), $pos);
428 #endregion
431 * Adjust the elements so that fieldset open and closes are matching
433 protected function balanceFieldsets()
435 $lastclose = 0;
436 $isopen = false;
437 $len = count($this->elements);
439 for ($pos = 0; $pos < $len; $pos++) {
440 $type = $this->elements[$pos]->getType();
441 if ($type == 'fieldsetopen') {
442 if ($isopen) {
443 //close previous fieldset
444 $this->addFieldsetClose($pos);
445 $lastclose = $pos + 1;
446 $pos++;
447 $len++;
449 $isopen = true;
450 } elseif ($type == 'fieldsetclose') {
451 if (!$isopen) {
452 // make sure there was a fieldsetopen
453 // either right after the last close or at the begining
454 $this->addFieldsetOpen('', $lastclose);
455 $len++;
456 $pos++;
458 $lastclose = $pos;
459 $isopen = false;
463 // close open fieldset at the end
464 if ($isopen) {
465 $this->addFieldsetClose();
470 * The HTML representation of the whole form
472 * @param string $eventName (optional) name of the event: FORM_{$name}_OUTPUT
473 * @return string
475 public function toHTML($eventName = null)
477 $this->balanceFieldsets();
479 // trigger event to provide an opportunity to modify this form
480 if (isset($eventName)) {
481 $eventName = 'FORM_' . strtoupper($eventName) . '_OUTPUT';
482 Event::createAndTrigger($eventName, $this, null, false);
485 $html = '<form ' . buildAttributes($this->attrs()) . '>';
487 foreach ($this->hidden as $name => $value) {
488 $html .= '<input type="hidden" name="' . $name . '" value="' . formText($value) . '" />';
491 foreach ($this->elements as $element) {
492 $html .= $element->toHTML();
495 $html .= '</form>';
497 return $html;