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