1<?php
2/**
3 * Custom Function for the template
4 */
5
6// must be run from within DokuWiki
7const BOOTIE = 'bootie';
8if (!defined('DOKU_INC')) die();
9
10
11/**
12 * Print the breadcrumbs trace with Bootstrap class
13 *
14 * @author Nicolas GERARD
15 *
16 * @param string $sep Separator between entries
17 * @return bool
18 */
19function tpl_breadcrumbs_bootstrap($sep = '�')
20{
21
22    global $conf;
23    global $lang;
24
25    //check if enabled
26    if (!$conf['breadcrumbs']) return false;
27
28    $crumbs = array_reverse(breadcrumbs()); //setup crumb trace
29
30
31    $last = count($crumbs);
32    $i = 0;
33    echo '<ol class="breadcrumb justify-content-start m-0 p-0 pb-1">' . PHP_EOL;
34
35    // Try to get the template custom breadcrumb
36    $breadCrumb = tpl_getLang('breadcrumb');
37    if ($breadCrumb == '') {
38        // If not present for the language, get the default one
39        $breadCrumb = $lang['breadcrumb'];
40    }
41    echo '<span id="breadCrumbTitle">' . $breadCrumb . ':   </span>';
42
43    foreach ($crumbs as $id => $name) {
44        $i++;
45
46        if ($i == $last) {
47            print '<li class="breadcrumb-item active">';
48        } else {
49            print '<li class="breadcrumb-item">';
50        }
51        tpl_link(wl($id), hsc($name), 'title="' . $id . '" style="width: 100%;"');
52
53        print '</li>' . PHP_EOL;
54
55    }
56    echo '</ol>' . PHP_EOL;
57    return true;
58}
59
60
61/**
62 * Hierarchical breadcrumbs
63 *
64 * This will return the Hierarchical breadcrumbs.
65 *
66 * Config:
67 *    - $conf['youarehere'] must be true
68 *    - add $lang['youarehere'] if $printPrefix is true
69 *
70 * @param bool $printPrefix print or not the $lang['youarehere']
71 * @return string
72 */
73function tpl_youarehere_bootstrap($printPrefix = false)
74{
75
76    global $conf;
77    global $lang;
78
79    // check if enabled
80    if (!$conf['youarehere']) return;
81
82    // print intermediate namespace links
83    $htmlOutput = '<ol class="breadcrumb">' . PHP_EOL;
84
85    // Print the home page
86    $htmlOutput .= '<li>' . PHP_EOL;
87    if ($printPrefix) {
88        $htmlOutput .= $lang['youarehere'] . ' ';
89    }
90    $page = $conf['start'];
91    $htmlOutput .= tpl_link(wl($page), '<span class="glyphicon glyphicon-home" aria-hidden="true"></span>', 'title="' . tpl_pagetitle($page, true) . '"', $return = true);
92    $htmlOutput .= '</li>' . PHP_EOL;
93
94    // Print the parts if there is more than one
95    global $ID;
96    $idParts = explode(':', $ID);
97    if (count($idParts) > 1) {
98
99        // Print the parts without the last one ($count -1)
100        $page = "";
101        for ($i = 0; $i < count($idParts) - 1; $i++) {
102
103            $page .= $idParts[$i] . ':';
104
105            // Skip home page of the namespace
106            // if ($page == $conf['start']) continue;
107
108            // The last part is the active one
109//            if ($i == $count) {
110//                $htmlOutput .= '<li class="active">';
111//            } else {
112//                $htmlOutput .= '<li>';
113//            }
114
115            $htmlOutput .= '<li>';
116            // html_wikilink because the page has the form pagename: and not pagename:pagename
117            $htmlOutput .= html_wikilink($page);
118            $htmlOutput .= '</li>' . PHP_EOL;
119
120        }
121    }
122
123    // Skipping Wiki Global Root Home Page
124//    resolve_pageid('', $page, $exists);
125//    if(isset($page) && $page == $idPart.$idParts[$i]) {
126//        echo '</ol>'.PHP_EOL;
127//        return true;
128//    }
129//    // skipping for namespace index
130//    $page = $idPart.$idParts[$i];
131//    if($page == $conf['start']) {
132//        echo '</ol>'.PHP_EOL;
133//        return true;
134//    }
135
136    // print current page
137//    print '<li>';
138//    tpl_link(wl($page), tpl_pagetitle($page,true), 'title="' . $page . '"');
139    $htmlOutput .= '</li>' . PHP_EOL;
140    // close the breadcrumb
141    $htmlOutput .= '</ol>' . PHP_EOL;
142    return $htmlOutput;
143
144}
145
146
147/*
148 * Function return the page name from an id
149 * @author Nicolas GERARD
150 *
151 * @param string $sep Separator between entries
152 * @return bool
153 */
154
155function tpl_pageName($id)
156{
157
158    // page names
159    $name = noNSorNS($id);
160    if (useHeading('navigation')) {
161        // get page title
162        $title = p_get_first_heading($id, METADATA_RENDER_USING_SIMPLE_CACHE);
163        if ($title) {
164            $name = $title;
165        }
166    }
167    return $name;
168
169}
170
171function tpl_searchform_bootie($ajax = true, $autocomplete = true)
172{
173    global $lang;
174    global $ACT;
175    global $QUERY;
176
177    // don't print the search form if search action has been disabled
178    if (!actionOK('search')) return false;
179
180    print '<form id="navBarSearch" action="' . wl() . '" accept-charset="utf-8" class="search form-inline my-lg-0" id="dw__search" method="get" role="search">';
181    print '<input type="hidden" name="do" value="search" />';
182    print '<label class="sr-only" for="search">Search Term</label>';
183    print '<input type="text" ';
184    if ($ACT == 'search') print 'value="' . htmlspecialchars($QUERY) . '" ';
185    print 'placeholder="' . $lang['btn_search'] . '..." ';
186    if (!$autocomplete) print 'autocomplete="off" ';
187    print 'id="qsearch__in" accesskey="f" name="id" class="edit form-control" title="[F]" />';
188//    print '<button type="submit" title="'.$lang['btn_search'].'">'.$lang['btn_search'].'</button>';
189    if ($ajax) print '<div id="qsearch__out" class="ajax_qsearch JSpopup"></div>';
190    print '</form>';
191    return true;
192}
193
194/**
195 * This is a fork of tpl_actionlink where I have added the class parameters
196 *
197 * Like the action buttons but links
198 *
199 * @author Adrian Lang <mail@adrianlang.de>
200 * @see    tpl_get_action
201 *
202 * @param string $type action command
203 * @param string $pre prefix of link
204 * @param string $suf suffix of link
205 * @param string $inner innerHML of link
206 * @param bool $return if true it returns html, otherwise prints
207 * @param string $class the class to be added
208 * @return bool|string html or false if no data, true if printed
209 */
210function tpl_actionlink_bootie($type, $class = '', $pre = '', $suf = '', $inner = '', $return = false)
211{
212    global $lang;
213    $data = tpl_get_action($type);
214    if ($data === false) {
215        return false;
216    } elseif (!is_array($data)) {
217        $out = sprintf($data, 'link');
218    } else {
219        /**
220         * @var string $accesskey
221         * @var string $id
222         * @var string $method
223         * @var bool $nofollow
224         * @var array $params
225         * @var string $replacement
226         */
227        extract($data);
228        if (strpos($id, '#') === 0) {
229            $linktarget = $id;
230        } else {
231            $linktarget = wl($id, $params);
232        }
233        $caption = $lang['btn_' . $type];
234        if (strpos($caption, '%s')) {
235            $caption = sprintf($caption, $replacement);
236        }
237        $akey = $addTitle = '';
238        if ($accesskey) {
239            $akey = 'accesskey="' . $accesskey . '" ';
240            $addTitle = ' [' . strtoupper($accesskey) . ']';
241        }
242        $rel = $nofollow ? 'rel="nofollow" ' : '';
243        $out = $pre . tpl_link(
244                $linktarget, (($inner) ? $inner : $caption),
245                'class="nav-link action ' . $type . ' ' . $class . '" ' .
246                $akey . $rel .
247                'title="' . hsc($caption) . $addTitle . '"', true
248            ) . $suf;
249    }
250    if ($return) return $out;
251    echo $out;
252    return true;
253}
254
255
256/**
257 * @return array
258 * Return the headers needed by this template
259 * */
260function tpl_get_default_headers()
261{
262
263    $useCdn = tpl_getConf('cdn');
264    $script = array();
265    if (!$useCdn) {
266
267        $localBaseJs = DOKU_BASE . 'lib/tpl/bootie/js/';
268
269        // Other mode, we pick the Javascript of Dokuwiki
270        // jquery must not be slim because the post is needed for qsearch
271        $script['jquery'] = array(
272            'src' => $localBaseJs . 'jquery-3.3.1.min.js',
273            'integrity' => "sha256-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT",
274            'crossorigin' => "anonymous",
275            'defer' => "true"
276        );
277
278
279        $script['popper'] = array(
280            'src' => $localBaseJs . 'popper-1.14.7.min.js',
281            'integrity' => "sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1",
282            'crossorigin' => "anonymous",
283            'defer' => "true"
284
285        );
286        $script['bootstrap'] = array(
287            'src' => $localBaseJs . 'bootstrap-4.3.1.min.js',
288            'integrity' => "sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM",
289            'crossorigin' => "anonymous",
290            'defer' => "true"
291
292        );
293
294
295    } else {
296
297        // use a cdn
298        // jquery must not be slim because the post is needed for qsearch
299        $script['jquery'] = array(
300            'src' => 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js',
301            'integrity' => "sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=",
302            'crossorigin' => "anonymous",
303            'defer' => "true"
304        );
305        $script['popper'] = array(
306            'src' => 'https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js',
307            'integrity' => "sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1",
308            'crossorigin' => "anonymous",
309            'defer' => "true"
310        );
311        $script['bootstrap'] = array(
312            'src' => 'https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js',
313            'integrity' => "sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM",
314            'crossorigin' => "anonymous",
315            'defer' => "true"
316        );
317
318    }
319
320
321    if (!$useCdn) {
322        $baseCss = DOKU_BASE . 'lib/tpl/bootie/css/';
323        $css['bootstrap'] = array(
324            'href' => $baseCss . 'bootstrap-4.3.1.min.css',
325            'integrity' => "sha384-ZYfZnVukOuh/gRpU9uN+T9XwwRFJ9Y+0Ylk3zKvI184omb/HoOtQ0F8Iol7Nix7q",
326            'crossorigin' => "anonymous",
327            'rel' => "stylesheet",
328
329        );
330
331    } else {
332        $css['bootstrap'] = array(
333            'href' => 'https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css',
334            'integrity' => "sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T",
335            'crossorigin' => "anonymous",
336            'rel' => "stylesheet",
337        );
338    };
339
340    return array(
341        'script' => $script,
342        'link' => $css
343    );
344
345
346}
347
348/**
349 * @param Doku_Event $event
350 * @param $param
351 * Function that handle the META HEADER event
352 *   * It will add the Bootstrap Js and CSS
353 *   * Make all script and resources defer
354 */
355function tpl_bootie_meta_header(Doku_Event &$event, $param)
356{
357
358    global $DOKU_TPL_BOOTIE_PRELOAD_CSS;
359    $DOKU_TPL_BOOTIE_PRELOAD_CSS = array();
360
361    $newHeaderTypes = array();
362    $bootstrapHeaders = tpl_get_default_headers();
363    $eventHeaderTypes = $event->data;
364    foreach ($eventHeaderTypes as $headerType => $headerData) {
365        switch ($headerType) {
366            case "meta":
367                // generator, color, robots, keywords
368                // nothing to do pick them all
369                $newHeaderTypes[$headerType] = $headerData;
370                break;
371            case "link":
372                // index, rss, manifest, search, alternate, stylesheet
373                // delete edit
374                $newLinkData = $bootstrapHeaders[$headerType]; // Css of Bootstrap will be unchanged
375                foreach ($headerData as $linkData) {
376                    switch ($linkData['rel']) {
377                        case 'edit':
378                            break;
379                        case 'stylesheet':
380
381                            // Take the stylesheet to load them at the end
382                            $DOKU_TPL_BOOTIE_PRELOAD_CSS[] = $linkData;
383
384                            // Change the loading mechanism to preload
385                            $linkData['rel'] = 'preload';
386                            $linkData['as'] = 'style';
387                            $newLinkData[] = $linkData;
388
389                            break;
390                        default:
391                            $newLinkData[] = $linkData;
392                            break;
393                    }
394                }
395                $newHeaderTypes[$headerType] = $newLinkData;
396                break;
397
398            case "script":
399
400                $newScriptData = array();
401                foreach ($headerData as $scriptData) {
402                    $scriptData['defer'] = "true";
403                    $pos = strpos($scriptData['src'], 'jquery');
404                    if ($pos === false) {
405                        $newScriptData[] = $scriptData;
406                    } else {
407                        // This is the Jquery script
408                        if (empty($_SERVER['REMOTE_USER'])) {
409                            // https://www.dokuwiki.org/config:jquerycdn
410                            // We take the Jquery of Bootstrap
411                            $newScriptData = array_merge($newScriptData, $bootstrapHeaders[$headerType]);
412                        } else {
413                            // We take the Jquery of doku and we add Bootstrap
414                            $newScriptData[] = $scriptData; // js
415                            $newScriptData[] = $bootstrapHeaders[$headerType]['popper'];
416                            $newScriptData[] = $bootstrapHeaders[$headerType]['bootstrap'];
417                        }
418                    }
419                }
420
421                $newHeaderTypes[$headerType] = $newScriptData;
422                break;
423
424        }
425    }
426    $event->data = $newHeaderTypes;
427
428
429}
430
431