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