[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/Form/ -> Form.php (source)

   1  <?php
   2  namespace dokuwiki\Form;
   3  
   4  /**
   5   * Class Form
   6   *
   7   * Represents the whole Form. This is what you work on, and add Elements to
   8   *
   9   * @package dokuwiki\Form
  10   */
  11  class Form extends Element {
  12  
  13      /**
  14       * @var array name value pairs for hidden values
  15       */
  16      protected $hidden = array();
  17  
  18      /**
  19       * @var Element[] the elements of the form
  20       */
  21      protected $elements = array();
  22  
  23      /**
  24       * Creates a new, empty form with some default attributes
  25       *
  26       * @param array $attributes
  27       * @param bool  $unsafe     if true, then the security token is ommited
  28       */
  29      public function __construct($attributes = array(), $unsafe = false) {
  30          global $ID;
  31  
  32          parent::__construct('form', $attributes);
  33  
  34          // use the current URL as default action
  35          if(!$this->attr('action')) {
  36              $get = $_GET;
  37              if(isset($get['id'])) unset($get['id']);
  38              $self = wl($ID, $get, false, '&'); //attributes are escaped later
  39              $this->attr('action', $self);
  40          }
  41  
  42          // post is default
  43          if(!$this->attr('method')) {
  44              $this->attr('method', 'post');
  45          }
  46  
  47          // we like UTF-8
  48          if(!$this->attr('accept-charset')) {
  49              $this->attr('accept-charset', 'utf-8');
  50          }
  51  
  52          // add the security token by default
  53          if (!$unsafe) {
  54              $this->setHiddenField('sectok', getSecurityToken());
  55          }
  56  
  57          // identify this as a new form based form in HTML
  58          $this->addClass('doku_form');
  59      }
  60  
  61      /**
  62       * Sets a hidden field
  63       *
  64       * @param string $name
  65       * @param string $value
  66       * @return $this
  67       */
  68      public function setHiddenField($name, $value) {
  69          $this->hidden[$name] = $value;
  70          return $this;
  71      }
  72  
  73      #region element query function
  74  
  75      /**
  76       * Returns the numbers of elements in the form
  77       *
  78       * @return int
  79       */
  80      public function elementCount() {
  81          return count($this->elements);
  82      }
  83  
  84      /**
  85       * Get the position of the element in the form or false if it is not in the form
  86       *
  87       * Warning: This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates
  88       * to FALSE. Please read the section on Booleans for more information. Use the === operator for testing the
  89       * return value of this function.
  90       *
  91       * @param Element $element
  92       *
  93       * @return false|int
  94       */
  95      public function getElementPosition(Element $element)
  96      {
  97          return array_search($element, $this->elements, true);
  98      }
  99  
 100      /**
 101       * Returns a reference to the element at a position.
 102       * A position out-of-bounds will return either the
 103       * first (underflow) or last (overflow) element.
 104       *
 105       * @param int $pos
 106       * @return Element
 107       */
 108      public function getElementAt($pos) {
 109          if($pos < 0) $pos = count($this->elements) + $pos;
 110          if($pos < 0) $pos = 0;
 111          if($pos >= count($this->elements)) $pos = count($this->elements) - 1;
 112          return $this->elements[$pos];
 113      }
 114  
 115      /**
 116       * Gets the position of the first of a type of element
 117       *
 118       * @param string $type Element type to look for.
 119       * @param int $offset search from this position onward
 120       * @return false|int position of element if found, otherwise false
 121       */
 122      public function findPositionByType($type, $offset = 0) {
 123          $len = $this->elementCount();
 124          for($pos = $offset; $pos < $len; $pos++) {
 125              if($this->elements[$pos]->getType() == $type) {
 126                  return $pos;
 127              }
 128          }
 129          return false;
 130      }
 131  
 132      /**
 133       * Gets the position of the first element matching the attribute
 134       *
 135       * @param string $name Name of the attribute
 136       * @param string $value Value the attribute should have
 137       * @param int $offset search from this position onward
 138       * @return false|int position of element if found, otherwise false
 139       */
 140      public function findPositionByAttribute($name, $value, $offset = 0) {
 141          $len = $this->elementCount();
 142          for($pos = $offset; $pos < $len; $pos++) {
 143              if($this->elements[$pos]->attr($name) == $value) {
 144                  return $pos;
 145              }
 146          }
 147          return false;
 148      }
 149  
 150      #endregion
 151  
 152      #region Element positioning functions
 153  
 154      /**
 155       * Adds or inserts an element to the form
 156       *
 157       * @param Element $element
 158       * @param int $pos 0-based position in the form, -1 for at the end
 159       * @return Element
 160       */
 161      public function addElement(Element $element, $pos = -1) {
 162          if(is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException(
 163              'You can\'t add a form to a form'
 164          );
 165          if($pos < 0) {
 166              $this->elements[] = $element;
 167          } else {
 168              array_splice($this->elements, $pos, 0, array($element));
 169          }
 170          return $element;
 171      }
 172  
 173      /**
 174       * Replaces an existing element with a new one
 175       *
 176       * @param Element $element the new element
 177       * @param int $pos 0-based position of the element to replace
 178       */
 179      public function replaceElement(Element $element, $pos) {
 180          if(is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException(
 181              'You can\'t add a form to a form'
 182          );
 183          array_splice($this->elements, $pos, 1, array($element));
 184      }
 185  
 186      /**
 187       * Remove an element from the form completely
 188       *
 189       * @param int $pos 0-based position of the element to remove
 190       */
 191      public function removeElement($pos) {
 192          array_splice($this->elements, $pos, 1);
 193      }
 194  
 195      #endregion
 196  
 197      #region Element adding functions
 198  
 199      /**
 200       * Adds a text input field
 201       *
 202       * @param string $name
 203       * @param string $label
 204       * @param int $pos
 205       * @return InputElement
 206       */
 207      public function addTextInput($name, $label = '', $pos = -1) {
 208          return $this->addElement(new InputElement('text', $name, $label), $pos);
 209      }
 210  
 211      /**
 212       * Adds a password input field
 213       *
 214       * @param string $name
 215       * @param string $label
 216       * @param int $pos
 217       * @return InputElement
 218       */
 219      public function addPasswordInput($name, $label = '', $pos = -1) {
 220          return $this->addElement(new InputElement('password', $name, $label), $pos);
 221      }
 222  
 223      /**
 224       * Adds a radio button field
 225       *
 226       * @param string $name
 227       * @param string $label
 228       * @param int $pos
 229       * @return CheckableElement
 230       */
 231      public function addRadioButton($name, $label = '', $pos = -1) {
 232          return $this->addElement(new CheckableElement('radio', $name, $label), $pos);
 233      }
 234  
 235      /**
 236       * Adds a checkbox field
 237       *
 238       * @param string $name
 239       * @param string $label
 240       * @param int $pos
 241       * @return CheckableElement
 242       */
 243      public function addCheckbox($name, $label = '', $pos = -1) {
 244          return $this->addElement(new CheckableElement('checkbox', $name, $label), $pos);
 245      }
 246  
 247      /**
 248       * Adds a dropdown field
 249       *
 250       * @param string $name
 251       * @param array $options
 252       * @param string $label
 253       * @param int $pos
 254       * @return DropdownElement
 255       */
 256      public function addDropdown($name, $options, $label = '', $pos = -1) {
 257          return $this->addElement(new DropdownElement($name, $options, $label), $pos);
 258      }
 259  
 260      /**
 261       * Adds a textarea field
 262       *
 263       * @param string $name
 264       * @param string $label
 265       * @param int $pos
 266       * @return TextareaElement
 267       */
 268      public function addTextarea($name, $label = '', $pos = -1) {
 269          return $this->addElement(new TextareaElement($name, $label), $pos);
 270      }
 271  
 272      /**
 273       * Adds a simple button, escapes the content for you
 274       *
 275       * @param string $name
 276       * @param string $content
 277       * @param int $pos
 278       * @return Element
 279       */
 280      public function addButton($name, $content, $pos = -1) {
 281          return $this->addElement(new ButtonElement($name, hsc($content)), $pos);
 282      }
 283  
 284      /**
 285       * Adds a simple button, allows HTML for content
 286       *
 287       * @param string $name
 288       * @param string $html
 289       * @param int $pos
 290       * @return Element
 291       */
 292      public function addButtonHTML($name, $html, $pos = -1) {
 293          return $this->addElement(new ButtonElement($name, $html), $pos);
 294      }
 295  
 296      /**
 297       * Adds a label referencing another input element, escapes the label for you
 298       *
 299       * @param string $label
 300       * @param string $for
 301       * @param int $pos
 302       * @return Element
 303       */
 304      public function addLabel($label, $for='', $pos = -1) {
 305          return $this->addLabelHTML(hsc($label), $for, $pos);
 306      }
 307  
 308      /**
 309       * Adds a label referencing another input element, allows HTML for content
 310       *
 311       * @param string $content
 312       * @param string|Element $for
 313       * @param int $pos
 314       * @return Element
 315       */
 316      public function addLabelHTML($content, $for='', $pos = -1) {
 317          $element = new LabelElement(hsc($content));
 318  
 319          if(is_a($for, '\dokuwiki\Form\Element')) {
 320              /** @var Element $for */
 321              $for = $for->id();
 322          }
 323          $for = (string) $for;
 324          if($for !== '') {
 325              $element->attr('for', $for);
 326          }
 327  
 328          return $this->addElement($element, $pos);
 329      }
 330  
 331      /**
 332       * Add fixed HTML to the form
 333       *
 334       * @param string $html
 335       * @param int $pos
 336       * @return HTMLElement
 337       */
 338      public function addHTML($html, $pos = -1) {
 339          return $this->addElement(new HTMLElement($html), $pos);
 340      }
 341  
 342      /**
 343       * Add a closed HTML tag to the form
 344       *
 345       * @param string $tag
 346       * @param int $pos
 347       * @return TagElement
 348       */
 349      public function addTag($tag, $pos = -1) {
 350          return $this->addElement(new TagElement($tag), $pos);
 351      }
 352  
 353      /**
 354       * Add an open HTML tag to the form
 355       *
 356       * Be sure to close it again!
 357       *
 358       * @param string $tag
 359       * @param int $pos
 360       * @return TagOpenElement
 361       */
 362      public function addTagOpen($tag, $pos = -1) {
 363          return $this->addElement(new TagOpenElement($tag), $pos);
 364      }
 365  
 366      /**
 367       * Add a closing HTML tag to the form
 368       *
 369       * Be sure it had been opened before
 370       *
 371       * @param string $tag
 372       * @param int $pos
 373       * @return TagCloseElement
 374       */
 375      public function addTagClose($tag, $pos = -1) {
 376          return $this->addElement(new TagCloseElement($tag), $pos);
 377      }
 378  
 379      /**
 380       * Open a Fieldset
 381       *
 382       * @param string $legend
 383       * @param int $pos
 384       * @return FieldsetOpenElement
 385       */
 386      public function addFieldsetOpen($legend = '', $pos = -1) {
 387          return $this->addElement(new FieldsetOpenElement($legend), $pos);
 388      }
 389  
 390      /**
 391       * Close a fieldset
 392       *
 393       * @param int $pos
 394       * @return TagCloseElement
 395       */
 396      public function addFieldsetClose($pos = -1) {
 397          return $this->addElement(new FieldsetCloseElement(), $pos);
 398      }
 399  
 400      #endregion
 401  
 402      /**
 403       * Adjust the elements so that fieldset open and closes are matching
 404       */
 405      protected function balanceFieldsets() {
 406          $lastclose = 0;
 407          $isopen = false;
 408          $len = count($this->elements);
 409  
 410          for($pos = 0; $pos < $len; $pos++) {
 411              $type = $this->elements[$pos]->getType();
 412              if($type == 'fieldsetopen') {
 413                  if($isopen) {
 414                      //close previous fieldset
 415                      $this->addFieldsetClose($pos);
 416                      $lastclose = $pos + 1;
 417                      $pos++;
 418                      $len++;
 419                  }
 420                  $isopen = true;
 421              } else if($type == 'fieldsetclose') {
 422                  if(!$isopen) {
 423                      // make sure there was a fieldsetopen
 424                      // either right after the last close or at the begining
 425                      $this->addFieldsetOpen('', $lastclose);
 426                      $len++;
 427                      $pos++;
 428                  }
 429                  $lastclose = $pos;
 430                  $isopen = false;
 431              }
 432          }
 433  
 434          // close open fieldset at the end
 435          if($isopen) {
 436              $this->addFieldsetClose();
 437          }
 438      }
 439  
 440      /**
 441       * The HTML representation of the whole form
 442       *
 443       * @return string
 444       */
 445      public function toHTML() {
 446          $this->balanceFieldsets();
 447  
 448          $html = '<form ' . buildAttributes($this->attrs()) . '>';
 449  
 450          foreach($this->hidden as $name => $value) {
 451              $html .= '<input type="hidden" name="' . $name . '" value="' . formText($value) . '" />';
 452          }
 453  
 454          foreach($this->elements as $element) {
 455              $html .= $element->toHTML();
 456          }
 457  
 458          $html .= '</form>';
 459  
 460          return $html;
 461      }
 462  }