Textpattern PHP Cross Reference Content Management Systems

Source: /textpattern/lib/txplib_forms.php - 700 lines - 18975 bytes - Summary - Text - Print

Description: Collection of HTML form widgets.

   1  <?php
   2  
   3  /*
   4   * Textpattern Content Management System
   5   * http://textpattern.com
   6   *
   7   * Copyright (C) 2016 The Textpattern Development Team
   8   *
   9   * This file is part of Textpattern.
  10   *
  11   * Textpattern is free software; you can redistribute it and/or
  12   * modify it under the terms of the GNU General Public License
  13   * as published by the Free Software Foundation, version 2.
  14   *
  15   * Textpattern is distributed in the hope that it will be useful,
  16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18   * GNU General Public License for more details.
  19   *
  20   * You should have received a copy of the GNU General Public License
  21   * along with Textpattern. If not, see <http://www.gnu.org/licenses/>.
  22   */
  23  
  24  /**
  25   * Collection of HTML form widgets.
  26   *
  27   * @package Form
  28   */
  29  
  30  /**
  31   * Generates a radio button toggle.
  32   *
  33   * @param  array  $values   The values as an array
  34   * @param  string $field    The field name
  35   * @param  string $checked  The checked button, takes a value from $vals
  36   * @param  int    $tabindex The HTML tabindex
  37   * @param  string $id       The HTML id
  38   * @return string A HTML radio button set
  39   * @example
  40   * echo radioSet(array(
  41   *     'value1' => 'Label1',
  42   *     'value2' => 'Label2',
  43   * ), 'myInput', 'value1');
  44   */
  45  
  46  function radioSet($values, $field, $checked = '', $tabindex = 0, $id = '')
  47  {
  48      if ($id) {
  49          $id = $id.'-'.$field;
  50      } else {
  51          $id = $field;
  52      }
  53  
  54      $out = array();
  55  
  56      foreach ((array) $values as $value => $label) {
  57          $out[] = radio($field, $value, (string) $value === (string) $checked, $id.'-'.$value, $tabindex);
  58          $out[] = n.tag($label, 'label', array('for' => $id.'-'.$value));
  59      }
  60  
  61      return join('', $out);
  62  }
  63  
  64  /**
  65   * Generates a Yes/No radio button toggle.
  66   *
  67   * These buttons are booleans. 'Yes' will have a value of 1 and 'No' is 0.
  68   *
  69   * @param  string $field    The field name
  70   * @param  string $checked  The checked button, either '1', '0'
  71   * @param  int    $tabindex The HTML tabindex
  72   * @param  string $id       The HTML id
  73   * @return string HTML
  74   * @see    radioSet()
  75   * @example
  76   * echo form(
  77   *     'Is this an example?'.
  78   *     yesnoRadio('is_example', 1)
  79   * );
  80   */
  81  
  82  function yesnoRadio($field, $checked = '', $tabindex = 0, $id = '')
  83  {
  84      $vals = array(
  85          '0' => gTxt('no'),
  86          '1' => gTxt('yes'),
  87      );
  88  
  89      return radioSet($vals, $field, $checked, $tabindex, $id);
  90  }
  91  
  92  /**
  93   * Generates an On/Off radio button toggle.
  94   *
  95   * @param  string $field    The field name
  96   * @param  string $checked  The checked button, either '1', '0'
  97   * @param  int    $tabindex The HTML tabindex
  98   * @param  string $id       The HTML id
  99   * @return string HTML
 100   * @see    radioSet()
 101   */
 102  
 103  function onoffRadio($field, $checked = '', $tabindex = 0, $id = '')
 104  {
 105      $vals = array(
 106          '0' => gTxt('off'),
 107          '1' => gTxt('on'),
 108      );
 109  
 110      return radioSet($vals, $field, $checked, $tabindex, $id);
 111  }
 112  
 113  /**
 114   * Generates a select field.
 115   *
 116   * @param  string $name        The field
 117   * @param  array  $array       The values as an array array( 'value' => 'label' )
 118   * @param  mixed  $value       The selected option(s). If an array, renders the select multiple
 119   * @param  bool   $blank_first If TRUE, prepends an empty option to the list
 120   * @param  mixed  $onchange    If TRUE submits the form when an option is changed. If a string, inserts it to the select tag
 121   * @param  string $select_id   The HTML id
 122   * @param  bool   $check_type  Type-agnostic comparison
 123   * @param  bool   $disabled    If TRUE renders the select disabled
 124   * @return string HTML
 125   * @example
 126   * echo selectInput('myInput', array(
 127   *     'value1' => 'Label1',
 128   *     'value2' => 'Label2',
 129   * ));
 130   */
 131  
 132  function selectInput($name = '', $array = array(), $value = '', $blank_first = false, $onchange = '', $select_id = '', $check_type = false, $disabled = false)
 133  {
 134      $out = array();
 135  
 136      $selected = false;
 137      $multiple = is_array($value) ? ' multiple="multiple"' : '';
 138      
 139      if ($multiple) {
 140          $name .= '[]';
 141      } else {
 142          $value = (string) $value;
 143      }
 144  
 145      foreach ($array as $avalue => $alabel) {
 146          if (!$multiple && $value === (string) $avalue || $multiple && in_array($avalue, $value)) {
 147              $sel = ' selected="selected"';
 148              $selected = true;
 149          } else {
 150              $sel = '';
 151          }
 152  
 153          $out[] = '<option value="'.txpspecialchars($avalue).'"'.$sel.'>'.txpspecialchars($alabel).'</option>';
 154      }
 155  
 156      if ($blank_first) {
 157          array_unshift($out, '<option value=""'.($selected === false ? ' selected="selected"' : '').'>&#160;</option>');
 158      }
 159  
 160      $atts = join_atts(array(
 161          'id'       => $select_id,
 162          'name'     => $name,
 163          'disabled' => (bool) $disabled,
 164      ), TEXTPATTERN_STRIP_EMPTY);
 165  
 166      if ((string) $onchange === '1') {
 167          $atts .= ' data-submit-on="change"';
 168      } elseif ($onchange) {
 169          $atts .= ' '.trim($onchange);
 170      }
 171  
 172      return n.'<select'.$atts.$multiple.'>'.n.join(n, $out).n.'</select>'.n
 173          .($multiple ? hInput($name, '').n : ''); // TODO: use jQuery UI selectmenu?
 174  }
 175  
 176  /**
 177   * Generates a tree structured select field.
 178   *
 179   * This field takes a NSTREE structure as an associative array. This is mainly
 180   * used for categories.
 181   *
 182   * @param  string $select_name The field
 183   * @param  array  $array       The values as an array
 184   * @param  string $value       The selected option. Takes a value from $value
 185   * @param  string $select_id   The HTML id
 186   * @param  int    $truncate    Truncate labels to certain length. Disabled if set <4.
 187   * @return string HTML
 188   * @see    getTree()
 189   */
 190  
 191  function treeSelectInput($select_name = '', $array = array(), $value = '', $select_id = '', $truncate = 0)
 192  {
 193      $out = array();
 194  
 195      $doctype = get_pref('doctype');
 196      $selected = false;
 197  
 198      foreach ($array as $a) {
 199          if ($a['name'] == 'root') {
 200              continue;
 201          }
 202  
 203          if ((string) $a['name'] === (string) $value) {
 204              $sel = ' selected="selected"';
 205              $selected = true;
 206          } else {
 207              $sel = '';
 208          }
 209  
 210          $sp = str_repeat(sp.sp, $a['level']);
 211  
 212          if (($truncate > 3) && (strlen(utf8_decode($a['title'])) > $truncate)) {
 213              $htmltitle = ' title="'.txpspecialchars($a['title']).'"';
 214              $a['title'] = preg_replace('/^(.{0,'.($truncate - 3).'}).*$/su', '$1', $a['title']);
 215              $hellip = '&#8230;';
 216          } else {
 217              $htmltitle = $hellip = '';
 218          }
 219  
 220          $data_level = '';
 221          if ($doctype !== 'xhtml') {
 222              $data_level = ' data-level="'.$a['level'].'"';
 223          }
 224  
 225          $out[] = '<option value="'.txpspecialchars($a['name']).'"'.$htmltitle.$sel.$data_level.'>'.$sp.txpspecialchars($a['title']).$hellip.'</option>';
 226      }
 227  
 228      array_unshift($out, '<option value=""'.($selected === false ? ' selected="selected"' : '').'>&#160;</option>');
 229  
 230      return n.tag(n.join(n, $out).n, 'select', array(
 231          'id'   => $select_id,
 232          'name' => $select_name,
 233      ));
 234  }
 235  
 236  /**
 237   * Generic form input.
 238   *
 239   * @param  string $type        The input type
 240   * @param  string $name        The input name
 241   * @param  string $value       The value
 242   * @param  string $class       The HTML class
 243   * @param  string $title       The tooltip
 244   * @param  string $onClick     Inline JavaScript attached to the click event
 245   * @param  int    $size        The input size
 246   * @param  int    $tab         The HTML tabindex
 247   * @param  string $id          The HTML id
 248   * @param  bool   $disabled    If TRUE renders the input disabled
 249   * @param  bool   $required    If TRUE the field is marked as required
 250   * @param  string $placeholder The placeholder value displayed when the field is empty
 251   * @return string HTML input
 252   * @example
 253   * echo fInput('text', 'myInput', 'My example value');
 254   */
 255  
 256  function fInput($type, $name, $value, $class = '', $title = '', $onClick = '', $size = 0, $tab = 0, $id = '', $disabled = false, $required = false, $placeholder = '')
 257  {
 258      $atts = join_atts(array(
 259          'class'       => $class,
 260          'id'          => $id,
 261          'name'        => $name,
 262          'type'        => $type,
 263          'size'        => (int) $size,
 264          'title'       => $title,
 265          'onclick'     => $onClick,
 266          'tabindex'    => (int) $tab,
 267          'disabled'    => (bool) $disabled,
 268          'required'    => (bool) $required,
 269          'placeholder' => $placeholder,
 270      ), TEXTPATTERN_STRIP_EMPTY);
 271  
 272      if ($type != 'file' && $type != 'image') {
 273          $atts .= join_atts(array('value' => (string) $value), TEXTPATTERN_STRIP_NONE);
 274      }
 275  
 276      return n.tag_void('input', $atts);
 277  }
 278  
 279  /**
 280   * Sanitises a page title.
 281   *
 282   * @param      string $text The input string
 283   * @return     string
 284   * @deprecated in 4.2.0
 285   * @see        escape_title()
 286   */
 287  
 288  function cleanfInput($text)
 289  {
 290      trigger_error(gTxt('deprecated_function_with', array('{name}' => __FUNCTION__, '{with}' => 'escape_title')), E_USER_NOTICE);
 291  
 292      return escape_title($text);
 293  }
 294  
 295  /**
 296   * Hidden form input.
 297   *
 298   * @param  string $name  The name
 299   * @param  string $value The value
 300   * @return string HTML input
 301   * @example
 302   * echo hInput('myInput', 'hidden value');
 303   */
 304  
 305  function hInput($name, $value)
 306  {
 307      return fInput('hidden', $name, $value);
 308  }
 309  
 310  /**
 311   * Hidden step input.
 312   *
 313   * @param  string $step The step
 314   * @return string HTML input
 315   * @see    form()
 316   * @see    eInput()
 317   * @example
 318   * echo form(
 319   *     eInput('event').
 320   *     sInput('step')
 321   * );
 322   */
 323  
 324  function sInput($step)
 325  {
 326      return hInput('step', $step);
 327  }
 328  
 329  /**
 330   * Hidden event input.
 331   *
 332   * @param  string $event The event
 333   * @return string HTML input
 334   * @see    form()
 335   * @see    sInput()
 336   * @example
 337   * echo form(
 338   *     eInput('event').
 339   *     sInput('step')
 340   * );
 341   */
 342  
 343  function eInput($event)
 344  {
 345      return hInput('event', $event);
 346  }
 347  
 348  /**
 349   * Hidden form token input.
 350   *
 351   * @return string A hidden HTML input containing a CSRF token
 352   * @see    bouncer()
 353   * @see    form_token()
 354   */
 355  
 356  function tInput()
 357  {
 358      return hInput('_txp_token', form_token());
 359  }
 360  
 361  /**
 362   * A checkbox.
 363   *
 364   * @param  string $name     The field
 365   * @param  string $value    The value
 366   * @param  bool   $checked  If TRUE the box is checked
 367   * @param  int    $tabindex The HTML tabindex
 368   * @param  string $id       The HTML id
 369   * @return string HTML input
 370   * @example
 371   * echo checkbox('name', 'value', true);
 372   */
 373  
 374  function checkbox($name, $value, $checked = true, $tabindex = 0, $id = '')
 375  {
 376      $class = 'checkbox';
 377  
 378      if ($checked) {
 379          $class .= ' active';
 380      }
 381  
 382      $atts = join_atts(array(
 383          'class'    => $class,
 384          'id'       => $id,
 385          'name'     => $name,
 386          'type'     => 'checkbox',
 387          'checked'  => (bool) $checked,
 388          'tabindex' => (int) $tabindex,
 389      ), TEXTPATTERN_STRIP_EMPTY);
 390  
 391      $atts .= join_atts(array('value' => (string) $value), TEXTPATTERN_STRIP_NONE);
 392  
 393      return n.tag_void('input', $atts);
 394  }
 395  
 396  /**
 397   * A checkbox without an option to set the value.
 398   *
 399   * @param  string $name     The field
 400   * @param  bool   $value    If TRUE the box is checked
 401   * @param  int    $tabindex The HTML tabindex
 402   * @param  string $id       The HTML id
 403   * @return string HTML input
 404   * @access private
 405   * @see    checkbox()
 406   */
 407  
 408  function checkbox2($name, $value, $tabindex = 0, $id = '')
 409  {
 410      return checkbox($name, 1, $value, $tabindex, $id);
 411  }
 412  
 413  /**
 414   * A single radio button.
 415   *
 416   * @param  string $name     The field
 417   * @param  string $value    The value
 418   * @param  bool   $checked  If TRUE, the button is selected
 419   * @param  string $id       The HTML id
 420   * @param  int    $tabindex The HTML tabindex
 421   * @return string HTML input
 422   */
 423  
 424  function radio($name, $value, $checked = true, $id = '', $tabindex = 0)
 425  {
 426      $class = 'radio';
 427  
 428      if ($checked) {
 429          $class .= ' active';
 430      }
 431  
 432      $atts = join_atts(array(
 433          'class'    => $class,
 434          'id'       => $id,
 435          'name'     => $name,
 436          'type'     => 'radio',
 437          'checked'  => (bool) $checked,
 438          'tabindex' => (int) $tabindex,
 439      ), TEXTPATTERN_STRIP_EMPTY);
 440  
 441      $atts .= join_atts(array('value' => (string) $value), TEXTPATTERN_STRIP_NONE);
 442  
 443      return n.tag_void('input', $atts);
 444  }
 445  
 446  /**
 447   * Generates a form element.
 448   *
 449   * This form will contain a CSRF token if called on an authenticated page.
 450   *
 451   * @param  string $contents The form contents
 452   * @param  string $style    Inline styles added to the form
 453   * @param  string $onsubmit JavaScript run when the form is sent
 454   * @param  string $method   The form method, e.g. "post", "get"
 455   * @param  string $class    The HTML class
 456   * @param  string $fragment A URL fragment added to the form target
 457   * @param  string $id       The HTML id
 458   * @param  string $role     ARIA role name
 459   * @return string HTML form element
 460   */
 461  
 462  function form($contents, $style = '', $onsubmit = '', $method = 'post', $class = '', $fragment = '', $id = '', $role = '')
 463  {
 464      $action = 'index.php';
 465  
 466      if ($onsubmit) {
 467          $onsubmit = 'return '.$onsubmit;
 468      }
 469  
 470      if ($fragment) {
 471          $action .= '#'.$fragment;
 472      }
 473  
 474      return n.tag($contents.tInput().n, 'form', array(
 475          'class'    => $class,
 476          'id'       => $id,
 477          'method'   => $method,
 478          'action'   => $action,
 479          'onsubmit' => $onsubmit,
 480          'role'     => $role,
 481          'style'    => $style,
 482      ));
 483  }
 484  
 485  /**
 486   * Gets and sanitises a field from a prefixed core database table.
 487   *
 488   * @param  string $name       The field
 489   * @param  string $event      The table
 490   * @param  string $identifier The field used for selecting
 491   * @param  string $id         The value used for selecting
 492   * @return string HTML
 493   * @access private
 494   * @see    fetch()
 495   * @see    txpspecialchars()
 496   */
 497  
 498  function fetch_editable($name, $event, $identifier, $id)
 499  {
 500      $q = fetch($name, 'txp_'.$event, $identifier, $id);
 501  
 502      return txpspecialchars($q);
 503  }
 504  
 505  /**
 506   * A textarea.
 507   *
 508   * @param  string $name        The field
 509   * @param  int    $h           The field height in pixels
 510   * @param  int    $w           The field width in pixels
 511   * @param  string $thing       The value
 512   * @param  string $id          The HTML id
 513   * @param  int    $rows        Rows
 514   * @param  int    $cols        Columns
 515   * @param  string $placeholder The placeholder value displayed when the field is empty
 516   * @return string HTML
 517   */
 518  
 519  function text_area($name, $h = 0, $w = 0, $thing = '', $id = '', $rows = 5, $cols = 40, $placeholder = '')
 520  {
 521      $style = '';
 522  
 523      if ($w) {
 524          $style .= 'width:'.intval($w).'px;';
 525      }
 526  
 527      if ($h) {
 528          $style .= 'height:'.intval($h).'px;';
 529      }
 530  
 531      if ((string) $thing === '') {
 532          $thing = null;
 533      } else {
 534          $thing = txpspecialchars($thing);
 535      }
 536  
 537      if (!intval($rows)) {
 538          $rows = 5;
 539      }
 540  
 541      if (!intval($cols)) {
 542          $cols = 40;
 543      }
 544  
 545      return n.tag($thing, 'textarea', array(
 546          'id'          => $id,
 547          'name'        => $name,
 548          'rows'        => (int) $rows,
 549          'cols'        => (int) $cols,
 550          'style'       => $style,
 551          'placeholder' => $placeholder,
 552      ));
 553  }
 554  
 555  /**
 556   * Generates a select field with a name "type".
 557   *
 558   * @param  array $options
 559   * @return string
 560   * @access private
 561   * @see    selectInput()
 562   */
 563  
 564  function type_select($options)
 565  {
 566      return n.'<select name="type">'.type_options($options).'</select>';
 567  }
 568  
 569  /**
 570   * Generates a list of options for use in a select field.
 571   *
 572   * @param  array $array
 573   * @return string
 574   * @access private
 575   * @see    selectInput()
 576   */
 577  
 578  function type_options($array)
 579  {
 580      foreach ($array as $a => $b) {
 581          $out[] = n.'<option value="'.$a.'">'.gTxt($b).'</option>';
 582      }
 583  
 584      return join('', $out);
 585  }
 586  
 587  /**
 588   * Generates a list of radio buttons wrapped in a unordered list.
 589   *
 590   * @param  string       $name        The field
 591   * @param  array        $values      The values as an array array( $value => $label )
 592   * @param  string       $current_val The selected option. Takes a value from $value
 593   * @param  string       $hilight_val The highlighted list item
 594   * @param  string|array $atts        HTML attributes
 595   * @return string       HTML
 596   */
 597  
 598  function radio_list($name, $values, $current_val = '', $hilight_val = '', $atts = array('class' => 'plain-list'))
 599  {
 600      foreach ($values as $value => $label) {
 601          $id = $name.'-'.$value;
 602          $class = 'status-'.$value;
 603  
 604          if ((string) $value === (string) $hilight_val) {
 605              $label = strong($label);
 606              $class .= ' active';
 607          }
 608  
 609          $out[] = tag(
 610              radio($name, $value, ((string) $current_val === (string) $value), $id).
 611              n.tag($label, 'label', array('for' => $id)),
 612              'li', array('class' => $class)
 613          );
 614      }
 615  
 616      return tag(n.join(n, $out).n, 'ul', $atts);
 617  }
 618  
 619  /**
 620   * Generates a field used to store and set a date.
 621   *
 622   * @param  string $name        The field
 623   * @param  string $datevar     The strftime format the date is displayed
 624   * @param  int    $time        The displayed date as a UNIX timestamp
 625   * @param  int    $tab         The HTML tabindex
 626   * @param  string $id          The HTML id
 627   * @return string HTML
 628   * @access private
 629   * @example
 630   * echo tsi('year', '%Y', 1200000000);
 631   */
 632  
 633  function tsi($name, $datevar, $time, $tab = 0, $id = '')
 634  {
 635      static $placeholders = array(
 636          '%Y' => 'yyyy',
 637          '%m' => 'mm',
 638          '%d' => 'dd',
 639          '%H' => 'hh',
 640          '%M' => 'mn',
 641          '%S' => 'ss',
 642      );
 643  
 644      $value = $placeholder = '';
 645      $size = INPUT_TINY;
 646      $pattern = '([0-5][0-9])';
 647  
 648      if ((int) $time) {
 649          $value = safe_strftime($datevar, (int) $time);
 650      }
 651  
 652      if (isset($placeholders[$datevar])) {
 653          $placeholder = gTxt($placeholders[$datevar]);
 654      }
 655  
 656      if ($datevar == '%Y' || $name == 'year' || $name == 'exp_year') {
 657          $class = 'input-year';
 658          $size = INPUT_XSMALL;
 659          $pattern = '[0-9]{4}';
 660      }
 661  
 662      if ($datevar == '%m' || $name == 'month' || $name == 'exp_month') {
 663          $class = 'input-month';
 664          $pattern = '(0[1-9]|1[012])';
 665      }
 666  
 667      if ($datevar == '%d' || $name == 'day' || $name == 'exp_day') {
 668          $class = 'input-day';
 669          $pattern = '(0[1-9]|1[0-9]|2[0-9]|3[01])';
 670      }
 671  
 672      if ($datevar == '%H' || $name == 'hour' || $name == 'exp_hour') {
 673          $class = 'input-hour';
 674          $pattern = '(0[0-9]|1[0-9]|2[0-3])';
 675      }
 676  
 677      if ($datevar == '%M' || $name == 'minute' || $name == 'exp_minute') {
 678          $class = 'input-minute';
 679      }
 680  
 681      if ($datevar == '%S' || $name == 'second' || $name == 'exp_second') {
 682          $class = 'input-second';
 683      }
 684  
 685      return n.tag_void('input', array(
 686          'class'       => $class,
 687          'id'          => $id,
 688          'name'        => $name,
 689          'type'        => 'text',
 690          'inputmode'   => 'numeric',
 691          'pattern'     => $pattern,
 692          'size'        => (int) $size,
 693          'maxlength'   => $size,
 694          'title'       => gTxt('article_'.$name),
 695          'aria-label'  => gTxt('article_'.$name),
 696          'placeholder' => $placeholder,
 697          'tabindex'    => (int) $tab,
 698          'value'       => $value,
 699      ));
 700  }

title

Description

title

Description

title

Description

title

title

Body