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 */ 8use dokuwiki\Ui\MediaRevisions; 9use dokuwiki\Form\Form; 10use dokuwiki\Action\Denied; 11use dokuwiki\Action\Locked; 12use dokuwiki\ChangeLog\PageChangeLog; 13use dokuwiki\Extension\AuthPlugin; 14use dokuwiki\Extension\Event; 15use dokuwiki\Ui\Backlinks; 16use dokuwiki\Ui\Editor; 17use dokuwiki\Ui\Index; 18use dokuwiki\Ui\Login; 19use dokuwiki\Ui\PageConflict; 20use dokuwiki\Ui\PageDiff; 21use dokuwiki\Ui\PageDraft; 22use dokuwiki\Ui\PageRevisions; 23use dokuwiki\Ui\PageView; 24use dokuwiki\Ui\Recent; 25use dokuwiki\Ui\UserProfile; 26use dokuwiki\Ui\UserRegister; 27use dokuwiki\Ui\UserResendPwd; 28use dokuwiki\Utf8\Clean; 29 30if (!defined('SEC_EDIT_PATTERN')) { 31 define('SEC_EDIT_PATTERN', '#<!-- EDIT({.*?}) -->#'); 32} 33 34 35/** 36 * Convenience function to quickly build a wikilink 37 * 38 * @author Andreas Gohr <andi@splitbrain.org> 39 * @param string $id id of the target page 40 * @param string $name the name of the link, i.e. the text that is displayed 41 * @param string|array $search search string(s) that shall be highlighted in the target page 42 * @return string the HTML code of the link 43 */ 44function html_wikilink($id, $name = null, $search = '') 45{ 46 /** @var Doku_Renderer_xhtml $xhtml_renderer */ 47 static $xhtml_renderer = null; 48 if (is_null($xhtml_renderer)) { 49 $xhtml_renderer = p_get_renderer('xhtml'); 50 } 51 52 return $xhtml_renderer->internallink($id, $name, $search, true, 'navigation'); 53} 54 55/** 56 * The loginform 57 * 58 * @author Andreas Gohr <andi@splitbrain.org> 59 * 60 * @param bool $svg Whether to show svg icons in the register and resendpwd links or not 61 * @deprecated 2020-07-18 62 */ 63function html_login($svg = false) 64{ 65 dbg_deprecated(Login::class .'::show()'); 66 (new Login($svg))->show(); 67} 68 69 70/** 71 * Denied page content 72 * 73 * @deprecated 2020-07-18 not called anymore, see inc/Action/Denied::tplContent() 74 */ 75function html_denied() 76{ 77 dbg_deprecated(Denied::class .'::showBanner()'); 78 (new Denied())->showBanner(); 79} 80 81/** 82 * inserts section edit buttons if wanted or removes the markers 83 * 84 * @author Andreas Gohr <andi@splitbrain.org> 85 * 86 * @param string $text 87 * @param bool $show show section edit buttons? 88 * @return string 89 */ 90function html_secedit($text, $show = true) 91{ 92 global $INFO; 93 94 if ((isset($INFO) && !$INFO['writable']) || !$show || (isset($INFO) && $INFO['rev'])) { 95 return preg_replace(SEC_EDIT_PATTERN, '', $text); 96 } 97 98 return preg_replace_callback( 99 SEC_EDIT_PATTERN, 100 'html_secedit_button', 101 $text 102 ); 103} 104 105/** 106 * prepares section edit button data for event triggering 107 * used as a callback in html_secedit 108 * 109 * @author Andreas Gohr <andi@splitbrain.org> 110 * 111 * @param array $matches matches with regexp 112 * @return string 113 * @triggers HTML_SECEDIT_BUTTON 114 */ 115function html_secedit_button($matches) 116{ 117 $json = htmlspecialchars_decode($matches[1], ENT_QUOTES); 118 119 try { 120 $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR); 121 } catch (JsonException $e) { 122 return ''; 123 } 124 $data['target'] = strtolower($data['target']); 125 $data['hid'] = strtolower($data['hid'] ?? ''); 126 127 return Event::createAndTrigger( 128 'HTML_SECEDIT_BUTTON', 129 $data, 130 'html_secedit_get_button' 131 ); 132} 133 134/** 135 * prints a section editing button 136 * used as default action form HTML_SECEDIT_BUTTON 137 * 138 * @author Adrian Lang <lang@cosmocode.de> 139 * 140 * @param array $data name, section id and target 141 * @return string html 142 */ 143function html_secedit_get_button($data) 144{ 145 global $ID; 146 global $INFO; 147 148 if (!isset($data['name']) || $data['name'] === '') return ''; 149 150 $name = $data['name']; 151 unset($data['name']); 152 153 $secid = $data['secid']; 154 unset($data['secid']); 155 156 $params = array_merge( 157 ['do' => 'edit', 'rev' => $INFO['lastmod'], 'summary' => '['.$name.'] '], 158 $data 159 ); 160 161 $html = '<div class="secedit editbutton_'.$data['target'] .' editbutton_'.$secid .'">'; 162 $html.= html_btn('secedit', $ID, '', $params, 'post', $name); 163 $html.= '</div>'; 164 return $html; 165} 166 167/** 168 * Just the back to top button (in its own form) 169 * 170 * @author Andreas Gohr <andi@splitbrain.org> 171 * 172 * @return string html 173 */ 174function html_topbtn() 175{ 176 global $lang; 177 178 return '<a class="nolink" href="#dokuwiki__top">' 179 .'<button class="button" onclick="window.scrollTo(0, 0)" title="'. $lang['btn_top'] .'">' 180 . $lang['btn_top'] 181 .'</button></a>'; 182} 183 184/** 185 * Displays a button (using its own form) 186 * If tooltip exists, the access key tooltip is replaced. 187 * 188 * @author Andreas Gohr <andi@splitbrain.org> 189 * 190 * @param string $name 191 * @param string $id 192 * @param string $akey access key 193 * @param string[] $params key-value pairs added as hidden inputs 194 * @param string $method 195 * @param string $tooltip 196 * @param bool|string $label label text, false: lookup btn_$name in localization 197 * @param string $svg (optional) svg code, inserted into the button 198 * @return string 199 */ 200function html_btn($name, $id, $akey, $params, $method = 'get', $tooltip = '', $label = false, $svg = null) 201{ 202 global $conf; 203 global $lang; 204 205 if (!$label) 206 $label = $lang['btn_'.$name]; 207 208 //filter id (without urlencoding) 209 $id = idfilter($id, false); 210 211 //make nice URLs even for buttons 212 if ($conf['userewrite'] == 2) { 213 $script = DOKU_BASE.DOKU_SCRIPT.'/'.$id; 214 } elseif ($conf['userewrite']) { 215 $script = DOKU_BASE.$id; 216 } else { 217 $script = DOKU_BASE.DOKU_SCRIPT; 218 $params['id'] = $id; 219 } 220 221 $html = '<form class="button btn_'.$name.'" method="'.$method.'" action="'.$script.'"><div class="no">'; 222 223 if (is_array($params)) { 224 foreach ($params as $key => $val) { 225 $html .= '<input type="hidden" name="'.$key.'" value="'.hsc($val).'" />'; 226 } 227 } 228 229 $tip = empty($tooltip) ? hsc($label) : hsc($tooltip); 230 231 $html .= '<button type="submit" '; 232 if ($akey) { 233 $tip .= ' ['.strtoupper($akey).']'; 234 $html .= 'accesskey="'.$akey.'" '; 235 } 236 $html .= 'title="'.$tip.'">'; 237 if ($svg) { 238 $html .= '<span>'. hsc($label) .'</span>'. inlineSVG($svg); 239 } else { 240 $html .= hsc($label); 241 } 242 $html .= '</button>'; 243 $html .= '</div></form>'; 244 245 return $html; 246} 247/** 248 * show a revision warning 249 * 250 * @author Szymon Olewniczak <dokuwiki@imz.re> 251 * @deprecated 2020-07-18 252 */ 253function html_showrev() 254{ 255 dbg_deprecated(PageView::class .'::showrev()'); 256} 257 258/** 259 * Show a wiki page 260 * 261 * @author Andreas Gohr <andi@splitbrain.org> 262 * 263 * @param null|string $txt wiki text or null for showing $ID 264 * @deprecated 2020-07-18 265 */ 266function html_show($txt = null) 267{ 268 dbg_deprecated(PageView::class .'::show()'); 269 (new PageView($txt))->show(); 270} 271 272/** 273 * ask the user about how to handle an exisiting draft 274 * 275 * @author Andreas Gohr <andi@splitbrain.org> 276 * @deprecated 2020-07-18 277 */ 278function html_draft() 279{ 280 dbg_deprecated(PageDraft::class .'::show()'); 281 (new PageDraft())->show(); 282} 283 284/** 285 * Highlights searchqueries in HTML code 286 * 287 * @author Andreas Gohr <andi@splitbrain.org> 288 * @author Harry Fuecks <hfuecks@gmail.com> 289 * 290 * @param string $html 291 * @param array|string $phrases 292 * @return string html 293 */ 294function html_hilight($html, $phrases) 295{ 296 $phrases = (array) $phrases; 297 $phrases = array_map('preg_quote_cb', $phrases); 298 $phrases = array_map('ft_snippet_re_preprocess', $phrases); 299 $phrases = array_filter($phrases); 300 301 $regex = implode('|', $phrases); 302 303 if ($regex === '') return $html; 304 if (!Clean::isUtf8($regex)) return $html; 305 306 return @preg_replace_callback("/((<[^>]*)|$regex)/ui", function ($match) { 307 $hlight = unslash($match[0]); 308 if (!isset($match[2])) { 309 $hlight = '<span class="search_hit">'.$hlight.'</span>'; 310 } 311 return $hlight; 312 }, $html); 313} 314 315/** 316 * Display error on locked pages 317 * 318 * @author Andreas Gohr <andi@splitbrain.org> 319 * @deprecated 2020-07-18 not called anymore, see inc/Action/Locked::tplContent() 320 */ 321function html_locked() 322{ 323 dbg_deprecated(Locked::class .'::showBanner()'); 324 (new Locked())->showBanner(); 325} 326 327/** 328 * list old revisions 329 * 330 * @author Andreas Gohr <andi@splitbrain.org> 331 * @author Ben Coburn <btcoburn@silicodon.net> 332 * @author Kate Arzamastseva <pshns@ukr.net> 333 * 334 * @param int $first skip the first n changelog lines 335 * @param string $media_id id of media, or empty for current page 336 * @deprecated 2020-07-18 337 */ 338function html_revisions($first = -1, $media_id = '') 339{ 340 dbg_deprecated(PageRevisions::class .'::show()'); 341 if ($media_id) { 342 (new MediaRevisions($media_id))->show($first); 343 } else { 344 global $INFO; 345 (new PageRevisions($INFO['id']))->show($first); 346 } 347} 348 349/** 350 * display recent changes 351 * 352 * @author Andreas Gohr <andi@splitbrain.org> 353 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 354 * @author Ben Coburn <btcoburn@silicodon.net> 355 * @author Kate Arzamastseva <pshns@ukr.net> 356 * 357 * @param int $first 358 * @param string $show_changes 359 * @deprecated 2020-07-18 360 */ 361function html_recent($first = 0, $show_changes = 'both') 362{ 363 dbg_deprecated(Recent::class .'::show()'); 364 (new Recent($first, $show_changes))->show(); 365} 366 367/** 368 * Display page index 369 * 370 * @author Andreas Gohr <andi@splitbrain.org> 371 * 372 * @param string $ns 373 * @deprecated 2020-07-18 374 */ 375function html_index($ns) 376{ 377 dbg_deprecated(Index::class .'::show()'); 378 (new Index($ns))->show(); 379} 380 381/** 382 * Index tree item formatter for html_buildlist() 383 * 384 * User function for html_buildlist() 385 * 386 * @author Andreas Gohr <andi@splitbrain.org> 387 * 388 * @param array $item 389 * @return string 390 * @deprecated 2020-07-18 391 */ 392function html_list_index($item) 393{ 394 dbg_deprecated(Index::class .'::formatListItem()'); 395 return (new Index())->formatListItem($item); 396} 397 398/** 399 * Index list item formatter for html_buildlist() 400 * 401 * This user function is used in html_buildlist to build the 402 * <li> tags for namespaces when displaying the page index 403 * it gives different classes to opened or closed "folders" 404 * 405 * @author Andreas Gohr <andi@splitbrain.org> 406 * 407 * @param array $item 408 * @return string html 409 * @deprecated 2020-07-18 410 */ 411function html_li_index($item) 412{ 413 dbg_deprecated(Index::class .'::tagListItem()'); 414 return (new Index())->tagListItem($item); 415} 416 417/** 418 * Default list item formatter for html_buildlist() 419 * 420 * @author Andreas Gohr <andi@splitbrain.org> 421 * 422 * @param array $item 423 * @return string html 424 * @deprecated 2020-07-18 425 */ 426function html_li_default($item) 427{ 428 return '<li class="level'.$item['level'].'">'; 429} 430 431/** 432 * Build an unordered list 433 * 434 * Build an unordered list from the given $data array 435 * Each item in the array has to have a 'level' property 436 * the item itself gets printed by the given $func user 437 * function. The second and optional function is used to 438 * print the <li> tag. Both user function need to accept 439 * a single item. 440 * 441 * Both user functions can be given as array to point to 442 * a member of an object. 443 * 444 * @author Andreas Gohr <andi@splitbrain.org> 445 * 446 * @param array $data array with item arrays 447 * @param string $class class of ul wrapper 448 * @param callable $func callback to print an list item 449 * @param callable $lifunc (optional) callback to the opening li tag 450 * @param bool $forcewrapper (optional) Trigger building a wrapper ul if the first level is 451 * 0 (we have a root object) or 1 (just the root content) 452 * @return string html of an unordered list 453 */ 454function html_buildlist($data, $class, $func, $lifunc = null, $forcewrapper = false) 455{ 456 if ($data === []) { 457 return ''; 458 } 459 460 $firstElement = reset($data); 461 $start_level = $firstElement['level']; 462 $level = $start_level; 463 $html = ''; 464 $open = 0; 465 466 // set callback function to build the <li> tag, formerly defined as html_li_default() 467 if (!is_callable($lifunc)) { 468 $lifunc = static fn($item) => '<li class="level'.$item['level'].'">'; 469 } 470 471 foreach ($data as $item) { 472 if ($item['level'] > $level) { 473 //open new list 474 for ($i = 0; $i < ($item['level'] - $level); $i++) { 475 if ($i) $html .= '<li class="clear">'; 476 $html .= "\n".'<ul class="'.$class.'">'."\n"; 477 $open++; 478 } 479 $level = $item['level']; 480 } elseif ($item['level'] < $level) { 481 //close last item 482 $html .= '</li>'."\n"; 483 while ($level > $item['level'] && $open > 0) { 484 //close higher lists 485 $html .= '</ul>'."\n".'</li>'."\n"; 486 $level--; 487 $open--; 488 } 489 } elseif ($html !== '') { 490 //close previous item 491 $html .= '</li>'."\n"; 492 } 493 494 //print item 495 $html .= call_user_func($lifunc, $item); 496 $html .= '<div class="li">'; 497 498 $html .= call_user_func($func, $item); 499 $html .= '</div>'; 500 } 501 502 //close remaining items and lists 503 $html .= '</li>'."\n"; 504 while ($open-- > 0) { 505 $html .= '</ul></li>'."\n"; 506 } 507 508 if ($forcewrapper || $start_level < 2) { 509 // Trigger building a wrapper ul if the first level is 510 // 0 (we have a root object) or 1 (just the root content) 511 $html = "\n".'<ul class="'.$class.'">'."\n".$html.'</ul>'."\n"; 512 } 513 514 return $html; 515} 516 517/** 518 * display backlinks 519 * 520 * @author Andreas Gohr <andi@splitbrain.org> 521 * @author Michael Klier <chi@chimeric.de> 522 * @deprecated 2020-07-18 523 */ 524function html_backlinks() 525{ 526 dbg_deprecated(Backlinks::class .'::show()'); 527 (new Backlinks())->show(); 528} 529 530/** 531 * Get header of diff HTML 532 * 533 * @param string $l_rev Left revisions 534 * @param string $r_rev Right revision 535 * @param string $id Page id, if null $ID is used 536 * @param bool $media If it is for media files 537 * @param bool $inline Return the header on a single line 538 * @return string[] HTML snippets for diff header 539 * @deprecated 2020-07-18 540 */ 541function html_diff_head($l_rev, $r_rev, $id = null, $media = false, $inline = false) 542{ 543 dbg_deprecated('see '. PageDiff::class .'::buildDiffHead()'); 544 return ['', '', '', '']; 545} 546 547/** 548 * Show diff 549 * between current page version and provided $text 550 * or between the revisions provided via GET or POST 551 * 552 * @author Andreas Gohr <andi@splitbrain.org> 553 * @param string $text when non-empty: compare with this text with most current version 554 * @param bool $intro display the intro text 555 * @param string $type type of the diff (inline or sidebyside) 556 * @deprecated 2020-07-18 557 */ 558function html_diff($text = '', $intro = true, $type = null) 559{ 560 dbg_deprecated(PageDiff::class .'::show()'); 561 global $INFO; 562 (new PageDiff($INFO['id']))->compareWith($text)->preference([ 563 'showIntro' => $intro, 564 'difftype' => $type, 565 ])->show(); 566} 567 568/** 569 * Create html for revision navigation 570 * 571 * @param PageChangeLog $pagelog changelog object of current page 572 * @param string $type inline vs sidebyside 573 * @param int $l_rev left revision timestamp 574 * @param int $r_rev right revision timestamp 575 * @return string[] html of left and right navigation elements 576 * @deprecated 2020-07-18 577 */ 578function html_diff_navigation($pagelog, $type, $l_rev, $r_rev) 579{ 580 dbg_deprecated('see '. PageDiff::class .'::buildRevisionsNavigation()'); 581 return ['', '']; 582} 583 584/** 585 * Create html link to a diff defined by two revisions 586 * 587 * @param string $difftype display type 588 * @param string $linktype 589 * @param int $lrev oldest revision 590 * @param int $rrev newest revision or null for diff with current revision 591 * @return string html of link to a diff 592 * @deprecated 2020-07-18 593 */ 594function html_diff_navigationlink($difftype, $linktype, $lrev, $rrev = null) 595{ 596 dbg_deprecated('see '. PageDiff::class .'::diffViewlink()'); 597 return ''; 598} 599 600/** 601 * Insert soft breaks in diff html 602 * 603 * @param string $diffhtml 604 * @return string 605 * @deprecated 2020-07-18 606 */ 607function html_insert_softbreaks($diffhtml) 608{ 609 dbg_deprecated(PageDiff::class .'::insertSoftbreaks()'); 610 return (new PageDiff())->insertSoftbreaks($diffhtml); 611} 612 613/** 614 * show warning on conflict detection 615 * 616 * @author Andreas Gohr <andi@splitbrain.org> 617 * 618 * @param string $text 619 * @param string $summary 620 * @deprecated 2020-07-18 621 */ 622function html_conflict($text, $summary) 623{ 624 dbg_deprecated(PageConflict::class .'::show()'); 625 (new PageConflict($text, $summary))->show(); 626} 627 628/** 629 * Prints the global message array 630 * 631 * @author Andreas Gohr <andi@splitbrain.org> 632 */ 633function html_msgarea() 634{ 635 global $MSG, $MSG_shown; 636 /** @var array $MSG */ 637 // store if the global $MSG has already been shown and thus HTML output has been started 638 $MSG_shown = true; 639 640 if (!isset($MSG)) return; 641 642 $shown = []; 643 foreach ($MSG as $msg) { 644 $hash = md5($msg['msg']); 645 if (isset($shown[$hash])) continue; // skip double messages 646 if (info_msg_allowed($msg)) { 647 print '<div class="'.$msg['lvl'].'">'; 648 print $msg['msg']; 649 print '</div>'; 650 } 651 $shown[$hash] = 1; 652 } 653 654 unset($GLOBALS['MSG']); 655} 656 657/** 658 * Prints the registration form 659 * 660 * @author Andreas Gohr <andi@splitbrain.org> 661 * @deprecated 2020-07-18 662 */ 663function html_register() 664{ 665 dbg_deprecated(UserRegister::class .'::show()'); 666 (new UserRegister())->show(); 667} 668 669/** 670 * Print the update profile form 671 * 672 * @author Christopher Smith <chris@jalakai.co.uk> 673 * @author Andreas Gohr <andi@splitbrain.org> 674 * @deprecated 2020-07-18 675 */ 676function html_updateprofile() 677{ 678 dbg_deprecated(UserProfile::class .'::show()'); 679 (new UserProfile())->show(); 680} 681 682/** 683 * Preprocess edit form data 684 * 685 * @author Andreas Gohr <andi@splitbrain.org> 686 * 687 * @deprecated 2020-07-18 688 */ 689function html_edit() 690{ 691 dbg_deprecated(Editor::class .'::show()'); 692 (new Editor())->show(); 693} 694 695/** 696 * Display the default edit form 697 * 698 * Is the default action for HTML_EDIT_FORMSELECTION. 699 * 700 * @param array $param 701 * @deprecated 2020-07-18 702 */ 703function html_edit_form($param) 704{ 705 dbg_deprecated(Editor::class .'::addTextarea()'); 706 (new Editor())->addTextarea($param); 707} 708 709/** 710 * prints some debug info 711 * 712 * @author Andreas Gohr <andi@splitbrain.org> 713 */ 714function html_debug() 715{ 716 global $conf; 717 global $lang; 718 /** @var AuthPlugin $auth */ 719 global $auth; 720 global $INFO; 721 722 //remove sensitive data 723 $cnf = $conf; 724 debug_guard($cnf); 725 $nfo = $INFO; 726 debug_guard($nfo); 727 $ses = $_SESSION; 728 debug_guard($ses); 729 730 print '<html><body>'; 731 732 print '<p>When reporting bugs please send all the following '; 733 print 'output as a mail to andi@splitbrain.org '; 734 print 'The best way to do this is to save this page in your browser</p>'; 735 736 print '<b>$INFO:</b><pre>'; 737 print_r($nfo); 738 print '</pre>'; 739 740 print '<b>$_SERVER:</b><pre>'; 741 print_r($_SERVER); 742 print '</pre>'; 743 744 print '<b>$conf:</b><pre>'; 745 print_r($cnf); 746 print '</pre>'; 747 748 print '<b>DOKU_BASE:</b><pre>'; 749 print DOKU_BASE; 750 print '</pre>'; 751 752 print '<b>abs DOKU_BASE:</b><pre>'; 753 print DOKU_URL; 754 print '</pre>'; 755 756 print '<b>rel DOKU_BASE:</b><pre>'; 757 print dirname($_SERVER['PHP_SELF']).'/'; 758 print '</pre>'; 759 760 print '<b>PHP Version:</b><pre>'; 761 print phpversion(); 762 print '</pre>'; 763 764 print '<b>locale:</b><pre>'; 765 print setlocale(LC_ALL, 0); 766 print '</pre>'; 767 768 print '<b>encoding:</b><pre>'; 769 print $lang['encoding']; 770 print '</pre>'; 771 772 if ($auth) { 773 print '<b>Auth backend capabilities:</b><pre>'; 774 foreach ($auth->getCapabilities() as $cando) { 775 print ' '.str_pad($cando, 16) .' => '. (int)$auth->canDo($cando) . DOKU_LF; 776 } 777 print '</pre>'; 778 } 779 780 print '<b>$_SESSION:</b><pre>'; 781 print_r($ses); 782 print '</pre>'; 783 784 print '<b>Environment:</b><pre>'; 785 print_r($_ENV); 786 print '</pre>'; 787 788 print '<b>PHP settings:</b><pre>'; 789 $inis = ini_get_all(); 790 print_r($inis); 791 print '</pre>'; 792 793 if (function_exists('apache_get_version')) { 794 $apache = []; 795 $apache['version'] = apache_get_version(); 796 797 if (function_exists('apache_get_modules')) { 798 $apache['modules'] = apache_get_modules(); 799 } 800 print '<b>Apache</b><pre>'; 801 print_r($apache); 802 print '</pre>'; 803 } 804 805 print '</body></html>'; 806} 807 808/** 809 * Form to request a new password for an existing account 810 * 811 * @author Benoit Chesneau <benoit@bchesneau.info> 812 * @author Andreas Gohr <gohr@cosmocode.de> 813 * @deprecated 2020-07-18 814 */ 815function html_resendpwd() 816{ 817 dbg_deprecated(UserResendPwd::class .'::show()'); 818 (new UserResendPwd())->show(); 819} 820 821/** 822 * Return the TOC rendered to XHTML 823 * 824 * @author Andreas Gohr <andi@splitbrain.org> 825 * 826 * @param array $toc 827 * @return string html 828 */ 829function html_TOC($toc) 830{ 831 if ($toc === []) return ''; 832 global $lang; 833 $out = '<!-- TOC START -->'.DOKU_LF; 834 $out .= '<div id="dw__toc" class="dw__toc">'.DOKU_LF; 835 $out .= '<h3 class="toggle">'; 836 $out .= $lang['toc']; 837 $out .= '</h3>'.DOKU_LF; 838 $out .= '<div>'.DOKU_LF; 839 $out .= html_buildlist($toc, 'toc', 'html_list_toc', null, true); 840 $out .= '</div>'.DOKU_LF.'</div>'.DOKU_LF; 841 $out .= '<!-- TOC END -->'.DOKU_LF; 842 return $out; 843} 844 845/** 846 * Callback for html_buildlist 847 * 848 * @param array $item 849 * @return string html 850 */ 851function html_list_toc($item) 852{ 853 if (isset($item['hid'])) { 854 $link = '#'.$item['hid']; 855 } else { 856 $link = $item['link']; 857 } 858 859 return '<a href="'.$link.'">'.hsc($item['title']).'</a>'; 860} 861 862/** 863 * Helper function to build TOC items 864 * 865 * Returns an array ready to be added to a TOC array 866 * 867 * @param string $link - where to link (if $hash set to '#' it's a local anchor) 868 * @param string $text - what to display in the TOC 869 * @param int $level - nesting level 870 * @param string $hash - is prepended to the given $link, set blank if you want full links 871 * @return array the toc item 872 */ 873function html_mktocitem($link, $text, $level, $hash = '#') 874{ 875 return [ 876 'link' => $hash.$link, 877 'title' => $text, 878 'type' => 'ul', 879 'level' => $level 880 ]; 881} 882 883/** 884 * Output a Doku_Form object. 885 * Triggers an event with the form name: HTML_{$name}FORM_OUTPUT 886 * 887 * @author Tom N Harris <tnharris@whoopdedo.org> 888 * 889 * @param string $name The name of the form 890 * @param Doku_Form $form The form 891 * @return void 892 * @deprecated 2020-07-18 893 */ 894function html_form($name, $form) 895{ 896 dbg_deprecated('use dokuwiki\Form\Form instead of Doku_Form'); 897 // Safety check in case the caller forgets. 898 $form->endFieldset(); 899 Event::createAndTrigger('HTML_'.strtoupper($name).'FORM_OUTPUT', $form, 'html_form_output', false); 900} 901 902/** 903 * Form print function. 904 * Just calls printForm() on the form object. 905 * 906 * @param Doku_Form $form The form 907 * @return void 908 * @deprecated 2020-07-18 909 */ 910function html_form_output($form) 911{ 912 dbg_deprecated('use ' . Form::class . '::toHTML()'); 913 $form->printForm(); 914} 915 916/** 917 * Embed a flash object in HTML 918 * 919 * This will create the needed HTML to embed a flash movie in a cross browser 920 * compatble way using valid XHTML 921 * 922 * The parameters $params, $flashvars and $atts need to be associative arrays. 923 * No escaping needs to be done for them. The alternative content *has* to be 924 * escaped because it is used as is. If no alternative content is given 925 * $lang['noflash'] is used. 926 * 927 * @author Andreas Gohr <andi@splitbrain.org> 928 * @link http://latrine.dgx.cz/how-to-correctly-insert-a-flash-into-xhtml 929 * 930 * @param string $swf - the SWF movie to embed 931 * @param int $width - width of the flash movie in pixels 932 * @param int $height - height of the flash movie in pixels 933 * @param array $params - additional parameters (<param>) 934 * @param array $flashvars - parameters to be passed in the flashvar parameter 935 * @param array $atts - additional attributes for the <object> tag 936 * @param string $alt - alternative content (is NOT automatically escaped!) 937 * @return string - the XHTML markup 938 */ 939function html_flashobject($swf, $width, $height, $params = null, $flashvars = null, $atts = null, $alt = '') 940{ 941 global $lang; 942 943 $out = ''; 944 945 // prepare the object attributes 946 if (is_null($atts)) $atts = []; 947 $atts['width'] = (int) $width; 948 $atts['height'] = (int) $height; 949 if (!$atts['width']) $atts['width'] = 425; 950 if (!$atts['height']) $atts['height'] = 350; 951 952 // add object attributes for standard compliant browsers 953 $std = $atts; 954 $std['type'] = 'application/x-shockwave-flash'; 955 $std['data'] = $swf; 956 957 // add object attributes for IE 958 $ie = $atts; 959 $ie['classid'] = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'; 960 961 // open object (with conditional comments) 962 $out .= '<!--[if !IE]> -->'.NL; 963 $out .= '<object '.buildAttributes($std).'>'.NL; 964 $out .= '<!-- <![endif]-->'.NL; 965 $out .= '<!--[if IE]>'.NL; 966 $out .= '<object '.buildAttributes($ie).'>'.NL; 967 $out .= ' <param name="movie" value="'.hsc($swf).'" />'.NL; 968 $out .= '<!--><!-- -->'.NL; 969 970 // print params 971 if (is_array($params)) foreach ($params as $key => $val) { 972 $out .= ' <param name="'.hsc($key).'" value="'.hsc($val).'" />'.NL; 973 } 974 975 // add flashvars 976 if (is_array($flashvars)) { 977 $out .= ' <param name="FlashVars" value="'.buildURLparams($flashvars).'" />'.NL; 978 } 979 980 // alternative content 981 if ($alt) { 982 $out .= $alt.NL; 983 } else { 984 $out .= $lang['noflash'].NL; 985 } 986 987 // finish 988 $out .= '</object>'.NL; 989 $out .= '<!-- <![endif]-->'.NL; 990 991 return $out; 992} 993 994/** 995 * Prints HTML code for the given tab structure 996 * 997 * @param array $tabs tab structure 998 * @param string $current_tab the current tab id 999 * @return void 1000 */ 1001function html_tabs($tabs, $current_tab = null) 1002{ 1003 echo '<ul class="tabs">'.NL; 1004 1005 foreach ($tabs as $id => $tab) { 1006 html_tab($tab['href'], $tab['caption'], $id === $current_tab); 1007 } 1008 1009 echo '</ul>'.NL; 1010} 1011 1012/** 1013 * Prints a single tab 1014 * 1015 * @author Kate Arzamastseva <pshns@ukr.net> 1016 * @author Adrian Lang <mail@adrianlang.de> 1017 * 1018 * @param string $href - tab href 1019 * @param string $caption - tab caption 1020 * @param boolean $selected - is tab selected 1021 * @return void 1022 */ 1023 1024function html_tab($href, $caption, $selected = false) 1025{ 1026 $tab = '<li>'; 1027 if ($selected) { 1028 $tab .= '<strong>'; 1029 } else { 1030 $tab .= '<a href="' . hsc($href) . '">'; 1031 } 1032 $tab .= hsc($caption) 1033 . '</' . ($selected ? 'strong' : 'a') . '>' 1034 . '</li>'.NL; 1035 echo $tab; 1036} 1037 1038/** 1039 * Display size change 1040 * 1041 * @param int $sizechange - size of change in Bytes 1042 * @param Doku_Form $form - (optional) form to add elements to 1043 * @return void|string 1044 */ 1045function html_sizechange($sizechange, $form = null) 1046{ 1047 if (isset($sizechange)) { 1048 $class = 'sizechange'; 1049 $value = filesize_h(abs($sizechange)); 1050 if ($sizechange > 0) { 1051 $class .= ' positive'; 1052 $value = '+' . $value; 1053 } elseif ($sizechange < 0) { 1054 $class .= ' negative'; 1055 $value = '-' . $value; 1056 } else { 1057 $value = '±' . $value; 1058 } 1059 if (!isset($form)) { 1060 return '<span class="'.$class.'">'.$value.'</span>'; 1061 } else { // Doku_Form 1062 $form->addElement(form_makeOpenTag('span', ['class' => $class])); 1063 $form->addElement($value); 1064 $form->addElement(form_makeCloseTag('span')); 1065 } 1066 } 1067} 1068