[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/ -> html.php (source)

   1  <?php
   2  /**
   3   * HTML output functions
   4   *
   5   * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
   6   * @author     Andreas Gohr <andi@splitbrain.org>
   7   */
   8  
   9  use dokuwiki\ChangeLog\MediaChangeLog;
  10  use dokuwiki\ChangeLog\PageChangeLog;
  11  use dokuwiki\Extension\AuthPlugin;
  12  use dokuwiki\Extension\Event;
  13  
  14  if (!defined('SEC_EDIT_PATTERN')) {
  15      define('SEC_EDIT_PATTERN', '#<!-- EDIT({.*?}) -->#');
  16  }
  17  
  18  
  19  /**
  20   * Convenience function to quickly build a wikilink
  21   *
  22   * @author Andreas Gohr <andi@splitbrain.org>
  23   * @param string  $id      id of the target page
  24   * @param string  $name    the name of the link, i.e. the text that is displayed
  25   * @param string|array  $search  search string(s) that shall be highlighted in the target page
  26   * @return string the HTML code of the link
  27   */
  28  function html_wikilink($id,$name=null,$search=''){
  29      /** @var Doku_Renderer_xhtml $xhtml_renderer */
  30      static $xhtml_renderer = null;
  31      if(is_null($xhtml_renderer)){
  32          $xhtml_renderer = p_get_renderer('xhtml');
  33      }
  34  
  35      return $xhtml_renderer->internallink($id,$name,$search,true,'navigation');
  36  }
  37  
  38  /**
  39   * The loginform
  40   *
  41   * @author   Andreas Gohr <andi@splitbrain.org>
  42   *
  43   * @param bool $svg Whether to show svg icons in the register and resendpwd links or not
  44   */
  45  function html_login($svg = false){
  46      global $lang;
  47      global $conf;
  48      global $ID;
  49      global $INPUT;
  50  
  51      print p_locale_xhtml('login');
  52      print '<div class="centeralign">'.NL;
  53      $form = new Doku_Form(array('id' => 'dw__login', 'action'=>wl($ID)));
  54      $form->startFieldset($lang['btn_login']);
  55      $form->addHidden('id', $ID);
  56      $form->addHidden('do', 'login');
  57      $form->addElement(form_makeTextField(
  58          'u',
  59          ((!$INPUT->bool('http_credentials')) ? $INPUT->str('u') : ''),
  60          $lang['user'],
  61          'focus__this',
  62          'block')
  63      );
  64      $form->addElement(form_makePasswordField('p', $lang['pass'], '', 'block'));
  65      if($conf['rememberme']) {
  66          $form->addElement(form_makeCheckboxField('r', '1', $lang['remember'], 'remember__me', 'simple'));
  67      }
  68      $form->addElement(form_makeButton('submit', '', $lang['btn_login']));
  69      $form->endFieldset();
  70  
  71      if(actionOK('register')){
  72          $registerLink = (new \dokuwiki\Menu\Item\Register())->asHtmlLink('', $svg);
  73          $form->addElement('<p>'.$lang['reghere'].': '. $registerLink .'</p>');
  74      }
  75  
  76      if (actionOK('resendpwd')) {
  77          $resendPwLink = (new \dokuwiki\Menu\Item\Resendpwd())->asHtmlLink('', $svg);
  78          $form->addElement('<p>'.$lang['pwdforget'].': '. $resendPwLink .'</p>');
  79      }
  80  
  81      html_form('login', $form);
  82      print '</div>'.NL;
  83  }
  84  
  85  
  86  /**
  87   * Denied page content
  88   *
  89   * @return string html
  90   */
  91  function html_denied() {
  92      print p_locale_xhtml('denied');
  93  
  94      if(empty($_SERVER['REMOTE_USER']) && actionOK('login')){
  95          html_login();
  96      }
  97  }
  98  
  99  /**
 100   * inserts section edit buttons if wanted or removes the markers
 101   *
 102   * @author Andreas Gohr <andi@splitbrain.org>
 103   *
 104   * @param string $text
 105   * @param bool   $show show section edit buttons?
 106   * @return string
 107   */
 108  function html_secedit($text,$show=true){
 109      global $INFO;
 110  
 111      if(!$INFO['writable'] || !$show || $INFO['rev']){
 112          return preg_replace(SEC_EDIT_PATTERN,'',$text);
 113      }
 114  
 115      return preg_replace_callback(SEC_EDIT_PATTERN,
 116                  'html_secedit_button', $text);
 117  }
 118  
 119  /**
 120   * prepares section edit button data for event triggering
 121   * used as a callback in html_secedit
 122   *
 123   * @author Andreas Gohr <andi@splitbrain.org>
 124   *
 125   * @param array $matches matches with regexp
 126   * @return string
 127   * @triggers HTML_SECEDIT_BUTTON
 128   */
 129  function html_secedit_button($matches){
 130      $json = htmlspecialchars_decode($matches[1], ENT_QUOTES);
 131      $data = json_decode($json, true);
 132      if ($data == NULL) {
 133          return;
 134      }
 135      $data ['target'] = strtolower($data['target']);
 136      $data ['hid'] = strtolower($data['hid']);
 137  
 138      return Event::createAndTrigger('HTML_SECEDIT_BUTTON', $data,
 139                           'html_secedit_get_button');
 140  }
 141  
 142  /**
 143   * prints a section editing button
 144   * used as default action form HTML_SECEDIT_BUTTON
 145   *
 146   * @author Adrian Lang <lang@cosmocode.de>
 147   *
 148   * @param array $data name, section id and target
 149   * @return string html
 150   */
 151  function html_secedit_get_button($data) {
 152      global $ID;
 153      global $INFO;
 154  
 155      if (!isset($data['name']) || $data['name'] === '') return '';
 156  
 157      $name = $data['name'];
 158      unset($data['name']);
 159  
 160      $secid = $data['secid'];
 161      unset($data['secid']);
 162  
 163      return "<div class='secedit editbutton_" . $data['target'] .
 164                         " editbutton_" . $secid . "'>" .
 165             html_btn('secedit', $ID, '',
 166                      array_merge(array('do'  => 'edit',
 167                                        'rev' => $INFO['lastmod'],
 168                                        'summary' => '['.$name.'] '), $data),
 169                      'post', $name) . '</div>';
 170  }
 171  
 172  /**
 173   * Just the back to top button (in its own form)
 174   *
 175   * @author Andreas Gohr <andi@splitbrain.org>
 176   *
 177   * @return string html
 178   */
 179  function html_topbtn(){
 180      global $lang;
 181  
 182      $ret = '<a class="nolink" href="#dokuwiki__top">' .
 183          '<button class="button" onclick="window.scrollTo(0, 0)" title="' . $lang['btn_top'] . '">' .
 184          $lang['btn_top'] .
 185          '</button></a>';
 186  
 187      return $ret;
 188  }
 189  
 190  /**
 191   * Displays a button (using its own form)
 192   * If tooltip exists, the access key tooltip is replaced.
 193   *
 194   * @author Andreas Gohr <andi@splitbrain.org>
 195   *
 196   * @param string         $name
 197   * @param string         $id
 198   * @param string         $akey   access key
 199   * @param string[] $params key-value pairs added as hidden inputs
 200   * @param string         $method
 201   * @param string         $tooltip
 202   * @param bool|string    $label  label text, false: lookup btn_$name in localization
 203   * @param string         $svg (optional) svg code, inserted into the button
 204   * @return string
 205   */
 206  function html_btn($name, $id, $akey, $params, $method='get', $tooltip='', $label=false, $svg=null){
 207      global $conf;
 208      global $lang;
 209  
 210      if (!$label)
 211          $label = $lang['btn_'.$name];
 212  
 213      $ret = '';
 214  
 215      //filter id (without urlencoding)
 216      $id = idfilter($id,false);
 217  
 218      //make nice URLs even for buttons
 219      if($conf['userewrite'] == 2){
 220          $script = DOKU_BASE.DOKU_SCRIPT.'/'.$id;
 221      }elseif($conf['userewrite']){
 222          $script = DOKU_BASE.$id;
 223      }else{
 224          $script = DOKU_BASE.DOKU_SCRIPT;
 225          $params['id'] = $id;
 226      }
 227  
 228      $ret .= '<form class="button btn_'.$name.'" method="'.$method.'" action="'.$script.'"><div class="no">';
 229  
 230      if(is_array($params)){
 231          foreach($params as $key => $val) {
 232              $ret .= '<input type="hidden" name="'.$key.'" ';
 233              $ret .= 'value="'.hsc($val).'" />';
 234          }
 235      }
 236  
 237      if ($tooltip!='') {
 238          $tip = hsc($tooltip);
 239      }else{
 240          $tip = hsc($label);
 241      }
 242  
 243      $ret .= '<button type="submit" ';
 244      if($akey){
 245          $tip .= ' ['.strtoupper($akey).']';
 246          $ret .= 'accesskey="'.$akey.'" ';
 247      }
 248      $ret .= 'title="'.$tip.'">';
 249      if ($svg) {
 250          $ret .= '<span>' . hsc($label) . '</span>';
 251          $ret .= inlineSVG($svg);
 252      } else {
 253          $ret .= hsc($label);
 254      }
 255      $ret .= '</button>';
 256      $ret .= '</div></form>';
 257  
 258      return $ret;
 259  }
 260  /**
 261   * show a revision warning
 262   *
 263   * @author Szymon Olewniczak <dokuwiki@imz.re>
 264   */
 265  function html_showrev() {
 266      print p_locale_xhtml('showrev');
 267  }
 268  
 269  /**
 270   * Show a wiki page
 271   *
 272   * @author Andreas Gohr <andi@splitbrain.org>
 273   *
 274   * @param null|string $txt wiki text or null for showing $ID
 275   */
 276  function html_show($txt=null){
 277      global $ID;
 278      global $REV;
 279      global $HIGH;
 280      global $INFO;
 281      global $DATE_AT;
 282      //disable section editing for old revisions or in preview
 283      if($txt || $REV){
 284          $secedit = false;
 285      }else{
 286          $secedit = true;
 287      }
 288  
 289      if (!is_null($txt)){
 290          //PreviewHeader
 291          echo '<br id="scroll__here" />';
 292          echo p_locale_xhtml('preview');
 293          echo '<div class="preview"><div class="pad">';
 294          $html = html_secedit(p_render('xhtml',p_get_instructions($txt),$info),$secedit);
 295          if($INFO['prependTOC']) $html = tpl_toc(true).$html;
 296          echo $html;
 297          echo '<div class="clearer"></div>';
 298          echo '</div></div>';
 299  
 300      }else{
 301          if ($REV||$DATE_AT){
 302              $data = array('rev' => &$REV, 'date_at' => &$DATE_AT);
 303              Event::createAndTrigger('HTML_SHOWREV_OUTPUT', $data, 'html_showrev');
 304          }
 305          $html = p_wiki_xhtml($ID,$REV,true,$DATE_AT);
 306          $html = html_secedit($html,$secedit);
 307          if($INFO['prependTOC']) $html = tpl_toc(true).$html;
 308          $html = html_hilight($html,$HIGH);
 309          echo $html;
 310      }
 311  }
 312  
 313  /**
 314   * ask the user about how to handle an exisiting draft
 315   *
 316   * @author Andreas Gohr <andi@splitbrain.org>
 317   */
 318  function html_draft(){
 319      global $INFO;
 320      global $ID;
 321      global $lang;
 322      $draft = new \dokuwiki\Draft($ID, $INFO['client']);
 323      $text  = $draft->getDraftText();
 324  
 325      print p_locale_xhtml('draft');
 326      html_diff($text, false);
 327      $form = new Doku_Form(array('id' => 'dw__editform'));
 328      $form->addHidden('id', $ID);
 329      $form->addHidden('date', $draft->getDraftDate());
 330      $form->addHidden('wikitext', $text);
 331      $form->addElement(form_makeOpenTag('div', array('id'=>'draft__status')));
 332      $form->addElement($draft->getDraftMessage());
 333      $form->addElement(form_makeCloseTag('div'));
 334      $form->addElement(form_makeButton('submit', 'recover', $lang['btn_recover'], array('tabindex'=>'1')));
 335      $form->addElement(form_makeButton('submit', 'draftdel', $lang['btn_draftdel'], array('tabindex'=>'2')));
 336      $form->addElement(form_makeButton('submit', 'show', $lang['btn_cancel'], array('tabindex'=>'3')));
 337      html_form('draft', $form);
 338  }
 339  
 340  /**
 341   * Highlights searchqueries in HTML code
 342   *
 343   * @author Andreas Gohr <andi@splitbrain.org>
 344   * @author Harry Fuecks <hfuecks@gmail.com>
 345   *
 346   * @param string $html
 347   * @param array|string $phrases
 348   * @return string html
 349   */
 350  function html_hilight($html,$phrases){
 351      $phrases = (array) $phrases;
 352      $phrases = array_map('preg_quote_cb', $phrases);
 353      $phrases = array_map('ft_snippet_re_preprocess', $phrases);
 354      $phrases = array_filter($phrases);
 355      $regex = join('|',$phrases);
 356  
 357      if ($regex === '') return $html;
 358      if (!\dokuwiki\Utf8\Clean::isUtf8($regex)) return $html;
 359      $html = @preg_replace_callback("/((<[^>]*)|$regex)/ui",'html_hilight_callback',$html);
 360      return $html;
 361  }
 362  
 363  /**
 364   * Callback used by html_hilight()
 365   *
 366   * @author Harry Fuecks <hfuecks@gmail.com>
 367   *
 368   * @param array $m matches
 369   * @return string html
 370   */
 371  function html_hilight_callback($m) {
 372      $hlight = unslash($m[0]);
 373      if ( !isset($m[2])) {
 374          $hlight = '<span class="search_hit">'.$hlight.'</span>';
 375      }
 376      return $hlight;
 377  }
 378  
 379  /**
 380   * Display error on locked pages
 381   *
 382   * @author Andreas Gohr <andi@splitbrain.org>
 383   */
 384  function html_locked(){
 385      global $ID;
 386      global $conf;
 387      global $lang;
 388      global $INFO;
 389  
 390      $locktime = filemtime(wikiLockFN($ID));
 391      $expire = dformat($locktime + $conf['locktime']);
 392      $min    = round(($conf['locktime'] - (time() - $locktime) )/60);
 393  
 394      print p_locale_xhtml('locked');
 395      print '<ul>';
 396      print '<li><div class="li"><strong>'.$lang['lockedby'].'</strong> '.editorinfo($INFO['locked']).'</div></li>';
 397      print '<li><div class="li"><strong>'.$lang['lockexpire'].'</strong> '.$expire.' ('.$min.' min)</div></li>';
 398      print '</ul>';
 399  }
 400  
 401  /**
 402   * list old revisions
 403   *
 404   * @author Andreas Gohr <andi@splitbrain.org>
 405   * @author Ben Coburn <btcoburn@silicodon.net>
 406   * @author Kate Arzamastseva <pshns@ukr.net>
 407   *
 408   * @param int $first skip the first n changelog lines
 409   * @param bool|string $media_id id of media, or false for current page
 410   */
 411  function html_revisions($first=0, $media_id = false){
 412      global $ID;
 413      global $INFO;
 414      global $conf;
 415      global $lang;
 416      $id = $ID;
 417      if ($media_id) {
 418          $id = $media_id;
 419          $changelog = new MediaChangeLog($id);
 420      } else {
 421          $changelog = new PageChangeLog($id);
 422      }
 423  
 424      /* we need to get one additional log entry to be able to
 425       * decide if this is the last page or is there another one.
 426       * see html_recent()
 427       */
 428  
 429      $revisions = $changelog->getRevisions($first, $conf['recent']+1);
 430  
 431      if(count($revisions)==0 && $first!=0){
 432          $first=0;
 433          $revisions = $changelog->getRevisions($first, $conf['recent']+1);
 434      }
 435      $hasNext = false;
 436      if (count($revisions)>$conf['recent']) {
 437          $hasNext = true;
 438          array_pop($revisions); // remove extra log entry
 439      }
 440  
 441      if (!$media_id) print p_locale_xhtml('revisions');
 442  
 443      $params = array('id' => 'page__revisions', 'class' => 'changes');
 444      if($media_id) {
 445          $params['action'] = media_managerURL(array('image' => $media_id), '&');
 446      }
 447  
 448      if(!$media_id) {
 449          $exists = $INFO['exists'];
 450          $display_name = useHeading('navigation') ? hsc(p_get_first_heading($id)) : $id;
 451          if(!$display_name) {
 452              $display_name = $id;
 453          }
 454      } else {
 455          $exists = file_exists(mediaFN($id));
 456          $display_name = $id;
 457      }
 458  
 459      $form = new Doku_Form($params);
 460      $form->addElement(form_makeOpenTag('ul'));
 461  
 462      if($exists && $first == 0) {
 463          $minor = false;
 464          if($media_id) {
 465              $date = dformat(@filemtime(mediaFN($id)));
 466              $href = media_managerURL(array('image' => $id, 'tab_details' => 'view'), '&');
 467  
 468              $changelog->setChunkSize(1024);
 469              $revinfo = $changelog->getRevisionInfo(@filemtime(fullpath(mediaFN($id))));
 470  
 471              $summary = $revinfo['sum'];
 472              if($revinfo['user']) {
 473                  $editor = $revinfo['user'];
 474              } else {
 475                  $editor = $revinfo['ip'];
 476              }
 477              $sizechange = $revinfo['sizechange'];
 478          } else {
 479              $date = dformat($INFO['lastmod']);
 480              if(isset($INFO['meta']) && isset($INFO['meta']['last_change'])) {
 481                  if($INFO['meta']['last_change']['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) {
 482                      $minor = true;
 483                  }
 484                  if(isset($INFO['meta']['last_change']['sizechange'])) {
 485                      $sizechange = $INFO['meta']['last_change']['sizechange'];
 486                  } else {
 487                      $sizechange = null;
 488                  }
 489              }
 490              $pagelog = new PageChangeLog($ID);
 491              $latestrev = $pagelog->getRevisions(-1, 1);
 492              $latestrev = array_pop($latestrev);
 493              $href = wl($id,"rev=$latestrev",false,'&');
 494              $summary = $INFO['sum'];
 495              $editor = $INFO['editor'];
 496          }
 497  
 498          $form->addElement(form_makeOpenTag('li', array('class' => ($minor ? 'minor' : ''))));
 499          $form->addElement(form_makeOpenTag('div', array('class' => 'li')));
 500          $form->addElement(form_makeTag('input', array(
 501                          'type' => 'checkbox',
 502                          'name' => 'rev2[]',
 503                          'value' => 'current')));
 504  
 505          $form->addElement(form_makeOpenTag('span', array('class' => 'date')));
 506          $form->addElement($date);
 507          $form->addElement(form_makeCloseTag('span'));
 508  
 509          $form->addElement('<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />');
 510  
 511          $form->addElement(form_makeOpenTag('a', array(
 512                          'class' => 'wikilink1',
 513                          'href'  => $href)));
 514          $form->addElement($display_name);
 515          $form->addElement(form_makeCloseTag('a'));
 516  
 517          if ($media_id) $form->addElement(form_makeOpenTag('div'));
 518  
 519          if($summary) {
 520              $form->addElement(form_makeOpenTag('span', array('class' => 'sum')));
 521              if(!$media_id) $form->addElement(' – ');
 522              $form->addElement('<bdi>' . hsc($summary) . '</bdi>');
 523              $form->addElement(form_makeCloseTag('span'));
 524          }
 525  
 526          $form->addElement(form_makeOpenTag('span', array('class' => 'user')));
 527          $form->addElement((empty($editor))?('('.$lang['external_edit'].')'):'<bdi>'.editorinfo($editor).'</bdi>');
 528          $form->addElement(form_makeCloseTag('span'));
 529  
 530          html_sizechange($sizechange, $form);
 531  
 532          $form->addElement('('.$lang['current'].')');
 533  
 534          if ($media_id) $form->addElement(form_makeCloseTag('div'));
 535  
 536          $form->addElement(form_makeCloseTag('div'));
 537          $form->addElement(form_makeCloseTag('li'));
 538      }
 539  
 540      foreach($revisions as $rev) {
 541          $date = dformat($rev);
 542          $info = $changelog->getRevisionInfo($rev);
 543          if($media_id) {
 544              $exists = file_exists(mediaFN($id, $rev));
 545          } else {
 546              $exists = page_exists($id, $rev);
 547          }
 548  
 549          $class = '';
 550          if($info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) {
 551              $class = 'minor';
 552          }
 553          $form->addElement(form_makeOpenTag('li', array('class' => $class)));
 554          $form->addElement(form_makeOpenTag('div', array('class' => 'li')));
 555          if($exists){
 556              $form->addElement(form_makeTag('input', array(
 557                              'type' => 'checkbox',
 558                              'name' => 'rev2[]',
 559                              'value' => $rev)));
 560          }else{
 561              $form->addElement('<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />');
 562          }
 563  
 564          $form->addElement(form_makeOpenTag('span', array('class' => 'date')));
 565          $form->addElement($date);
 566          $form->addElement(form_makeCloseTag('span'));
 567  
 568          if($exists){
 569              if (!$media_id) {
 570                  $href = wl($id,"rev=$rev,do=diff", false, '&');
 571              } else {
 572                  $href = media_managerURL(array('image' => $id, 'rev' => $rev, 'mediado' => 'diff'), '&');
 573              }
 574              $form->addElement(form_makeOpenTag('a', array(
 575                              'class' => 'diff_link',
 576                              'href' => $href)));
 577              $form->addElement(form_makeTag('img', array(
 578                              'src'    => DOKU_BASE.'lib/images/diff.png',
 579                              'width'  => 15,
 580                              'height' => 11,
 581                              'title'  => $lang['diff'],
 582                              'alt'    => $lang['diff'])));
 583              $form->addElement(form_makeCloseTag('a'));
 584  
 585              if (!$media_id) {
 586                  $href = wl($id,"rev=$rev",false,'&');
 587              } else {
 588                  $href = media_managerURL(array('image' => $id, 'tab_details' => 'view', 'rev' => $rev), '&');
 589              }
 590              $form->addElement(form_makeOpenTag('a', array(
 591                              'class' => 'wikilink1',
 592                              'href' => $href)));
 593              $form->addElement($display_name);
 594              $form->addElement(form_makeCloseTag('a'));
 595          }else{
 596              $form->addElement('<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />');
 597              $form->addElement($display_name);
 598          }
 599  
 600          if ($media_id) $form->addElement(form_makeOpenTag('div'));
 601  
 602          if ($info['sum']) {
 603              $form->addElement(form_makeOpenTag('span', array('class' => 'sum')));
 604              if(!$media_id) $form->addElement(' – ');
 605              $form->addElement('<bdi>'.hsc($info['sum']).'</bdi>');
 606              $form->addElement(form_makeCloseTag('span'));
 607          }
 608  
 609          $form->addElement(form_makeOpenTag('span', array('class' => 'user')));
 610          if($info['user']){
 611              $form->addElement('<bdi>'.editorinfo($info['user']).'</bdi>');
 612              if(auth_ismanager()){
 613                  $form->addElement(' <bdo dir="ltr">('.$info['ip'].')</bdo>');
 614              }
 615          }else{
 616              $form->addElement('<bdo dir="ltr">'.$info['ip'].'</bdo>');
 617          }
 618          $form->addElement(form_makeCloseTag('span'));
 619  
 620          html_sizechange($info['sizechange'], $form);
 621  
 622          if ($media_id) $form->addElement(form_makeCloseTag('div'));
 623  
 624          $form->addElement(form_makeCloseTag('div'));
 625          $form->addElement(form_makeCloseTag('li'));
 626      }
 627      $form->addElement(form_makeCloseTag('ul'));
 628      if (!$media_id) {
 629          $form->addElement(form_makeButton('submit', 'diff', $lang['diff2']));
 630      } else {
 631          $form->addHidden('mediado', 'diff');
 632          $form->addElement(form_makeButton('submit', '', $lang['diff2']));
 633      }
 634      html_form('revisions', $form);
 635  
 636      print '<div class="pagenav">';
 637      $last = $first + $conf['recent'];
 638      if ($first > 0) {
 639          $first -= $conf['recent'];
 640          if ($first < 0) $first = 0;
 641          print '<div class="pagenav-prev">';
 642          if ($media_id) {
 643              print html_btn('newer',$media_id,"p",media_managerURL(array('first' => $first), '&amp;', false, true));
 644          } else {
 645              print html_btn('newer',$id,"p",array('do' => 'revisions', 'first' => $first));
 646          }
 647          print '</div>';
 648      }
 649      if ($hasNext) {
 650          print '<div class="pagenav-next">';
 651          if ($media_id) {
 652              print html_btn('older',$media_id,"n",media_managerURL(array('first' => $last), '&amp;', false, true));
 653          } else {
 654              print html_btn('older',$id,"n",array('do' => 'revisions', 'first' => $last));
 655          }
 656          print '</div>';
 657      }
 658      print '</div>';
 659  
 660  }
 661  
 662  /**
 663   * display recent changes
 664   *
 665   * @author Andreas Gohr <andi@splitbrain.org>
 666   * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
 667   * @author Ben Coburn <btcoburn@silicodon.net>
 668   * @author Kate Arzamastseva <pshns@ukr.net>
 669   *
 670   * @param int $first
 671   * @param string $show_changes
 672   */
 673  function html_recent($first = 0, $show_changes = 'both') {
 674      global $conf;
 675      global $lang;
 676      global $ID;
 677      /* we need to get one additionally log entry to be able to
 678       * decide if this is the last page or is there another one.
 679       * This is the cheapest solution to get this information.
 680       */
 681      $flags = 0;
 682      if($show_changes == 'mediafiles' && $conf['mediarevisions']) {
 683          $flags = RECENTS_MEDIA_CHANGES;
 684      } elseif($show_changes == 'pages') {
 685          $flags = 0;
 686      } elseif($conf['mediarevisions']) {
 687          $show_changes = 'both';
 688          $flags = RECENTS_MEDIA_PAGES_MIXED;
 689      }
 690  
 691      $recents = getRecents($first, $conf['recent'] + 1, getNS($ID), $flags);
 692      if(count($recents) == 0 && $first != 0) {
 693          $first = 0;
 694          $recents = getRecents($first, $conf['recent'] + 1, getNS($ID), $flags);
 695      }
 696      $hasNext = false;
 697      if(count($recents) > $conf['recent']) {
 698          $hasNext = true;
 699          array_pop($recents); // remove extra log entry
 700      }
 701  
 702      print p_locale_xhtml('recent');
 703  
 704      if(getNS($ID) != '') {
 705          print '<div class="level1"><p>' .
 706              sprintf($lang['recent_global'], getNS($ID), wl('', 'do=recent')) .
 707              '</p></div>';
 708      }
 709  
 710      $form = new Doku_Form(array('id' => 'dw__recent', 'method' => 'GET', 'class' => 'changes', 'action'=>wl($ID)));
 711      $form->addHidden('sectok', null);
 712      $form->addHidden('do', 'recent');
 713      $form->addHidden('id', $ID);
 714  
 715      if($conf['mediarevisions']) {
 716          $form->addElement('<div class="changeType">');
 717          $form->addElement(form_makeListboxField(
 718                      'show_changes',
 719                      array(
 720                          'pages'      => $lang['pages_changes'],
 721                          'mediafiles' => $lang['media_changes'],
 722                          'both'       => $lang['both_changes']
 723                      ),
 724                      $show_changes,
 725                      $lang['changes_type'],
 726                      '', '',
 727                      array('class' => 'quickselect')));
 728  
 729          $form->addElement(form_makeButton('submit', 'recent', $lang['btn_apply']));
 730          $form->addElement('</div>');
 731      }
 732  
 733      $form->addElement(form_makeOpenTag('ul'));
 734  
 735      foreach($recents as $recent) {
 736          $date = dformat($recent['date']);
 737  
 738          $class = '';
 739          if($recent['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) {
 740              $class = 'minor';
 741          }
 742          $form->addElement(form_makeOpenTag('li', array('class' => $class)));
 743          $form->addElement(form_makeOpenTag('div', array('class' => 'li')));
 744  
 745          if(!empty($recent['media'])) {
 746              $form->addElement(media_printicon($recent['id']));
 747          } else {
 748              $icon = DOKU_BASE . 'lib/images/fileicons/file.png';
 749              $form->addElement('<img src="' . $icon . '" alt="' . $recent['id'] . '" class="icon" />');
 750          }
 751  
 752          $form->addElement(form_makeOpenTag('span', array('class' => 'date')));
 753          $form->addElement($date);
 754          $form->addElement(form_makeCloseTag('span'));
 755  
 756          $diff = false;
 757          $href = '';
 758  
 759          if(!empty($recent['media'])) {
 760              $changelog = new MediaChangeLog($recent['id']);
 761              $revs = $changelog->getRevisions(0, 1);
 762              $diff = (count($revs) && file_exists(mediaFN($recent['id'])));
 763              if($diff) {
 764                  $href = media_managerURL(array(
 765                                              'tab_details' => 'history',
 766                                              'mediado' => 'diff',
 767                                              'image' => $recent['id'],
 768                                              'ns' => getNS($recent['id'])
 769                                          ), '&');
 770              }
 771          } else {
 772              $href = wl($recent['id'], "do=diff", false, '&');
 773          }
 774  
 775          if(!empty($recent['media']) && !$diff) {
 776              $form->addElement('<img src="' . DOKU_BASE . 'lib/images/blank.gif" width="15" height="11" alt="" />');
 777          } else {
 778              $form->addElement(form_makeOpenTag('a', array('class' => 'diff_link', 'href' => $href)));
 779              $form->addElement(form_makeTag('img', array(
 780                              'src'    => DOKU_BASE . 'lib/images/diff.png',
 781                              'width'  => 15,
 782                              'height' => 11,
 783                              'title'  => $lang['diff'],
 784                              'alt'    => $lang['diff']
 785                          )));
 786              $form->addElement(form_makeCloseTag('a'));
 787          }
 788  
 789          if(!empty($recent['media'])) {
 790              $href = media_managerURL(
 791                  array(
 792                      'tab_details' => 'history',
 793                      'image' => $recent['id'],
 794                      'ns' => getNS($recent['id'])
 795                  ),
 796                  '&'
 797              );
 798          } else {
 799              $href = wl($recent['id'], "do=revisions", false, '&');
 800          }
 801          $form->addElement(form_makeOpenTag('a', array(
 802                          'class' => 'revisions_link',
 803                          'href'  => $href)));
 804          $form->addElement(form_makeTag('img', array(
 805                          'src'    => DOKU_BASE . 'lib/images/history.png',
 806                          'width'  => 12,
 807                          'height' => 14,
 808                          'title'  => $lang['btn_revs'],
 809                          'alt'    => $lang['btn_revs']
 810                      )));
 811          $form->addElement(form_makeCloseTag('a'));
 812  
 813          if(!empty($recent['media'])) {
 814              $href = media_managerURL(
 815                  array(
 816                      'tab_details' => 'view',
 817                      'image' => $recent['id'],
 818                      'ns' => getNS($recent['id'])
 819                  ),
 820                  '&'
 821              );
 822              $class = file_exists(mediaFN($recent['id'])) ? 'wikilink1' : 'wikilink2';
 823              $form->addElement(form_makeOpenTag('a', array(
 824                          'class' => $class,
 825                          'href'  => $href)));
 826              $form->addElement($recent['id']);
 827              $form->addElement(form_makeCloseTag('a'));
 828          } else {
 829              $form->addElement(html_wikilink(':' . $recent['id'], useHeading('navigation') ? null : $recent['id']));
 830          }
 831          $form->addElement(form_makeOpenTag('span', array('class' => 'sum')));
 832          $form->addElement(' – ' . hsc($recent['sum']));
 833          $form->addElement(form_makeCloseTag('span'));
 834  
 835          $form->addElement(form_makeOpenTag('span', array('class' => 'user')));
 836          if($recent['user']) {
 837              $form->addElement('<bdi>' . editorinfo($recent['user']) . '</bdi>');
 838              if(auth_ismanager()) {
 839                  $form->addElement(' <bdo dir="ltr">(' . $recent['ip'] . ')</bdo>');
 840              }
 841          } else {
 842              $form->addElement('<bdo dir="ltr">' . $recent['ip'] . '</bdo>');
 843          }
 844          $form->addElement(form_makeCloseTag('span'));
 845  
 846          html_sizechange($recent['sizechange'], $form);
 847  
 848          $form->addElement(form_makeCloseTag('div'));
 849          $form->addElement(form_makeCloseTag('li'));
 850      }
 851      $form->addElement(form_makeCloseTag('ul'));
 852  
 853      $form->addElement(form_makeOpenTag('div', array('class' => 'pagenav')));
 854      $last = $first + $conf['recent'];
 855      if($first > 0) {
 856          $first -= $conf['recent'];
 857          if($first < 0) $first = 0;
 858          $form->addElement(form_makeOpenTag('div', array('class' => 'pagenav-prev')));
 859          $form->addElement(form_makeOpenTag('button', array(
 860                          'type'      => 'submit',
 861                          'name'      => 'first[' . $first . ']',
 862                          'accesskey' => 'n',
 863                          'title'     => $lang['btn_newer'] . ' [N]',
 864                          'class'     => 'button show'
 865                      )));
 866          $form->addElement($lang['btn_newer']);
 867          $form->addElement(form_makeCloseTag('button'));
 868          $form->addElement(form_makeCloseTag('div'));
 869      }
 870      if($hasNext) {
 871          $form->addElement(form_makeOpenTag('div', array('class' => 'pagenav-next')));
 872          $form->addElement(form_makeOpenTag('button', array(
 873                          'type'      => 'submit',
 874                          'name'      => 'first[' . $last . ']',
 875                          'accesskey' => 'p',
 876                          'title'     => $lang['btn_older'] . ' [P]',
 877                          'class'     => 'button show'
 878                      )));
 879          $form->addElement($lang['btn_older']);
 880          $form->addElement(form_makeCloseTag('button'));
 881          $form->addElement(form_makeCloseTag('div'));
 882      }
 883      $form->addElement(form_makeCloseTag('div'));
 884      html_form('recent', $form);
 885  }
 886  
 887  /**
 888   * Display page index
 889   *
 890   * @author Andreas Gohr <andi@splitbrain.org>
 891   *
 892   * @param string $ns
 893   */
 894  function html_index($ns){
 895      global $conf;
 896      global $ID;
 897      $ns  = cleanID($ns);
 898      if(empty($ns)){
 899          $ns = getNS($ID);
 900          if($ns === false) $ns ='';
 901      }
 902      $ns  = utf8_encodeFN(str_replace(':','/',$ns));
 903  
 904      echo p_locale_xhtml('index');
 905      echo '<div id="index__tree" class="index__tree">';
 906  
 907      $data = array();
 908      search($data,$conf['datadir'],'search_index',array('ns' => $ns));
 909      echo html_buildlist($data,'idx','html_list_index','html_li_index');
 910  
 911      echo '</div>';
 912  }
 913  
 914  /**
 915   * Index item formatter
 916   *
 917   * User function for html_buildlist()
 918   *
 919   * @author Andreas Gohr <andi@splitbrain.org>
 920   *
 921   * @param array $item
 922   * @return string
 923   */
 924  function html_list_index($item){
 925      global $ID, $conf;
 926  
 927      // prevent searchbots needlessly following links
 928      $nofollow = ($ID != $conf['start'] || $conf['sitemap']) ? 'rel="nofollow"' : '';
 929  
 930      $ret = '';
 931      $base = ':'.$item['id'];
 932      $base = substr($base,strrpos($base,':')+1);
 933      if($item['type']=='d'){
 934          // FS#2766, no need for search bots to follow namespace links in the index
 935          $link = wl($ID, 'idx=' . rawurlencode($item['id']));
 936          $ret .= '<a href="' . $link . '" title="' . $item['id'] . '" class="idx_dir" ' . $nofollow . '><strong>';
 937          $ret .= $base;
 938          $ret .= '</strong></a>';
 939      }else{
 940          // default is noNSorNS($id), but we want noNS($id) when useheading is off FS#2605
 941          $ret .= html_wikilink(':'.$item['id'], useHeading('navigation') ? null : noNS($item['id']));
 942      }
 943      return $ret;
 944  }
 945  
 946  /**
 947   * Index List item
 948   *
 949   * This user function is used in html_buildlist to build the
 950   * <li> tags for namespaces when displaying the page index
 951   * it gives different classes to opened or closed "folders"
 952   *
 953   * @author Andreas Gohr <andi@splitbrain.org>
 954   *
 955   * @param array $item
 956   * @return string html
 957   */
 958  function html_li_index($item){
 959      global $INFO;
 960      global $ACT;
 961  
 962      $class = '';
 963      $id = '';
 964  
 965      if($item['type'] == "f"){
 966          // scroll to the current item
 967          if($item['id'] == $INFO['id'] && $ACT == 'index') {
 968              $id = ' id="scroll__here"';
 969              $class = ' bounce';
 970          }
 971          return '<li class="level'.$item['level'].$class.'" '.$id.'>';
 972      }elseif($item['open']){
 973          return '<li class="open">';
 974      }else{
 975          return '<li class="closed">';
 976      }
 977  }
 978  
 979  /**
 980   * Default List item
 981   *
 982   * @author Andreas Gohr <andi@splitbrain.org>
 983   *
 984   * @param array $item
 985   * @return string html
 986   */
 987  function html_li_default($item){
 988      return '<li class="level'.$item['level'].'">';
 989  }
 990  
 991  /**
 992   * Build an unordered list
 993   *
 994   * Build an unordered list from the given $data array
 995   * Each item in the array has to have a 'level' property
 996   * the item itself gets printed by the given $func user
 997   * function. The second and optional function is used to
 998   * print the <li> tag. Both user function need to accept
 999   * a single item.
1000   *
1001   * Both user functions can be given as array to point to
1002   * a member of an object.
1003   *
1004   * @author Andreas Gohr <andi@splitbrain.org>
1005   *
1006   * @param array    $data  array with item arrays
1007   * @param string   $class class of ul wrapper
1008   * @param callable $func  callback to print an list item
1009   * @param callable $lifunc callback to the opening li tag
1010   * @param bool     $forcewrapper Trigger building a wrapper ul if the first level is
1011   *                               0 (we have a root object) or 1 (just the root content)
1012   * @return string html of an unordered list
1013   */
1014  function html_buildlist($data,$class,$func,$lifunc='html_li_default',$forcewrapper=false){
1015      if (count($data) === 0) {
1016          return '';
1017      }
1018  
1019      $firstElement = reset($data);
1020      $start_level = $firstElement['level'];
1021      $level = $start_level;
1022      $ret   = '';
1023      $open  = 0;
1024  
1025      foreach ($data as $item){
1026  
1027          if( $item['level'] > $level ){
1028              //open new list
1029              for($i=0; $i<($item['level'] - $level); $i++){
1030                  if ($i) $ret .= "<li class=\"clear\">";
1031                  $ret .= "\n<ul class=\"$class\">\n";
1032                  $open++;
1033              }
1034              $level = $item['level'];
1035  
1036          }elseif( $item['level'] < $level ){
1037              //close last item
1038              $ret .= "</li>\n";
1039              while( $level > $item['level'] && $open > 0 ){
1040                  //close higher lists
1041                  $ret .= "</ul>\n</li>\n";
1042                  $level--;
1043                  $open--;
1044              }
1045          } elseif ($ret !== '') {
1046              //close previous item
1047              $ret .= "</li>\n";
1048          }
1049  
1050          //print item
1051          $ret .= call_user_func($lifunc,$item);
1052          $ret .= '<div class="li">';
1053  
1054          $ret .= call_user_func($func,$item);
1055          $ret .= '</div>';
1056      }
1057  
1058      //close remaining items and lists
1059      $ret .= "</li>\n";
1060      while($open-- > 0) {
1061          $ret .= "</ul></li>\n";
1062      }
1063  
1064      if ($forcewrapper || $start_level < 2) {
1065          // Trigger building a wrapper ul if the first level is
1066          // 0 (we have a root object) or 1 (just the root content)
1067          $ret = "\n<ul class=\"$class\">\n".$ret."</ul>\n";
1068      }
1069  
1070      return $ret;
1071  }
1072  
1073  /**
1074   * display backlinks
1075   *
1076   * @author Andreas Gohr <andi@splitbrain.org>
1077   * @author Michael Klier <chi@chimeric.de>
1078   */
1079  function html_backlinks(){
1080      global $ID;
1081      global $lang;
1082  
1083      print p_locale_xhtml('backlinks');
1084  
1085      $data = ft_backlinks($ID);
1086  
1087      if(!empty($data)) {
1088          print '<ul class="idx">';
1089          foreach($data as $blink){
1090              print '<li><div class="li">';
1091              print html_wikilink(':'.$blink,useHeading('navigation')?null:$blink);
1092              print '</div></li>';
1093          }
1094          print '</ul>';
1095      } else {
1096          print '<div class="level1"><p>' . $lang['nothingfound'] . '</p></div>';
1097      }
1098  }
1099  
1100  /**
1101   * Get header of diff HTML
1102   *
1103   * @param string $l_rev   Left revisions
1104   * @param string $r_rev   Right revision
1105   * @param string $id      Page id, if null $ID is used
1106   * @param bool   $media   If it is for media files
1107   * @param bool   $inline  Return the header on a single line
1108   * @return string[] HTML snippets for diff header
1109   */
1110  function html_diff_head($l_rev, $r_rev, $id = null, $media = false, $inline = false) {
1111      global $lang;
1112      if ($id === null) {
1113          global $ID;
1114          $id = $ID;
1115      }
1116      $head_separator = $inline ? ' ' : '<br />';
1117      $media_or_wikiFN = $media ? 'mediaFN' : 'wikiFN';
1118      $ml_or_wl = $media ? 'ml' : 'wl';
1119      $l_minor = $r_minor = '';
1120  
1121      if($media) {
1122          $changelog = new MediaChangeLog($id);
1123      } else {
1124          $changelog = new PageChangeLog($id);
1125      }
1126      if(!$l_rev){
1127          $l_head = '&mdash;';
1128      }else{
1129          $l_info   = $changelog->getRevisionInfo($l_rev);
1130          if($l_info['user']){
1131              $l_user = '<bdi>'.editorinfo($l_info['user']).'</bdi>';
1132              if(auth_ismanager()) $l_user .= ' <bdo dir="ltr">('.$l_info['ip'].')</bdo>';
1133          } else {
1134              $l_user = '<bdo dir="ltr">'.$l_info['ip'].'</bdo>';
1135          }
1136          $l_user  = '<span class="user">'.$l_user.'</span>';
1137          $l_sum   = ($l_info['sum']) ? '<span class="sum"><bdi>'.hsc($l_info['sum']).'</bdi></span>' : '';
1138          if ($l_info['type']===DOKU_CHANGE_TYPE_MINOR_EDIT) $l_minor = 'class="minor"';
1139  
1140          $l_head_title = ($media) ? dformat($l_rev) : $id.' ['.dformat($l_rev).']';
1141          $l_head = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id,"rev=$l_rev").'">'.
1142          $l_head_title.'</a></bdi>'.
1143          $head_separator.$l_user.' '.$l_sum;
1144      }
1145  
1146      if($r_rev){
1147          $r_info   = $changelog->getRevisionInfo($r_rev);
1148          if($r_info['user']){
1149              $r_user = '<bdi>'.editorinfo($r_info['user']).'</bdi>';
1150              if(auth_ismanager()) $r_user .= ' <bdo dir="ltr">('.$r_info['ip'].')</bdo>';
1151          } else {
1152              $r_user = '<bdo dir="ltr">'.$r_info['ip'].'</bdo>';
1153          }
1154          $r_user = '<span class="user">'.$r_user.'</span>';
1155          $r_sum  = ($r_info['sum']) ? '<span class="sum"><bdi>'.hsc($r_info['sum']).'</bdi></span>' : '';
1156          if ($r_info['type']===DOKU_CHANGE_TYPE_MINOR_EDIT) $r_minor = 'class="minor"';
1157  
1158          $r_head_title = ($media) ? dformat($r_rev) : $id.' ['.dformat($r_rev).']';
1159          $r_head = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id,"rev=$r_rev").'">'.
1160          $r_head_title.'</a></bdi>'.
1161          $head_separator.$r_user.' '.$r_sum;
1162      }elseif($_rev = @filemtime($media_or_wikiFN($id))){
1163          $_info   = $changelog->getRevisionInfo($_rev);
1164          if($_info['user']){
1165              $_user = '<bdi>'.editorinfo($_info['user']).'</bdi>';
1166              if(auth_ismanager()) $_user .= ' <bdo dir="ltr">('.$_info['ip'].')</bdo>';
1167          } else {
1168              $_user = '<bdo dir="ltr">'.$_info['ip'].'</bdo>';
1169          }
1170          $_user = '<span class="user">'.$_user.'</span>';
1171          $_sum  = ($_info['sum']) ? '<span class="sum"><bdi>'.hsc($_info['sum']).'</span></bdi>' : '';
1172          if ($_info['type']===DOKU_CHANGE_TYPE_MINOR_EDIT) $r_minor = 'class="minor"';
1173  
1174          $r_head_title = ($media) ? dformat($_rev) : $id.' ['.dformat($_rev).']';
1175          $r_head  = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id).'">'.
1176          $r_head_title.'</a></bdi> '.
1177          '('.$lang['current'].')'.
1178          $head_separator.$_user.' '.$_sum;
1179      }else{
1180          $r_head = '&mdash; ('.$lang['current'].')';
1181      }
1182  
1183      return array($l_head, $r_head, $l_minor, $r_minor);
1184  }
1185  
1186  /**
1187   * Show diff
1188   * between current page version and provided $text
1189   * or between the revisions provided via GET or POST
1190   *
1191   * @author Andreas Gohr <andi@splitbrain.org>
1192   * @param  string $text  when non-empty: compare with this text with most current version
1193   * @param  bool   $intro display the intro text
1194   * @param  string $type  type of the diff (inline or sidebyside)
1195   */
1196  function html_diff($text = '', $intro = true, $type = null) {
1197      global $ID;
1198      global $REV;
1199      global $lang;
1200      global $INPUT;
1201      global $INFO;
1202      $pagelog = new PageChangeLog($ID);
1203  
1204      /*
1205       * Determine diff type
1206       */
1207      if(!$type) {
1208          $type = $INPUT->str('difftype');
1209          if(empty($type)) {
1210              $type = get_doku_pref('difftype', $type);
1211              if(empty($type) && $INFO['ismobile']) {
1212                  $type = 'inline';
1213              }
1214          }
1215      }
1216      if($type != 'inline') $type = 'sidebyside';
1217  
1218      /*
1219       * Determine requested revision(s)
1220       */
1221      // we're trying to be clever here, revisions to compare can be either
1222      // given as rev and rev2 parameters, with rev2 being optional. Or in an
1223      // array in rev2.
1224      $rev1 = $REV;
1225  
1226      $rev2 = $INPUT->ref('rev2');
1227      if(is_array($rev2)) {
1228          $rev1 = (int) $rev2[0];
1229          $rev2 = (int) $rev2[1];
1230  
1231          if(!$rev1) {
1232              $rev1 = $rev2;
1233              unset($rev2);
1234          }
1235      } else {
1236          $rev2 = $INPUT->int('rev2');
1237      }
1238  
1239      /*
1240       * Determine left and right revision, its texts and the header
1241       */
1242      $r_minor = '';
1243      $l_minor = '';
1244  
1245      if($text) { // compare text to the most current revision
1246          $l_rev = '';
1247          $l_text = rawWiki($ID, '');
1248          $l_head = '<a class="wikilink1" href="' . wl($ID) . '">' .
1249              $ID . ' ' . dformat((int) @filemtime(wikiFN($ID))) . '</a> ' .
1250              $lang['current'];
1251  
1252          $r_rev = '';
1253          $r_text = cleanText($text);
1254          $r_head = $lang['yours'];
1255      } else {
1256          if($rev1 && isset($rev2) && $rev2) { // two specific revisions wanted
1257              // make sure order is correct (older on the left)
1258              if($rev1 < $rev2) {
1259                  $l_rev = $rev1;
1260                  $r_rev = $rev2;
1261              } else {
1262                  $l_rev = $rev2;
1263                  $r_rev = $rev1;
1264              }
1265          } elseif($rev1) { // single revision given, compare to current
1266              $r_rev = '';
1267              $l_rev = $rev1;
1268          } else { // no revision was given, compare previous to current
1269              $r_rev = '';
1270              $revs = $pagelog->getRevisions(0, 1);
1271              $l_rev = $revs[0];
1272              $REV = $l_rev; // store revision back in $REV
1273          }
1274  
1275          // when both revisions are empty then the page was created just now
1276          if(!$l_rev && !$r_rev) {
1277              $l_text = '';
1278          } else {
1279              $l_text = rawWiki($ID, $l_rev);
1280          }
1281          $r_text = rawWiki($ID, $r_rev);
1282  
1283          list($l_head, $r_head, $l_minor, $r_minor) = html_diff_head($l_rev, $r_rev, null, false, $type == 'inline');
1284      }
1285  
1286      /*
1287       * Build navigation
1288       */
1289      $l_nav = '';
1290      $r_nav = '';
1291      if(!$text) {
1292          list($l_nav, $r_nav) = html_diff_navigation($pagelog, $type, $l_rev, $r_rev);
1293      }
1294      /*
1295       * Create diff object and the formatter
1296       */
1297      $diff = new Diff(explode("\n", $l_text), explode("\n", $r_text));
1298  
1299      if($type == 'inline') {
1300          $diffformatter = new InlineDiffFormatter();
1301      } else {
1302          $diffformatter = new TableDiffFormatter();
1303      }
1304      /*
1305       * Display intro
1306       */
1307      if($intro) print p_locale_xhtml('diff');
1308  
1309      /*
1310       * Display type and exact reference
1311       */
1312      if(!$text) {
1313          ptln('<div class="diffoptions group">');
1314  
1315  
1316          $form = new Doku_Form(array('action' => wl()));
1317          $form->addHidden('id', $ID);
1318          $form->addHidden('rev2[0]', $l_rev);
1319          $form->addHidden('rev2[1]', $r_rev);
1320          $form->addHidden('do', 'diff');
1321          $form->addElement(
1322               form_makeListboxField(
1323                   'difftype',
1324                   array(
1325                       'sidebyside' => $lang['diff_side'],
1326                       'inline' => $lang['diff_inline']
1327                   ),
1328                   $type,
1329                   $lang['diff_type'],
1330                   '', '',
1331                   array('class' => 'quickselect')
1332               )
1333          );
1334          $form->addElement(form_makeButton('submit', 'diff', 'Go'));
1335          $form->printForm();
1336  
1337          ptln('<p>');
1338          // link to exactly this view FS#2835
1339          echo html_diff_navigationlink($type, 'difflink', $l_rev, $r_rev ? $r_rev : $INFO['currentrev']);
1340          ptln('</p>');
1341  
1342          ptln('</div>'); // .diffoptions
1343      }
1344  
1345      /*
1346       * Display diff view table
1347       */
1348      ?>
1349      <div class="table">
1350      <table class="diff diff_<?php echo $type ?>">
1351  
1352          <?php
1353          //navigation and header
1354          if($type == 'inline') {
1355              if(!$text) { ?>
1356                  <tr>
1357                      <td class="diff-lineheader">-</td>
1358                      <td class="diffnav"><?php echo $l_nav ?></td>
1359                  </tr>
1360                  <tr>
1361                      <th class="diff-lineheader">-</th>
1362                      <th <?php echo $l_minor ?>>
1363                          <?php echo $l_head ?>
1364                      </th>
1365                  </tr>
1366              <?php } ?>
1367              <tr>
1368                  <td class="diff-lineheader">+</td>
1369                  <td class="diffnav"><?php echo $r_nav ?></td>
1370              </tr>
1371              <tr>
1372                  <th class="diff-lineheader">+</th>
1373                  <th <?php echo $r_minor ?>>
1374                      <?php echo $r_head ?>
1375                  </th>
1376              </tr>
1377          <?php } else {
1378              if(!$text) { ?>
1379                  <tr>
1380                      <td colspan="2" class="diffnav"><?php echo $l_nav ?></td>
1381                      <td colspan="2" class="diffnav"><?php echo $r_nav ?></td>
1382                  </tr>
1383              <?php } ?>
1384              <tr>
1385                  <th colspan="2" <?php echo $l_minor ?>>
1386                      <?php echo $l_head ?>
1387                  </th>
1388                  <th colspan="2" <?php echo $r_minor ?>>
1389                      <?php echo $r_head ?>
1390                  </th>
1391              </tr>
1392          <?php }
1393  
1394          //diff view
1395          echo html_insert_softbreaks($diffformatter->format($diff)); ?>
1396  
1397      </table>
1398      </div>
1399  <?php
1400  }
1401  
1402  /**
1403   * Create html for revision navigation
1404   *
1405   * @param PageChangeLog $pagelog changelog object of current page
1406   * @param string        $type    inline vs sidebyside
1407   * @param int           $l_rev   left revision timestamp
1408   * @param int           $r_rev   right revision timestamp
1409   * @return string[] html of left and right navigation elements
1410   */
1411  function html_diff_navigation($pagelog, $type, $l_rev, $r_rev) {
1412      global $INFO, $ID;
1413  
1414      // last timestamp is not in changelog, retrieve timestamp from metadata
1415      // note: when page is removed, the metadata timestamp is zero
1416      if(!$r_rev) {
1417          if(isset($INFO['meta']['last_change']['date'])) {
1418              $r_rev = $INFO['meta']['last_change']['date'];
1419          } else {
1420              $r_rev = 0;
1421          }
1422      }
1423  
1424      //retrieve revisions with additional info
1425      list($l_revs, $r_revs) = $pagelog->getRevisionsAround($l_rev, $r_rev);
1426      $l_revisions = array();
1427      if(!$l_rev) {
1428          $l_revisions[0] = array(0, "", false); //no left revision given, add dummy
1429      }
1430      foreach($l_revs as $rev) {
1431          $info = $pagelog->getRevisionInfo($rev);
1432          $l_revisions[$rev] = array(
1433              $rev,
1434              dformat($info['date']) . ' ' . editorinfo($info['user'], true) . ' ' . $info['sum'],
1435              $r_rev ? $rev >= $r_rev : false //disable?
1436          );
1437      }
1438      $r_revisions = array();
1439      if(!$r_rev) {
1440          $r_revisions[0] = array(0, "", false); //no right revision given, add dummy
1441      }
1442      foreach($r_revs as $rev) {
1443          $info = $pagelog->getRevisionInfo($rev);
1444          $r_revisions[$rev] = array(
1445              $rev,
1446              dformat($info['date']) . ' ' . editorinfo($info['user'], true) . ' ' . $info['sum'],
1447              $rev <= $l_rev //disable?
1448          );
1449      }
1450  
1451      //determine previous/next revisions
1452      $l_index = array_search($l_rev, $l_revs);
1453      $l_prev = $l_revs[$l_index + 1];
1454      $l_next = $l_revs[$l_index - 1];
1455      if($r_rev) {
1456          $r_index = array_search($r_rev, $r_revs);
1457          $r_prev = $r_revs[$r_index + 1];
1458          $r_next = $r_revs[$r_index - 1];
1459      } else {
1460          //removed page
1461          if($l_next) {
1462              $r_prev = $r_revs[0];
1463          } else {
1464              $r_prev = null;
1465          }
1466          $r_next = null;
1467      }
1468  
1469      /*
1470       * Left side:
1471       */
1472      $l_nav = '';
1473      //move back
1474      if($l_prev) {
1475          $l_nav .= html_diff_navigationlink($type, 'diffbothprevrev', $l_prev, $r_prev);
1476          $l_nav .= html_diff_navigationlink($type, 'diffprevrev', $l_prev, $r_rev);
1477      }
1478      //dropdown
1479      $form = new Doku_Form(array('action' => wl()));
1480      $form->addHidden('id', $ID);
1481      $form->addHidden('difftype', $type);
1482      $form->addHidden('rev2[1]', $r_rev);
1483      $form->addHidden('do', 'diff');
1484      $form->addElement(
1485           form_makeListboxField(
1486               'rev2[0]',
1487               $l_revisions,
1488               $l_rev,
1489               '', '', '',
1490               array('class' => 'quickselect')
1491           )
1492      );
1493      $form->addElement(form_makeButton('submit', 'diff', 'Go'));
1494      $l_nav .= $form->getForm();
1495      //move forward
1496      if($l_next && ($l_next < $r_rev || !$r_rev)) {
1497          $l_nav .= html_diff_navigationlink($type, 'diffnextrev', $l_next, $r_rev);
1498      }
1499  
1500      /*
1501       * Right side:
1502       */
1503      $r_nav = '';
1504      //move back
1505      if($l_rev < $r_prev) {
1506          $r_nav .= html_diff_navigationlink($type, 'diffprevrev', $l_rev, $r_prev);
1507      }
1508      //dropdown
1509      $form = new Doku_Form(array('action' => wl()));
1510      $form->addHidden('id', $ID);
1511      $form->addHidden('rev2[0]', $l_rev);
1512      $form->addHidden('difftype', $type);
1513      $form->addHidden('do', 'diff');
1514      $form->addElement(
1515           form_makeListboxField(
1516               'rev2[1]',
1517               $r_revisions,
1518               $r_rev,
1519               '', '', '',
1520               array('class' => 'quickselect')
1521           )
1522      );
1523      $form->addElement(form_makeButton('submit', 'diff', 'Go'));
1524      $r_nav .= $form->getForm();
1525      //move forward
1526      if($r_next) {
1527          if($pagelog->isCurrentRevision($r_next)) {
1528              $r_nav .= html_diff_navigationlink($type, 'difflastrev', $l_rev); //last revision is diff with current page
1529          } else {
1530              $r_nav .= html_diff_navigationlink($type, 'diffnextrev', $l_rev, $r_next);
1531          }
1532          $r_nav .= html_diff_navigationlink($type, 'diffbothnextrev', $l_next, $r_next);
1533      }
1534      return array($l_nav, $r_nav);
1535  }
1536  
1537  /**
1538   * Create html link to a diff defined by two revisions
1539   *
1540   * @param string $difftype display type
1541   * @param string $linktype
1542   * @param int $lrev oldest revision
1543   * @param int $rrev newest revision or null for diff with current revision
1544   * @return string html of link to a diff
1545   */
1546  function html_diff_navigationlink($difftype, $linktype, $lrev, $rrev = null) {
1547      global $ID, $lang;
1548      if(!$rrev) {
1549          $urlparam = array(
1550              'do' => 'diff',
1551              'rev' => $lrev,
1552              'difftype' => $difftype,
1553          );
1554      } else {
1555          $urlparam = array(
1556              'do' => 'diff',
1557              'rev2[0]' => $lrev,
1558              'rev2[1]' => $rrev,
1559              'difftype' => $difftype,
1560          );
1561      }
1562      return  '<a class="' . $linktype . '" href="' . wl($ID, $urlparam) . '" title="' . $lang[$linktype] . '">' .
1563                  '<span>' . $lang[$linktype] . '</span>' .
1564              '</a>' . "\n";
1565  }
1566  
1567  /**
1568   * Insert soft breaks in diff html
1569   *
1570   * @param string $diffhtml
1571   * @return string
1572   */
1573  function html_insert_softbreaks($diffhtml) {
1574      // search the diff html string for both:
1575      // - html tags, so these can be ignored
1576      // - long strings of characters without breaking characters
1577      return preg_replace_callback('/<[^>]*>|[^<> ]{12,}/','html_softbreak_callback',$diffhtml);
1578  }
1579  
1580  /**
1581   * callback which adds softbreaks
1582   *
1583   * @param array $match array with first the complete match
1584   * @return string the replacement
1585   */
1586  function html_softbreak_callback($match){
1587      // if match is an html tag, return it intact
1588      if ($match[0][0] == '<') return $match[0];
1589  
1590      // its a long string without a breaking character,
1591      // make certain characters into breaking characters by inserting a
1592      // word break opportunity (<wbr> tag) in front of them.
1593      $regex = <<< REGEX
1594  (?(?=              # start a conditional expression with a positive look ahead ...
1595  &\#?\\w{1,6};)     # ... for html entities - we don't want to split them (ok to catch some invalid combinations)
1596  &\#?\\w{1,6};      # yes pattern - a quicker match for the html entity, since we know we have one
1597  |
1598  [?/,&\#;:]         # no pattern - any other group of 'special' characters to insert a breaking character after
1599  )+                 # end conditional expression
1600  REGEX;
1601  
1602      return preg_replace('<'.$regex.'>xu','\0<wbr>',$match[0]);
1603  }
1604  
1605  /**
1606   * show warning on conflict detection
1607   *
1608   * @author Andreas Gohr <andi@splitbrain.org>
1609   *
1610   * @param string $text
1611   * @param string $summary
1612   */
1613  function html_conflict($text,$summary){
1614      global $ID;
1615      global $lang;
1616  
1617      print p_locale_xhtml('conflict');
1618      $form = new Doku_Form(array('id' => 'dw__editform'));
1619      $form->addHidden('id', $ID);
1620      $form->addHidden('wikitext', $text);
1621      $form->addHidden('summary', $summary);
1622      $form->addElement(form_makeButton('submit', 'save', $lang['btn_save'], array('accesskey'=>'s')));
1623      $form->addElement(form_makeButton('submit', 'cancel', $lang['btn_cancel']));
1624      html_form('conflict', $form);
1625      print '<br /><br /><br /><br />'.NL;
1626  }
1627  
1628  /**
1629   * Prints the global message array
1630   *
1631   * @author Andreas Gohr <andi@splitbrain.org>
1632   */
1633  function html_msgarea(){
1634      global $MSG, $MSG_shown;
1635      /** @var array $MSG */
1636      // store if the global $MSG has already been shown and thus HTML output has been started
1637      $MSG_shown = true;
1638  
1639      if(!isset($MSG)) return;
1640  
1641      $shown = array();
1642      foreach($MSG as $msg){
1643          $hash = md5($msg['msg']);
1644          if(isset($shown[$hash])) continue; // skip double messages
1645          if(info_msg_allowed($msg)){
1646              print '<div class="'.$msg['lvl'].'">';
1647              print $msg['msg'];
1648              print '</div>';
1649          }
1650          $shown[$hash] = 1;
1651      }
1652  
1653      unset($GLOBALS['MSG']);
1654  }
1655  
1656  /**
1657   * Prints the registration form
1658   *
1659   * @author Andreas Gohr <andi@splitbrain.org>
1660   */
1661  function html_register(){
1662      global $lang;
1663      global $conf;
1664      global $INPUT;
1665  
1666      $base_attrs = array('size'=>50,'required'=>'required');
1667      $email_attrs = $base_attrs + array('type'=>'email','class'=>'edit');
1668  
1669      print p_locale_xhtml('register');
1670      print '<div class="centeralign">'.NL;
1671      $form = new Doku_Form(array('id' => 'dw__register'));
1672      $form->startFieldset($lang['btn_register']);
1673      $form->addHidden('do', 'register');
1674      $form->addHidden('save', '1');
1675      $form->addElement(
1676          form_makeTextField(
1677              'login',
1678              $INPUT->post->str('login'),
1679              $lang['user'],
1680              '',
1681              'block',
1682              $base_attrs
1683          )
1684      );
1685      if (!$conf['autopasswd']) {
1686          $form->addElement(form_makePasswordField('pass', $lang['pass'], '', 'block', $base_attrs));
1687          $form->addElement(form_makePasswordField('passchk', $lang['passchk'], '', 'block', $base_attrs));
1688      }
1689      $form->addElement(
1690          form_makeTextField(
1691              'fullname',
1692              $INPUT->post->str('fullname'),
1693              $lang['fullname'],
1694              '',
1695              'block',
1696              $base_attrs
1697          )
1698      );
1699      $form->addElement(
1700          form_makeField(
1701              'email',
1702              'email',
1703              $INPUT->post->str('email'),
1704              $lang['email'],
1705              '',
1706              'block',
1707              $email_attrs
1708          )
1709      );
1710      $form->addElement(form_makeButton('submit', '', $lang['btn_register']));
1711      $form->endFieldset();
1712      html_form('register', $form);
1713  
1714      print '</div>'.NL;
1715  }
1716  
1717  /**
1718   * Print the update profile form
1719   *
1720   * @author Christopher Smith <chris@jalakai.co.uk>
1721   * @author Andreas Gohr <andi@splitbrain.org>
1722   */
1723  function html_updateprofile(){
1724      global $lang;
1725      global $conf;
1726      global $INPUT;
1727      global $INFO;
1728      /** @var AuthPlugin $auth */
1729      global $auth;
1730  
1731      print p_locale_xhtml('updateprofile');
1732      print '<div class="centeralign">'.NL;
1733  
1734      $fullname = $INPUT->post->str('fullname', $INFO['userinfo']['name'], true);
1735      $email = $INPUT->post->str('email', $INFO['userinfo']['mail'], true);
1736      $form = new Doku_Form(array('id' => 'dw__register'));
1737      $form->startFieldset($lang['profile']);
1738      $form->addHidden('do', 'profile');
1739      $form->addHidden('save', '1');
1740      $form->addElement(
1741          form_makeTextField(
1742              'login',
1743              $_SERVER['REMOTE_USER'],
1744              $lang['user'],
1745              '',
1746              'block',
1747              array('size' => '50', 'disabled' => 'disabled')
1748          )
1749      );
1750      $attr = array('size'=>'50');
1751      if (!$auth->canDo('modName')) $attr['disabled'] = 'disabled';
1752      $form->addElement(form_makeTextField('fullname', $fullname, $lang['fullname'], '', 'block', $attr));
1753      $attr = array('size'=>'50', 'class'=>'edit');
1754      if (!$auth->canDo('modMail')) $attr['disabled'] = 'disabled';
1755      $form->addElement(form_makeField('email','email', $email, $lang['email'], '', 'block', $attr));
1756      $form->addElement(form_makeTag('br'));
1757      if ($auth->canDo('modPass')) {
1758          $form->addElement(form_makePasswordField('newpass', $lang['newpass'], '', 'block', array('size'=>'50')));
1759          $form->addElement(form_makePasswordField('passchk', $lang['passchk'], '', 'block', array('size'=>'50')));
1760      }
1761      if ($conf['profileconfirm']) {
1762          $form->addElement(form_makeTag('br'));
1763          $form->addElement(
1764              form_makePasswordField(
1765                  'oldpass',
1766                  $lang['oldpass'],
1767                  '',
1768                  'block',
1769                  array('size' => '50', 'required' => 'required')
1770              )
1771          );
1772      }
1773      $form->addElement(form_makeButton('submit', '', $lang['btn_save']));
1774      $form->addElement(form_makeButton('reset', '', $lang['btn_reset']));
1775  
1776      $form->endFieldset();
1777      html_form('updateprofile', $form);
1778  
1779      if ($auth->canDo('delUser') && actionOK('profile_delete')) {
1780          $form_profiledelete = new Doku_Form(array('id' => 'dw__profiledelete'));
1781          $form_profiledelete->startFieldset($lang['profdeleteuser']);
1782          $form_profiledelete->addHidden('do', 'profile_delete');
1783          $form_profiledelete->addHidden('delete', '1');
1784          $form_profiledelete->addElement(
1785              form_makeCheckboxField(
1786                  'confirm_delete',
1787                  '1',
1788                  $lang['profconfdelete'],
1789                  'dw__confirmdelete',
1790                  '',
1791                  array('required' => 'required')
1792              )
1793          );
1794          if ($conf['profileconfirm']) {
1795              $form_profiledelete->addElement(form_makeTag('br'));
1796              $form_profiledelete->addElement(
1797                  form_makePasswordField(
1798                      'oldpass',
1799                      $lang['oldpass'],
1800                      '',
1801                      'block',
1802                      array('size' => '50', 'required' => 'required')
1803                  )
1804              );
1805          }
1806          $form_profiledelete->addElement(form_makeButton('submit', '', $lang['btn_deleteuser']));
1807          $form_profiledelete->endFieldset();
1808  
1809          html_form('profiledelete', $form_profiledelete);
1810      }
1811  
1812      print '</div>'.NL;
1813  }
1814  
1815  /**
1816   * Preprocess edit form data
1817   *
1818   * @author   Andreas Gohr <andi@splitbrain.org>
1819   *
1820   * @triggers HTML_EDITFORM_OUTPUT
1821   */
1822  function html_edit(){
1823      global $INPUT;
1824      global $ID;
1825      global $REV;
1826      global $DATE;
1827      global $PRE;
1828      global $SUF;
1829      global $INFO;
1830      global $SUM;
1831      global $lang;
1832      global $conf;
1833      global $TEXT;
1834  
1835      if ($INPUT->has('changecheck')) {
1836          $check = $INPUT->str('changecheck');
1837      } elseif(!$INFO['exists']){
1838          // $TEXT has been loaded from page template
1839          $check = md5('');
1840      } else {
1841          $check = md5($TEXT);
1842      }
1843      $mod = md5($TEXT) !== $check;
1844  
1845      $wr = $INFO['writable'] && !$INFO['locked'];
1846      $include = 'edit';
1847      if($wr){
1848          if ($REV) $include = 'editrev';
1849      }else{
1850          // check pseudo action 'source'
1851          if(!actionOK('source')){
1852              msg('Command disabled: source',-1);
1853              return;
1854          }
1855          $include = 'read';
1856      }
1857  
1858      global $license;
1859  
1860      $form = new Doku_Form(array('id' => 'dw__editform'));
1861      $form->addHidden('id', $ID);
1862      $form->addHidden('rev', $REV);
1863      $form->addHidden('date', $DATE);
1864      $form->addHidden('prefix', $PRE . '.');
1865      $form->addHidden('suffix', $SUF);
1866      $form->addHidden('changecheck', $check);
1867  
1868      $data = array('form' => $form,
1869                    'wr'   => $wr,
1870                    'media_manager' => true,
1871                    'target' => ($INPUT->has('target') && $wr) ? $INPUT->str('target') : 'section',
1872                    'intro_locale' => $include);
1873  
1874      if ($data['target'] !== 'section') {
1875          // Only emit event if page is writable, section edit data is valid and
1876          // edit target is not section.
1877          Event::createAndTrigger('HTML_EDIT_FORMSELECTION', $data, 'html_edit_form', true);
1878      } else {
1879          html_edit_form($data);
1880      }
1881      if (isset($data['intro_locale'])) {
1882          echo p_locale_xhtml($data['intro_locale']);
1883      }
1884  
1885      $form->addHidden('target', $data['target']);
1886      if ($INPUT->has('hid')) {
1887          $form->addHidden('hid', $INPUT->str('hid'));
1888      }
1889      if ($INPUT->has('codeblockOffset')) {
1890          $form->addHidden('codeblockOffset', $INPUT->str('codeblockOffset'));
1891      }
1892      $form->addElement(form_makeOpenTag('div', array('id'=>'wiki__editbar', 'class'=>'editBar')));
1893      $form->addElement(form_makeOpenTag('div', array('id'=>'size__ctl')));
1894      $form->addElement(form_makeCloseTag('div'));
1895      if ($wr) {
1896          $form->addElement(form_makeOpenTag('div', array('class'=>'editButtons')));
1897          $form->addElement(
1898              form_makeButton(
1899                  'submit',
1900                  'save',
1901                  $lang['btn_save'],
1902                  array('id' => 'edbtn__save', 'accesskey' => 's', 'tabindex' => '4')
1903              )
1904          );
1905          $form->addElement(
1906              form_makeButton(
1907                  'submit',
1908                  'preview',
1909                  $lang['btn_preview'],
1910                  array('id' => 'edbtn__preview', 'accesskey' => 'p', 'tabindex' => '5')
1911              )
1912          );
1913          $form->addElement(form_makeButton('submit', 'cancel', $lang['btn_cancel'], array('tabindex'=>'6')));
1914          $form->addElement(form_makeCloseTag('div'));
1915          $form->addElement(form_makeOpenTag('div', array('class'=>'summary')));
1916          $form->addElement(
1917              form_makeTextField(
1918                  'summary',
1919                  $SUM,
1920                  $lang['summary'],
1921                  'edit__summary',
1922                  'nowrap',
1923                  array('size' => '50', 'tabindex' => '2')
1924              )
1925          );
1926          $elem = html_minoredit();
1927          if ($elem) $form->addElement($elem);
1928          $form->addElement(form_makeCloseTag('div'));
1929      }
1930      $form->addElement(form_makeCloseTag('div'));
1931      if($wr && $conf['license']){
1932          $form->addElement(form_makeOpenTag('div', array('class'=>'license')));
1933          $out  = $lang['licenseok'];
1934          $out .= ' <a href="'.$license[$conf['license']]['url'].'" rel="license" class="urlextern"';
1935          if($conf['target']['extern']) $out .= ' target="'.$conf['target']['extern'].'"';
1936          $out .= '>'.$license[$conf['license']]['name'].'</a>';
1937          $form->addElement($out);
1938          $form->addElement(form_makeCloseTag('div'));
1939      }
1940  
1941      if ($wr) {
1942          // sets changed to true when previewed
1943          echo '<script type="text/javascript">/*<![CDATA[*/'. NL;
1944          echo 'textChanged = ' . ($mod ? 'true' : 'false');
1945          echo '/*!]]>*/</script>' . NL;
1946      } ?>
1947      <div class="editBox" role="application">
1948  
1949      <div class="toolbar group">
1950          <div id="tool__bar" class="tool__bar"><?php
1951              if ($wr && $data['media_manager']){
1952                  ?><a href="<?php echo DOKU_BASE?>lib/exe/mediamanager.php?ns=<?php echo $INFO['namespace']?>"
1953                  target="_blank"><?php echo $lang['mediaselect'] ?></a><?php
1954              }?>
1955          </div>
1956      </div>
1957      <div id="draft__status" class="draft__status">
1958          <?php
1959          $draft = new \dokuwiki\Draft($ID, $INFO['client']);
1960          if ($draft->isDraftAvailable()) {
1961              echo $draft->getDraftMessage();
1962          }
1963          ?>
1964      </div>
1965      <?php
1966  
1967      html_form('edit', $form);
1968      print '</div>'.NL;
1969  }
1970  
1971  /**
1972   * Display the default edit form
1973   *
1974   * Is the default action for HTML_EDIT_FORMSELECTION.
1975   *
1976   * @param mixed[] $param
1977   */
1978  function html_edit_form($param) {
1979      global $TEXT;
1980  
1981      if ($param['target'] !== 'section') {
1982          msg('No editor for edit target ' . hsc($param['target']) . ' found.', -1);
1983      }
1984  
1985      $attr = array('tabindex'=>'1');
1986      if (!$param['wr']) $attr['readonly'] = 'readonly';
1987  
1988      $param['form']->addElement(form_makeWikiText($TEXT, $attr));
1989  }
1990  
1991  /**
1992   * Adds a checkbox for minor edits for logged in users
1993   *
1994   * @author Andreas Gohr <andi@splitbrain.org>
1995   *
1996   * @return array|bool
1997   */
1998  function html_minoredit(){
1999      global $conf;
2000      global $lang;
2001      global $INPUT;
2002      // minor edits are for logged in users only
2003      if(!$conf['useacl'] || !$_SERVER['REMOTE_USER']){
2004          return false;
2005      }
2006  
2007      $p = array();
2008      $p['tabindex'] = 3;
2009      if($INPUT->bool('minor')) $p['checked']='checked';
2010      return form_makeCheckboxField('minor', '1', $lang['minoredit'], 'minoredit', 'nowrap', $p);
2011  }
2012  
2013  /**
2014   * prints some debug info
2015   *
2016   * @author Andreas Gohr <andi@splitbrain.org>
2017   */
2018  function html_debug(){
2019      global $conf;
2020      global $lang;
2021      /** @var AuthPlugin $auth */
2022      global $auth;
2023      global $INFO;
2024  
2025      //remove sensitive data
2026      $cnf = $conf;
2027      debug_guard($cnf);
2028      $nfo = $INFO;
2029      debug_guard($nfo);
2030      $ses = $_SESSION;
2031      debug_guard($ses);
2032  
2033      print '<html><body>';
2034  
2035      print '<p>When reporting bugs please send all the following ';
2036      print 'output as a mail to andi@splitbrain.org ';
2037      print 'The best way to do this is to save this page in your browser</p>';
2038  
2039      print '<b>$INFO:</b><pre>';
2040      print_r($nfo);
2041      print '</pre>';
2042  
2043      print '<b>$_SERVER:</b><pre>';
2044      print_r($_SERVER);
2045      print '</pre>';
2046  
2047      print '<b>$conf:</b><pre>';
2048      print_r($cnf);
2049      print '</pre>';
2050  
2051      print '<b>DOKU_BASE:</b><pre>';
2052      print DOKU_BASE;
2053      print '</pre>';
2054  
2055      print '<b>abs DOKU_BASE:</b><pre>';
2056      print DOKU_URL;
2057      print '</pre>';
2058  
2059      print '<b>rel DOKU_BASE:</b><pre>';
2060      print dirname($_SERVER['PHP_SELF']).'/';
2061      print '</pre>';
2062  
2063      print '<b>PHP Version:</b><pre>';
2064      print phpversion();
2065      print '</pre>';
2066  
2067      print '<b>locale:</b><pre>';
2068      print setlocale(LC_ALL,0);
2069      print '</pre>';
2070  
2071      print '<b>encoding:</b><pre>';
2072      print $lang['encoding'];
2073      print '</pre>';
2074  
2075      if($auth){
2076          print '<b>Auth backend capabilities:</b><pre>';
2077          foreach ($auth->getCapabilities() as $cando){
2078              print '   '.str_pad($cando,16) . ' => ' . (int)$auth->canDo($cando) . NL;
2079          }
2080          print '</pre>';
2081      }
2082  
2083      print '<b>$_SESSION:</b><pre>';
2084      print_r($ses);
2085      print '</pre>';
2086  
2087      print '<b>Environment:</b><pre>';
2088      print_r($_ENV);
2089      print '</pre>';
2090  
2091      print '<b>PHP settings:</b><pre>';
2092      $inis = ini_get_all();
2093      print_r($inis);
2094      print '</pre>';
2095  
2096      if (function_exists('apache_get_version')) {
2097          $apache = array();
2098          $apache['version'] = apache_get_version();
2099  
2100          if (function_exists('apache_get_modules')) {
2101              $apache['modules'] = apache_get_modules();
2102          }
2103          print '<b>Apache</b><pre>';
2104          print_r($apache);
2105          print '</pre>';
2106      }
2107  
2108      print '</body></html>';
2109  }
2110  
2111  /**
2112   * Form to request a new password for an existing account
2113   *
2114   * @author Benoit Chesneau <benoit@bchesneau.info>
2115   * @author Andreas Gohr <gohr@cosmocode.de>
2116   */
2117  function html_resendpwd() {
2118      global $lang;
2119      global $conf;
2120      global $INPUT;
2121  
2122      $token = preg_replace('/[^a-f0-9]+/','',$INPUT->str('pwauth'));
2123  
2124      if(!$conf['autopasswd'] && $token){
2125          print p_locale_xhtml('resetpwd');
2126          print '<div class="centeralign">'.NL;
2127          $form = new Doku_Form(array('id' => 'dw__resendpwd'));
2128          $form->startFieldset($lang['btn_resendpwd']);
2129          $form->addHidden('token', $token);
2130          $form->addHidden('do', 'resendpwd');
2131  
2132          $form->addElement(form_makePasswordField('pass', $lang['pass'], '', 'block', array('size'=>'50')));
2133          $form->addElement(form_makePasswordField('passchk', $lang['passchk'], '', 'block', array('size'=>'50')));
2134  
2135          $form->addElement(form_makeButton('submit', '', $lang['btn_resendpwd']));
2136          $form->endFieldset();
2137          html_form('resendpwd', $form);
2138          print '</div>'.NL;
2139      }else{
2140          print p_locale_xhtml('resendpwd');
2141          print '<div class="centeralign">'.NL;
2142          $form = new Doku_Form(array('id' => 'dw__resendpwd'));
2143          $form->startFieldset($lang['resendpwd']);
2144          $form->addHidden('do', 'resendpwd');
2145          $form->addHidden('save', '1');
2146          $form->addElement(form_makeTag('br'));
2147          $form->addElement(form_makeTextField('login', $INPUT->post->str('login'), $lang['user'], '', 'block'));
2148          $form->addElement(form_makeTag('br'));
2149          $form->addElement(form_makeTag('br'));
2150          $form->addElement(form_makeButton('submit', '', $lang['btn_resendpwd']));
2151          $form->endFieldset();
2152          html_form('resendpwd', $form);
2153          print '</div>'.NL;
2154      }
2155  }
2156  
2157  /**
2158   * Return the TOC rendered to XHTML
2159   *
2160   * @author Andreas Gohr <andi@splitbrain.org>
2161   *
2162   * @param array $toc
2163   * @return string html
2164   */
2165  function html_TOC($toc){
2166      if(!count($toc)) return '';
2167      global $lang;
2168      $out  = '<!-- TOC START -->'.DOKU_LF;
2169      $out .= '<div id="dw__toc" class="dw__toc">'.DOKU_LF;
2170      $out .= '<h3 class="toggle">';
2171      $out .= $lang['toc'];
2172      $out .= '</h3>'.DOKU_LF;
2173      $out .= '<div>'.DOKU_LF;
2174      $out .= html_buildlist($toc,'toc','html_list_toc','html_li_default',true);
2175      $out .= '</div>'.DOKU_LF.'</div>'.DOKU_LF;
2176      $out .= '<!-- TOC END -->'.DOKU_LF;
2177      return $out;
2178  }
2179  
2180  /**
2181   * Callback for html_buildlist
2182   *
2183   * @param array $item
2184   * @return string html
2185   */
2186  function html_list_toc($item){
2187      if(isset($item['hid'])){
2188          $link = '#'.$item['hid'];
2189      }else{
2190          $link = $item['link'];
2191      }
2192  
2193      return '<a href="'.$link.'">'.hsc($item['title']).'</a>';
2194  }
2195  
2196  /**
2197   * Helper function to build TOC items
2198   *
2199   * Returns an array ready to be added to a TOC array
2200   *
2201   * @param string $link  - where to link (if $hash set to '#' it's a local anchor)
2202   * @param string $text  - what to display in the TOC
2203   * @param int    $level - nesting level
2204   * @param string $hash  - is prepended to the given $link, set blank if you want full links
2205   * @return array the toc item
2206   */
2207  function html_mktocitem($link, $text, $level, $hash='#'){
2208      return  array( 'link'  => $hash.$link,
2209              'title' => $text,
2210              'type'  => 'ul',
2211              'level' => $level);
2212  }
2213  
2214  /**
2215   * Output a Doku_Form object.
2216   * Triggers an event with the form name: HTML_{$name}FORM_OUTPUT
2217   *
2218   * @author Tom N Harris <tnharris@whoopdedo.org>
2219   *
2220   * @param string     $name The name of the form
2221   * @param Doku_Form  $form The form
2222   */
2223  function html_form($name, &$form) {
2224      // Safety check in case the caller forgets.
2225      $form->endFieldset();
2226      Event::createAndTrigger('HTML_'.strtoupper($name).'FORM_OUTPUT', $form, 'html_form_output', false);
2227  }
2228  
2229  /**
2230   * Form print function.
2231   * Just calls printForm() on the data object.
2232   *
2233   * @param Doku_Form $data The form
2234   */
2235  function html_form_output($data) {
2236      $data->printForm();
2237  }
2238  
2239  /**
2240   * Embed a flash object in HTML
2241   *
2242   * This will create the needed HTML to embed a flash movie in a cross browser
2243   * compatble way using valid XHTML
2244   *
2245   * The parameters $params, $flashvars and $atts need to be associative arrays.
2246   * No escaping needs to be done for them. The alternative content *has* to be
2247   * escaped because it is used as is. If no alternative content is given
2248   * $lang['noflash'] is used.
2249   *
2250   * @author Andreas Gohr <andi@splitbrain.org>
2251   * @link   http://latrine.dgx.cz/how-to-correctly-insert-a-flash-into-xhtml
2252   *
2253   * @param string $swf      - the SWF movie to embed
2254   * @param int $width       - width of the flash movie in pixels
2255   * @param int $height      - height of the flash movie in pixels
2256   * @param array $params    - additional parameters (<param>)
2257   * @param array $flashvars - parameters to be passed in the flashvar parameter
2258   * @param array $atts      - additional attributes for the <object> tag
2259   * @param string $alt      - alternative content (is NOT automatically escaped!)
2260   * @return string         - the XHTML markup
2261   */
2262  function html_flashobject($swf,$width,$height,$params=null,$flashvars=null,$atts=null,$alt=''){
2263      global $lang;
2264  
2265      $out = '';
2266  
2267      // prepare the object attributes
2268      if(is_null($atts)) $atts = array();
2269      $atts['width']  = (int) $width;
2270      $atts['height'] = (int) $height;
2271      if(!$atts['width'])  $atts['width']  = 425;
2272      if(!$atts['height']) $atts['height'] = 350;
2273  
2274      // add object attributes for standard compliant browsers
2275      $std = $atts;
2276      $std['type'] = 'application/x-shockwave-flash';
2277      $std['data'] = $swf;
2278  
2279      // add object attributes for IE
2280      $ie  = $atts;
2281      $ie['classid'] = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
2282  
2283      // open object (with conditional comments)
2284      $out .= '<!--[if !IE]> -->'.NL;
2285      $out .= '<object '.buildAttributes($std).'>'.NL;
2286      $out .= '<!-- <![endif]-->'.NL;
2287      $out .= '<!--[if IE]>'.NL;
2288      $out .= '<object '.buildAttributes($ie).'>'.NL;
2289      $out .= '    <param name="movie" value="'.hsc($swf).'" />'.NL;
2290      $out .= '<!--><!-- -->'.NL;
2291  
2292      // print params
2293      if(is_array($params)) foreach($params as $key => $val){
2294          $out .= '  <param name="'.hsc($key).'" value="'.hsc($val).'" />'.NL;
2295      }
2296  
2297      // add flashvars
2298      if(is_array($flashvars)){
2299          $out .= '  <param name="FlashVars" value="'.buildURLparams($flashvars).'" />'.NL;
2300      }
2301  
2302      // alternative content
2303      if($alt){
2304          $out .= $alt.NL;
2305      }else{
2306          $out .= $lang['noflash'].NL;
2307      }
2308  
2309      // finish
2310      $out .= '</object>'.NL;
2311      $out .= '<!-- <![endif]-->'.NL;
2312  
2313      return $out;
2314  }
2315  
2316  /**
2317   * Prints HTML code for the given tab structure
2318   *
2319   * @param array  $tabs        tab structure
2320   * @param string $current_tab the current tab id
2321   */
2322  function html_tabs($tabs, $current_tab = null) {
2323      echo '<ul class="tabs">'.NL;
2324  
2325      foreach($tabs as $id => $tab) {
2326          html_tab($tab['href'], $tab['caption'], $id === $current_tab);
2327      }
2328  
2329      echo '</ul>'.NL;
2330  }
2331  
2332  /**
2333   * Prints a single tab
2334   *
2335   * @author Kate Arzamastseva <pshns@ukr.net>
2336   * @author Adrian Lang <mail@adrianlang.de>
2337   *
2338   * @param string $href - tab href
2339   * @param string $caption - tab caption
2340   * @param boolean $selected - is tab selected
2341   */
2342  
2343  function html_tab($href, $caption, $selected=false) {
2344      $tab = '<li>';
2345      if ($selected) {
2346          $tab .= '<strong>';
2347      } else {
2348          $tab .= '<a href="' . hsc($href) . '">';
2349      }
2350      $tab .= hsc($caption)
2351           .  '</' . ($selected ? 'strong' : 'a') . '>'
2352           .  '</li>'.NL;
2353      echo $tab;
2354  }
2355  
2356  /**
2357   * Display size change
2358   *
2359   * @param int $sizechange - size of change in Bytes
2360   * @param Doku_Form $form - form to add elements to
2361   */
2362  
2363  function html_sizechange($sizechange, Doku_Form $form) {
2364      if(isset($sizechange)) {
2365          $class = 'sizechange';
2366          $value = filesize_h(abs($sizechange));
2367          if($sizechange > 0) {
2368              $class .= ' positive';
2369              $value = '+' . $value;
2370          } elseif($sizechange < 0) {
2371              $class .= ' negative';
2372              $value = '-' . $value;
2373          } else {
2374              $value = '±' . $value;
2375          }
2376          $form->addElement(form_makeOpenTag('span', array('class' => $class)));
2377          $form->addElement($value);
2378          $form->addElement(form_makeCloseTag('span'));
2379      }
2380  }