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