1 <?php
2 /**
3  * Custom Function for the template
4  */
5 
6 // must be run from within DokuWiki
7 const BOOTIE = 'bootie';
8 if (!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  */
19 function 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  */
73 function 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 
155 function 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 
171 function 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  */
210 function 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  * */
260 function 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  */
355 function 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