[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/ -> install.php (source)

   1  <?php
   2  
   3  /*><div style="width:60%; margin: auto; background-color: #fcc;
   4                  border: 1px solid #faa; padding: 0.5em 1em;">
   5      <h1 style="font-size: 120%">No PHP Support</h1>
   6  
   7      It seems this server has no PHP support enabled. You will need to
   8      enable PHP before you can install and run DokuWiki. Contact your hosting
   9      provider if you're unsure what this means.
  10  
  11  </div>*/
  12  
  13  use dokuwiki\PassHash;
  14  
  15  if (!defined('DOKU_INC')) define('DOKU_INC', __DIR__ . '/');
  16  if (!defined('DOKU_CONF')) define('DOKU_CONF', DOKU_INC . 'conf/');
  17  if (!defined('DOKU_LOCAL')) define('DOKU_LOCAL', DOKU_INC . 'conf/');
  18  
  19  // load and initialize the core system
  20  require_once (DOKU_INC . 'inc/init.php');
  21  require_once (DOKU_INC . 'inc/pageutils.php');
  22  
  23  // check for error reporting override or set error reporting to sane values
  24  if (!defined('DOKU_E_LEVEL')) {
  25      error_reporting(E_ALL ^ E_NOTICE);
  26  } else {
  27      error_reporting(DOKU_E_LEVEL);
  28  }
  29  
  30  // language strings
  31  require_once (DOKU_INC . 'inc/lang/en/lang.php');
  32  if (isset($_REQUEST['l']) && !is_array($_REQUEST['l'])) {
  33      $LC = preg_replace('/[^a-z\-]+/', '', $_REQUEST['l']);
  34  }
  35  if (empty($LC)) $LC = 'en';
  36  if ($LC && $LC != 'en') {
  37      require_once(DOKU_INC . 'inc/lang/' . $LC . '/lang.php');
  38  }
  39  
  40  // initialise variables ...
  41  $error = [];
  42  
  43  // begin output
  44  header('Content-Type: text/html; charset=utf-8');
  45  ?>
  46      <!DOCTYPE html>
  47      <html lang="<?php echo $LC ?>" dir="<?php echo $lang['direction'] ?>">
  48      <head>
  49          <meta charset="utf-8"/>
  50          <title><?php echo $lang['i_installer'] ?></title>
  51          <style>
  52              body {
  53                  width: 90%;
  54                  margin: 0 auto;
  55                  font: 84% Verdana, Helvetica, Arial, sans-serif;
  56              }
  57  
  58              img {
  59                  border: none
  60              }
  61  
  62              br.cl {
  63                  clear: both;
  64              }
  65  
  66              code {
  67                  font-size: 110%;
  68                  color: #800000;
  69              }
  70  
  71              fieldset {
  72                  border: none
  73              }
  74  
  75              label {
  76                  display: block;
  77                  margin-top: 0.5em;
  78              }
  79  
  80              select.text, input.text {
  81                  width: 30em;
  82                  margin: 0 0.5em;
  83              }
  84  
  85              a {
  86                  text-decoration: none
  87              }
  88          </style>
  89          <script>
  90              function acltoggle() {
  91                  var cb = document.getElementById('acl');
  92                  var fs = document.getElementById('acldep');
  93                  if (!cb || !fs) return;
  94                  if (cb.checked) {
  95                      fs.style.display = '';
  96                  } else {
  97                      fs.style.display = 'none';
  98                  }
  99              }
 100  
 101              window.onload = function () {
 102                  acltoggle();
 103                  var cb = document.getElementById('acl');
 104                  if (cb) cb.onchange = acltoggle;
 105              };
 106          </script>
 107      </head>
 108      <body style="">
 109      <h1 style="float:left">
 110          <img src="lib/exe/fetch.php?media=wiki:dokuwiki-128.png"
 111               style="vertical-align: middle;" alt="" height="64" width="64"/>
 112          <?php echo $lang['i_installer'] ?>
 113      </h1>
 114      <div style="float:right; margin: 1em;">
 115          <?php langsel() ?>
 116      </div>
 117      <br class="cl"/>
 118  
 119      <div style="float: right; width: 34%;">
 120          <?php
 121          if (file_exists(DOKU_INC . 'inc/lang/' . $LC . '/install.html')) {
 122              include(DOKU_INC . 'inc/lang/' . $LC . '/install.html');
 123          } else {
 124              echo "<div lang=\"en\" dir=\"ltr\">\n";
 125              include(DOKU_INC . 'inc/lang/en/install.html');
 126              echo "</div>\n";
 127          }
 128          ?>
 129          <a style="
 130                  background: transparent
 131                  url(data/dont-panic-if-you-see-this-in-your-logs-it-means-your-directory-permissions-are-correct.png)
 132                  left top no-repeat;
 133                  display: block; width:380px; height:73px; border:none; clear:both;"
 134             target="_blank"
 135             href="https://www.dokuwiki.org/security#web_access_security"></a>
 136      </div>
 137  
 138      <div style="float: left; width: 58%;">
 139          <?php
 140          try {
 141              if (!(check_functions() && check_permissions())) {
 142                  echo '<p>' . $lang['i_problems'] . '</p>';
 143                  print_errors();
 144                  print_retry();
 145              } elseif (!check_configs()) {
 146                  echo '<p>' . $lang['i_modified'] . '</p>';
 147                  print_errors();
 148              } elseif (check_data($_REQUEST['d'])) {
 149                  // check_data has sanitized all input parameters
 150                  if (!store_data($_REQUEST['d'])) {
 151                      echo '<p>' . $lang['i_failure'] . '</p>';
 152                      print_errors();
 153                  } else {
 154                      echo '<p>' . $lang['i_success'] . '</p>';
 155                  }
 156              } else {
 157                  print_errors();
 158                  print_form($_REQUEST['d']);
 159              }
 160          } catch (Exception $e) {
 161              echo 'Caught exception: ', $e->getMessage(), "\n";
 162          }
 163          ?>
 164      </div>
 165  
 166  
 167      <div style="clear: both">
 168          <a href="https://dokuwiki.org/"><img src="lib/tpl/dokuwiki/images/button-dw.png" alt="driven by DokuWiki"/></a>
 169          <a href="https://php.net"><img src="lib/tpl/dokuwiki/images/button-php.gif" alt="powered by PHP"/></a>
 170      </div>
 171      </body>
 172      </html>
 173  <?php
 174  
 175  /**
 176   * Print the input form
 177   *
 178   * @param array $d submitted entry 'd' of request data
 179   */
 180  function print_form($d)
 181  {
 182      global $lang;
 183      global $LC;
 184  
 185      include(DOKU_CONF . 'license.php');
 186  
 187      if (!is_array($d)) $d = [];
 188      $d = array_map('hsc', $d);
 189  
 190      if (!isset($d['acl'])) $d['acl'] = 1;
 191      if (!isset($d['pop'])) $d['pop'] = 1;
 192  
 193      ?>
 194      <form action="" method="post">
 195          <input type="hidden" name="l" value="<?php echo $LC ?>"/>
 196          <fieldset>
 197              <label for="title"><?php echo $lang['i_wikiname'] ?>
 198                  <input type="text" name="d[title]" id="title" value="<?php echo $d['title'] ?>" style="width: 20em;"/>
 199              </label>
 200  
 201              <fieldset style="margin-top: 1em;">
 202                  <label for="acl">
 203                      <input type="checkbox" name="d[acl]"
 204                             id="acl" <?php echo(($d['acl'] ? ' checked="checked"' : '')); ?> />
 205                      <?php echo $lang['i_enableacl'] ?></label>
 206  
 207                  <fieldset id="acldep">
 208                      <label for="superuser"><?php echo $lang['i_superuser'] ?></label>
 209                      <input class="text" type="text" name="d[superuser]" id="superuser"
 210                             value="<?php echo $d['superuser'] ?>"/>
 211  
 212                      <label for="fullname"><?php echo $lang['fullname'] ?></label>
 213                      <input class="text" type="text" name="d[fullname]" id="fullname"
 214                             value="<?php echo $d['fullname'] ?>"/>
 215  
 216                      <label for="email"><?php echo $lang['email'] ?></label>
 217                      <input class="text" type="text" name="d[email]" id="email" value="<?php echo $d['email'] ?>"/>
 218  
 219                      <label for="password"><?php echo $lang['pass'] ?></label>
 220                      <input class="text" type="password" name="d[password]" id="password"/>
 221  
 222                      <label for="confirm"><?php echo $lang['passchk'] ?></label>
 223                      <input class="text" type="password" name="d[confirm]" id="confirm"/>
 224  
 225                      <label for="policy"><?php echo $lang['i_policy'] ?></label>
 226                      <select class="text" name="d[policy]" id="policy">
 227                          <option value="0" <?php echo ($d['policy'] == 0) ? 'selected="selected"' : '' ?>><?php
 228                              echo $lang['i_pol0'] ?></option>
 229                          <option value="1" <?php echo ($d['policy'] == 1) ? 'selected="selected"' : '' ?>><?php
 230                              echo $lang['i_pol1'] ?></option>
 231                          <option value="2" <?php echo ($d['policy'] == 2) ? 'selected="selected"' : '' ?>><?php
 232                              echo $lang['i_pol2'] ?></option>
 233                      </select>
 234  
 235                      <label for="allowreg">
 236                          <input type="checkbox" name="d[allowreg]" id="allowreg" <?php
 237                          echo(($d['allowreg'] ? ' checked="checked"' : '')); ?> />
 238                          <?php echo $lang['i_allowreg'] ?>
 239                      </label>
 240                  </fieldset>
 241              </fieldset>
 242  
 243              <fieldset>
 244                  <p><?php echo $lang['i_license'] ?></p>
 245                  <?php
 246                  $license[] = ['name' => $lang['i_license_none'], 'url' => ''];
 247                  if (empty($d['license'])) $d['license'] = 'cc-by-sa';
 248                  foreach ($license as $key => $lic) {
 249                      echo '<label for="lic_' . $key . '">';
 250                      echo '<input type="radio" name="d[license]" value="' . hsc($key) . '" id="lic_' . $key . '"' .
 251                          (($d['license'] === $key) ? ' checked="checked"' : '') . '>';
 252                      echo hsc($lic['name']);
 253                      if ($lic['url']) echo ' <a href="' . $lic['url'] . '" target="_blank"><sup>[?]</sup></a>';
 254                      echo '</label>';
 255                  }
 256                  ?>
 257              </fieldset>
 258  
 259              <fieldset>
 260                  <p><?php echo $lang['i_pop_field'] ?></p>
 261                  <label for="pop">
 262                      <input type="checkbox" name="d[pop]" id="pop" <?php
 263                      echo(($d['pop'] ? ' checked="checked"' : '')); ?> />
 264                      <?php echo $lang['i_pop_label'] ?>
 265                      <a href="https://www.dokuwiki.org/popularity" target="_blank"><sup>[?]</sup></a>
 266                  </label>
 267              </fieldset>
 268  
 269          </fieldset>
 270          <fieldset id="process">
 271              <button type="submit" name="submit"><?php echo $lang['btn_save'] ?></button>
 272          </fieldset>
 273      </form>
 274      <?php
 275  }
 276  
 277  function print_retry()
 278  {
 279      global $lang;
 280      global $LC;
 281      ?>
 282      <form action="" method="get">
 283          <fieldset>
 284              <input type="hidden" name="l" value="<?php echo $LC ?>"/>
 285              <button type="submit"><?php echo $lang['i_retry']; ?></button>
 286          </fieldset>
 287      </form>
 288      <?php
 289  }
 290  
 291  /**
 292   * Check validity of data
 293   *
 294   * @param array $d
 295   * @return bool ok?
 296   * @author Andreas Gohr
 297   *
 298   */
 299  function check_data(&$d)
 300  {
 301      static $form_default = [
 302          'title' => '',
 303          'acl' => '1',
 304          'superuser' => '',
 305          'fullname' => '',
 306          'email' => '',
 307          'password' => '',
 308          'confirm' => '',
 309          'policy' => '0',
 310          'allowreg' => '0',
 311          'license' => 'cc-by-sa'
 312      ];
 313      global $lang;
 314      global $error;
 315  
 316      if (!is_array($d)) $d = [];
 317      foreach ($d as $k => $v) {
 318          if (is_array($v))
 319              unset($d[$k]);
 320          else $d[$k] = (string)$v;
 321      }
 322  
 323      //autolowercase the username
 324      $d['superuser'] = isset($d['superuser']) ? strtolower($d['superuser']) : "";
 325  
 326      $ok = false;
 327  
 328      if (isset($_REQUEST['submit'])) {
 329          $ok = true;
 330  
 331          // check input
 332          if (empty($d['title'])) {
 333              $error[] = sprintf($lang['i_badval'], $lang['i_wikiname']);
 334              $ok = false;
 335          }
 336          if (isset($d['acl'])) {
 337              if (empty($d['superuser']) || ($d['superuser'] !== cleanID($d['superuser']))) {
 338                  $error[] = sprintf($lang['i_badval'], $lang['i_superuser']);
 339                  $ok = false;
 340              }
 341              if (empty($d['password'])) {
 342                  $error[] = sprintf($lang['i_badval'], $lang['pass']);
 343                  $ok = false;
 344              } elseif (!isset($d['confirm']) || $d['confirm'] != $d['password']) {
 345                  $error[] = sprintf($lang['i_badval'], $lang['passchk']);
 346                  $ok = false;
 347              }
 348              if (empty($d['fullname']) || strstr($d['fullname'], ':')) {
 349                  $error[] = sprintf($lang['i_badval'], $lang['fullname']);
 350                  $ok = false;
 351              }
 352              if (empty($d['email']) || strstr($d['email'], ':') || !strstr($d['email'], '@')) {
 353                  $error[] = sprintf($lang['i_badval'], $lang['email']);
 354                  $ok = false;
 355              }
 356          } else {
 357              // Since default = 1, browser won't send acl=0 when user untick acl
 358              $d['acl'] = '0';
 359          }
 360      }
 361      $d = array_merge($form_default, $d);
 362      return $ok;
 363  }
 364  
 365  /**
 366   * Writes the data to the config files
 367   *
 368   * @param array $d
 369   * @return bool
 370   * @throws Exception
 371   *
 372   * @author  Chris Smith <chris@jalakai.co.uk>
 373   */
 374  function store_data($d)
 375  {
 376      global $LC;
 377      $ok = true;
 378      $d['policy'] = (int)$d['policy'];
 379  
 380      // create local.php
 381      $now = gmdate('r');
 382      $output = <<<EOT
 383  <?php
 384  /**
 385   * Dokuwiki's Main Configuration File - Local Settings
 386   * Auto-generated by install script
 387   * Date: $now
 388   */
 389  
 390  EOT;
 391      // add any config options set by a previous installer
 392      $preset = __DIR__ . '/install.conf';
 393      if (file_exists($preset)) {
 394          $output .= "# preset config options\n";
 395          $output .= file_get_contents($preset);
 396          $output .= "\n\n";
 397          $output .= "# options selected in installer\n";
 398          @unlink($preset);
 399      }
 400  
 401      $output .= '$conf[\'title\'] = \'' . addslashes($d['title']) . "';\n";
 402      $output .= '$conf[\'lang\'] = \'' . addslashes($LC) . "';\n";
 403      $output .= '$conf[\'license\'] = \'' . addslashes($d['license']) . "';\n";
 404      if ($d['acl']) {
 405          $output .= '$conf[\'useacl\'] = 1' . ";\n";
 406          $output .= "\$conf['superuser'] = '@admin';\n";
 407      }
 408      if (!$d['allowreg']) {
 409          $output .= '$conf[\'disableactions\'] = \'register\'' . ";\n";
 410      }
 411      $ok = $ok && fileWrite(DOKU_LOCAL . 'local.php', $output);
 412  
 413      if ($d['acl']) {
 414          // hash the password
 415          $phash = new PassHash();
 416          $pass = $phash->hash_bcrypt($d['password']);
 417  
 418          // create users.auth.php
 419          $output = <<<EOT
 420  # users.auth.php
 421  # <?php exit()?>
 422  # Don't modify the lines above
 423  #
 424  # Userfile
 425  #
 426  # Auto-generated by install script
 427  # Date: $now
 428  #
 429  # Format:
 430  # login:passwordhash:Real Name:email:groups,comma,separated
 431  
 432  EOT;
 433          // --- user:bcryptpasswordhash:Real Name:email:groups,comma,seperated
 434          $output = $output . "\n" . implode(':', [
 435                  $d['superuser'],
 436                  $pass,
 437                  $d['fullname'],
 438                  $d['email'],
 439                  'admin,user',
 440              ]) . "\n";
 441          $ok = $ok && fileWrite(DOKU_LOCAL . 'users.auth.php', $output);
 442  
 443          // create acl.auth.php
 444          $output = <<<EOT
 445  # acl.auth.php
 446  # <?php exit()?>
 447  # Don't modify the lines above
 448  #
 449  # Access Control Lists
 450  #
 451  # Auto-generated by install script
 452  # Date: $now
 453  
 454  EOT;
 455          if ($d['policy'] == 2) {
 456              $output .= "*               @ALL          0\n";
 457              $output .= "*               @user         8\n";
 458          } elseif ($d['policy'] == 1) {
 459              $output .= "*               @ALL          1\n";
 460              $output .= "*               @user         8\n";
 461          } else {
 462              $output .= "*               @ALL          8\n";
 463          }
 464          $ok = $ok && fileWrite(DOKU_LOCAL . 'acl.auth.php', $output);
 465      }
 466  
 467      // enable popularity submission
 468      if (isset($d['pop']) && $d['pop']) {
 469          @touch(DOKU_INC . 'data/cache/autosubmit.txt');
 470      }
 471  
 472      // disable auth plugins til needed
 473      $output = <<<EOT
 474  <?php
 475  /*
 476   * Local plugin enable/disable settings
 477   *
 478   * Auto-generated by install script
 479   * Date: $now
 480   */
 481  
 482  \$plugins['authad']    = 0;
 483  \$plugins['authldap']  = 0;
 484  \$plugins['authmysql'] = 0;
 485  \$plugins['authpgsql'] = 0;
 486  
 487  EOT;
 488      $ok = $ok && fileWrite(DOKU_LOCAL . 'plugins.local.php', $output);
 489  
 490      return $ok;
 491  }
 492  
 493  /**
 494   * Write the given content to a file
 495   *
 496   * @param string $filename
 497   * @param string $data
 498   * @return bool
 499   *
 500   * @author  Chris Smith <chris@jalakai.co.uk>
 501   */
 502  function fileWrite($filename, $data)
 503  {
 504      global $error;
 505      global $lang;
 506  
 507      if (($fp = @fopen($filename, 'wb')) === false) {
 508          $filename = str_replace($_SERVER['DOCUMENT_ROOT'], '{DOCUMENT_ROOT}/', $filename);
 509          $error[] = sprintf($lang['i_writeerr'], $filename);
 510          return false;
 511      }
 512  
 513      if (!empty($data)) {
 514          fwrite($fp, $data);
 515      }
 516      fclose($fp);
 517      return true;
 518  }
 519  
 520  
 521  /**
 522   * check installation dependent local config files and tests for a known
 523   * unmodified main config file
 524   *
 525   * @return bool
 526   *
 527   * @author      Chris Smith <chris@jalakai.co.uk>
 528   */
 529  function check_configs()
 530  {
 531      global $error;
 532      global $lang;
 533  
 534      $ok = true;
 535  
 536      $config_files = [
 537          'local' => DOKU_LOCAL . 'local.php',
 538          'users' => DOKU_LOCAL . 'users.auth.php',
 539          'auth' => DOKU_LOCAL . 'acl.auth.php'
 540      ];
 541  
 542      // configs shouldn't exist
 543      foreach ($config_files as $file) {
 544          if (file_exists($file) && filesize($file)) {
 545              $file = str_replace($_SERVER['DOCUMENT_ROOT'], '{DOCUMENT_ROOT}/', $file);
 546              $error[] = sprintf($lang['i_confexists'], $file);
 547              $ok = false;
 548          }
 549      }
 550      return $ok;
 551  }
 552  
 553  
 554  /**
 555   * Check other installation dir/file permission requirements
 556   *
 557   * @return bool
 558   *
 559   * @author      Chris Smith <chris@jalakai.co.uk>
 560   */
 561  function check_permissions()
 562  {
 563      global $error;
 564      global $lang;
 565  
 566      $dirs = [
 567          'conf' => DOKU_LOCAL,
 568          'data' => DOKU_INC . 'data',
 569          'pages' => DOKU_INC . 'data/pages',
 570          'attic' => DOKU_INC . 'data/attic',
 571          'media' => DOKU_INC . 'data/media',
 572          'media_attic' => DOKU_INC . 'data/media_attic',
 573          'media_meta' => DOKU_INC . 'data/media_meta',
 574          'meta' => DOKU_INC . 'data/meta',
 575          'cache' => DOKU_INC . 'data/cache',
 576          'locks' => DOKU_INC . 'data/locks',
 577          'index' => DOKU_INC . 'data/index',
 578          'tmp' => DOKU_INC . 'data/tmp'
 579      ];
 580  
 581      $ok = true;
 582      foreach ($dirs as $dir) {
 583          if (!file_exists("$dir/.") || !is_writable($dir)) {
 584              $dir = str_replace($_SERVER['DOCUMENT_ROOT'], '{DOCUMENT_ROOT}', $dir);
 585              $error[] = sprintf($lang['i_permfail'], $dir);
 586              $ok = false;
 587          }
 588      }
 589      return $ok;
 590  }
 591  
 592  /**
 593   * Check the availability of functions used in DokuWiki and the PHP version
 594   *
 595   * @return bool
 596   *
 597   * @author Andreas Gohr <andi@splitbrain.org>
 598   */
 599  function check_functions()
 600  {
 601      global $error;
 602      global $lang;
 603      $ok = true;
 604  
 605      if (version_compare(phpversion(), '7.4.0', '<')) {
 606          $error[] = sprintf($lang['i_phpver'], phpversion(), '7.4.0');
 607          $ok = false;
 608      }
 609  
 610      if (ini_get('mbstring.func_overload') != 0) {
 611          $error[] = $lang['i_mbfuncoverload'];
 612          $ok = false;
 613      }
 614  
 615      try {
 616          random_bytes(1);
 617      } catch (Exception $th) {
 618          // If an appropriate source of randomness cannot be found, an Exception will be thrown by PHP 7+
 619          $error[] = $lang['i_urandom'];
 620          $ok = false;
 621      }
 622  
 623      if (ini_get('mbstring.func_overload') != 0) {
 624          $error[] = $lang['i_mbfuncoverload'];
 625          $ok = false;
 626      }
 627  
 628      $funcs = explode(' ', 'addslashes call_user_func chmod copy fgets ' .
 629          'file file_exists fseek flush filesize ftell fopen ' .
 630          'glob header ignore_user_abort ini_get mkdir ' .
 631          'ob_start opendir parse_ini_file readfile realpath ' .
 632          'rename rmdir serialize session_start unlink usleep ' .
 633          'preg_replace file_get_contents htmlspecialchars_decode ' .
 634          'spl_autoload_register stream_select fsockopen pack xml_parser_create');
 635  
 636      if (!function_exists('mb_substr')) {
 637          $funcs[] = 'utf8_encode';
 638          $funcs[] = 'utf8_decode';
 639      }
 640  
 641      if (!function_exists('mail')) {
 642          if (strpos(ini_get('disable_functions'), 'mail') !== false) {
 643              $disabled = $lang['i_disabled'];
 644          } else {
 645              $disabled = "";
 646          }
 647          $error[] = sprintf($lang['i_funcnmail'], $disabled);
 648      }
 649  
 650      foreach ($funcs as $func) {
 651          if (!function_exists($func)) {
 652              $error[] = sprintf($lang['i_funcna'], $func);
 653              $ok = false;
 654          }
 655      }
 656      return $ok;
 657  }
 658  
 659  /**
 660   * Print language selection
 661   *
 662   * @author Andreas Gohr <andi@splitbrain.org>
 663   */
 664  function langsel()
 665  {
 666      global $lang;
 667      global $LC;
 668  
 669      $dir = DOKU_INC . 'inc/lang';
 670      $dh = opendir($dir);
 671      if (!$dh) return;
 672  
 673      $langs = [];
 674      while (($file = readdir($dh)) !== false) {
 675          if (preg_match('/^[._]/', $file)) continue;
 676          if (is_dir($dir . '/' . $file) && file_exists($dir . '/' . $file . '/lang.php')) {
 677              $langs[] = $file;
 678          }
 679      }
 680      closedir($dh);
 681      sort($langs);
 682  
 683      echo '<form action="">';
 684      echo $lang['i_chooselang'];
 685      echo ': <select name="l" onchange="submit()">';
 686      foreach ($langs as $l) {
 687          $sel = ($l == $LC) ? 'selected="selected"' : '';
 688          echo '<option value="' . $l . '" ' . $sel . '>' . $l . '</option>';
 689      }
 690      echo '</select> ';
 691      echo '<button type="submit">' . $lang['btn_update'] . '</button>';
 692      echo '</form>';
 693  }
 694  
 695  /**
 696   * Print global error array
 697   *
 698   * @author Andreas Gohr <andi@splitbrain.org>
 699   */
 700  function print_errors()
 701  {
 702      global $error;
 703      if (!empty($error)) {
 704          echo '<ul>';
 705          foreach ($error as $err) {
 706              echo "<li>$err</li>";
 707          }
 708          echo '</ul>';
 709      }
 710  }