3 namespace dokuwiki\Form
;
8 * Represents the whole Form. This is what you work on, and add Elements to
10 * @package dokuwiki\Form
12 class Form
extends Element
15 * @var array name value pairs for hidden values
17 protected $hidden = array();
20 * @var Element[] the elements of the form
22 protected $elements = array();
25 * Creates a new, empty form with some default attributes
27 * @param array $attributes
28 * @param bool $unsafe if true, then the security token is ommited
30 public function __construct($attributes = array(), $unsafe = false)
34 parent
::__construct('form', $attributes);
36 // use the current URL as default action
37 if (!$this->attr('action')) {
39 if (isset($get['id'])) unset($get['id']);
40 $self = wl($ID, $get, false, '&'); //attributes are escaped later
41 $this->attr('action', $self);
45 if (!$this->attr('method')) {
46 $this->attr('method', 'post');
50 if (!$this->attr('accept-charset')) {
51 $this->attr('accept-charset', 'utf-8');
54 // add the security token by default
56 $this->setHiddenField('sectok', getSecurityToken());
59 // identify this as a new form based form in HTML
60 $this->addClass('doku_form');
67 * @param string $value
70 public function setHiddenField($name, $value)
72 $this->hidden
[$name] = $value;
76 #region element query function
79 * Returns the numbers of elements in the form
83 public function elementCount()
85 return count($this->elements
);
89 * Get the position of the element in the form or false if it is not in the form
91 * Warning: This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates
92 * to FALSE. Please read the section on Booleans for more information. Use the === operator for testing the
93 * return value of this function.
95 * @param Element $element
99 public function getElementPosition(Element
$element)
101 return array_search($element, $this->elements
, true);
105 * Returns a reference to the element at a position.
106 * A position out-of-bounds will return either the
107 * first (underflow) or last (overflow) element.
112 public function getElementAt($pos)
114 if ($pos < 0) $pos = count($this->elements
) +
$pos;
115 if ($pos < 0) $pos = 0;
116 if ($pos >= count($this->elements
)) $pos = count($this->elements
) - 1;
117 return $this->elements
[$pos];
121 * Gets the position of the first of a type of element
123 * @param string $type Element type to look for.
124 * @param int $offset search from this position onward
125 * @return false|int position of element if found, otherwise false
127 public function findPositionByType($type, $offset = 0)
129 $len = $this->elementCount();
130 for ($pos = $offset; $pos < $len; $pos++
) {
131 if ($this->elements
[$pos]->getType() == $type) {
139 * Gets the position of the first element matching the attribute
141 * @param string $name Name of the attribute
142 * @param string $value Value the attribute should have
143 * @param int $offset search from this position onward
144 * @return false|int position of element if found, otherwise false
146 public function findPositionByAttribute($name, $value, $offset = 0)
148 $len = $this->elementCount();
149 for ($pos = $offset; $pos < $len; $pos++
) {
150 if ($this->elements
[$pos]->attr($name) == $value) {
159 #region Element positioning functions
162 * Adds or inserts an element to the form
164 * @param Element $element
165 * @param int $pos 0-based position in the form, -1 for at the end
168 public function addElement(Element
$element, $pos = -1)
170 if (is_a($element, '\dokuwiki\Form\Form')) throw new \
InvalidArgumentException(
171 'You can\'t add a form to a form'
174 $this->elements
[] = $element;
176 array_splice($this->elements
, $pos, 0, array($element));
182 * Replaces an existing element with a new one
184 * @param Element $element the new element
185 * @param int $pos 0-based position of the element to replace
187 public function replaceElement(Element
$element, $pos)
189 if (is_a($element, '\dokuwiki\Form\Form')) throw new \
InvalidArgumentException(
190 'You can\'t add a form to a form'
192 array_splice($this->elements
, $pos, 1, array($element));
196 * Remove an element from the form completely
198 * @param int $pos 0-based position of the element to remove
200 public function removeElement($pos)
202 array_splice($this->elements
, $pos, 1);
207 #region Element adding functions
210 * Adds a text input field
212 * @param string $name
213 * @param string $label
215 * @return InputElement
217 public function addTextInput($name, $label = '', $pos = -1)
219 return $this->addElement(new InputElement('text', $name, $label), $pos);
223 * Adds a password input field
225 * @param string $name
226 * @param string $label
228 * @return InputElement
230 public function addPasswordInput($name, $label = '', $pos = -1)
232 return $this->addElement(new InputElement('password', $name, $label), $pos);
236 * Adds a radio button field
238 * @param string $name
239 * @param string $label
241 * @return CheckableElement
243 public function addRadioButton($name, $label = '', $pos = -1)
245 return $this->addElement(new CheckableElement('radio', $name, $label), $pos);
249 * Adds a checkbox field
251 * @param string $name
252 * @param string $label
254 * @return CheckableElement
256 public function addCheckbox($name, $label = '', $pos = -1)
258 return $this->addElement(new CheckableElement('checkbox', $name, $label), $pos);
262 * Adds a dropdown field
264 * @param string $name
265 * @param array $options
266 * @param string $label
268 * @return DropdownElement
270 public function addDropdown($name, $options, $label = '', $pos = -1)
272 return $this->addElement(new DropdownElement($name, $options, $label), $pos);
276 * Adds a textarea field
278 * @param string $name
279 * @param string $label
281 * @return TextareaElement
283 public function addTextarea($name, $label = '', $pos = -1)
285 return $this->addElement(new TextareaElement($name, $label), $pos);
289 * Adds a simple button, escapes the content for you
291 * @param string $name
292 * @param string $content
296 public function addButton($name, $content, $pos = -1)
298 return $this->addElement(new ButtonElement($name, hsc($content)), $pos);
302 * Adds a simple button, allows HTML for content
304 * @param string $name
305 * @param string $html
309 public function addButtonHTML($name, $html, $pos = -1)
311 return $this->addElement(new ButtonElement($name, $html), $pos);
315 * Adds a label referencing another input element, escapes the label for you
317 * @param string $label
322 public function addLabel($label, $for='', $pos = -1)
324 return $this->addLabelHTML(hsc($label), $for, $pos);
328 * Adds a label referencing another input element, allows HTML for content
330 * @param string $content
331 * @param string|Element $for
335 public function addLabelHTML($content, $for='', $pos = -1)
337 $element = new LabelElement(hsc($content));
339 if (is_a($for, '\dokuwiki\Form\Element')) {
340 /** @var Element $for */
343 $for = (string) $for;
345 $element->attr('for', $for);
348 return $this->addElement($element, $pos);
352 * Add fixed HTML to the form
354 * @param string $html
356 * @return HTMLElement
358 public function addHTML($html, $pos = -1)
360 return $this->addElement(new HTMLElement($html), $pos);
364 * Add a closed HTML tag to the form
370 public function addTag($tag, $pos = -1)
372 return $this->addElement(new TagElement($tag), $pos);
376 * Add an open HTML tag to the form
378 * Be sure to close it again!
382 * @return TagOpenElement
384 public function addTagOpen($tag, $pos = -1)
386 return $this->addElement(new TagOpenElement($tag), $pos);
390 * Add a closing HTML tag to the form
392 * Be sure it had been opened before
396 * @return TagCloseElement
398 public function addTagClose($tag, $pos = -1)
400 return $this->addElement(new TagCloseElement($tag), $pos);
406 * @param string $legend
408 * @return FieldsetOpenElement
410 public function addFieldsetOpen($legend = '', $pos = -1)
412 return $this->addElement(new FieldsetOpenElement($legend), $pos);
419 * @return TagCloseElement
421 public function addFieldsetClose($pos = -1)
423 return $this->addElement(new FieldsetCloseElement(), $pos);
429 * Adjust the elements so that fieldset open and closes are matching
431 protected function balanceFieldsets()
435 $len = count($this->elements
);
437 for ($pos = 0; $pos < $len; $pos++
) {
438 $type = $this->elements
[$pos]->getType();
439 if ($type == 'fieldsetopen') {
441 //close previous fieldset
442 $this->addFieldsetClose($pos);
443 $lastclose = $pos +
1;
448 } elseif ($type == 'fieldsetclose') {
450 // make sure there was a fieldsetopen
451 // either right after the last close or at the begining
452 $this->addFieldsetOpen('', $lastclose);
461 // close open fieldset at the end
463 $this->addFieldsetClose();
468 * The HTML representation of the whole form
470 * @param string $eventName (optional) name of the event: HTMLFORM_{$name}_OUTPUT
473 public function toHTML($eventName = null)
475 $this->balanceFieldsets();
477 // trigger event to provide an opportunity to modify this form
478 if (isset($eventName)) {
479 if (!preg_match('/^HTMLFORM_[A-Z]+?_OUTPUT$/', $eventName)) {
480 $eventName = 'HTMLFORM_'.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();