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