1<?php 2/** 3 * DokuWiki Mikio Template 4 * 5 * @link http://dokuwiki.org/template:mikio 6 * @author James Collins <james.collins@outlook.com.au> 7 * @license GPLv2 (http://www.gnu.org/licenses/gpl-2.0.html) 8 */ 9namespace dokuwiki\template\mikio; 10 11if (!defined('DOKU_INC')) die(); 12 13require_once('icons/icons.php'); 14require_once('inc/simple_html_dom.php'); 15 16class Template { 17 public $tplDir = ''; 18 public $baseDir = ''; 19 20 21 /** 22 * Class constructor 23 */ 24 public function __construct() { 25 $this->tplDir = tpl_incdir(); 26 $this->baseDir = tpl_basedir(); 27 28 $this->_registerHooks(); 29 } 30 31 32 /** 33 * Returns the instance of the class 34 * 35 * @return Template class instance 36 */ 37 public static function getInstance() 38 { 39 static $instance = null; 40 41 if ($instance === null) { 42 $instance = new Template(); 43 } 44 45 return $instance; 46 } 47 48 49 /** 50 * Register the themes hooks into Dokuwiki 51 */ 52 private function _registerHooks() { 53 global $EVENT_HANDLER; 54 55 $events_dispatcher = array( 56 'TPL_METAHEADER_OUTPUT' => 'metaheadersHandler' 57 ); 58 59 foreach ($events_dispatcher as $event => $method) { 60 $EVENT_HANDLER->register_hook($event, 'BEFORE', $this, $method); 61 } 62 } 63 64 65 /** 66 * Meta handler hook for DokuWiki 67 * 68 * @param Doku_Event $event 69 */ 70 public function metaHeadersHandler(\Doku_Event $event) { 71 global $MIKIO_ICONS; 72 73 $stylesheets = array(); 74 $scripts = array(); 75 76 if($this->getConf('customTheme') != '') { 77 if(file_exists($this->tplDir . 'themes/' . $this->getConf('customTheme') . '/style.css')) { 78 $stylesheets[] = $this->baseDir . 'themes/' . $this->getConf('customTheme') . '/style.css'; 79 } 80 if(file_exists($this->tplDir . 'themes/' . $this->getConf('customTheme') . '/script.js')) { 81 $scripts[] = $this->baseDir . 'themes/' . $this->getConf('customTheme') . '/script.js'; 82 } 83 } 84 85 if(is_array($MIKIO_ICONS)) { 86 $icons = Array(); 87 foreach($MIKIO_ICONS as $icon) { 88 if(isset($icon['name']) && isset($icon['css']) && isset($icon['insert'])) { 89 $icons[] = $icon; 90 91 if($icon['css'] != '') { 92 if(strpos($icon['css'], '//') === FALSE) { 93 // $stylesheets[] = $this->baseDir . 'icons/' . $icon['css']; 94 } else { 95 // $stylesheets[] = $icon['css']; 96 } 97 } 98 } 99 } 100 $MIKIO_ICONS = $icons; 101 } else { 102 $MIKIO_ICONS = []; 103 } 104 105 $scripts[] = $this->baseDir . 'assets/mikio.js'; 106 $stylesheets[] = $this->baseDir . 'assets/mikio.less'; 107 108 $set = []; 109 foreach ($stylesheets as $style) { 110 if(in_array($style, $set) == FALSE) { 111 if(strtolower(substr($style, -5)) == '.less') { 112 $style = $this->baseDir . 'css.php?css=' . str_replace($this->baseDir, '', $style); 113 } 114 115 array_unshift($event->data['link'], array( 116 'type' => 'text/css', 117 'rel' => 'stylesheet', 118 'href' => $style 119 )); 120 } 121 $set[] = $style; 122 } 123 124 $set = []; 125 foreach ($scripts as $script) { 126 if(in_array($script, $set) == FALSE) { 127 $event->data['script'][] = array( 128 'type' => 'text/javascript', 129 '_data' => '', 130 'src' => $script); 131 } 132 $set[] = $script; 133 } 134 } 135 136 137 /** 138 * DokuWiki content event handler 139 * 140 * @author James Collins <james.collins@outlook.com.au> 141 */ 142 /*public function contentHandler(\Doku_Event $event) 143 { 144 $event->data = $this->parseContent($event->data); 145 }*/ 146 147 148 /** ---- 149 * Retreive and parse theme configuration options 150 * 151 * @param string $key the configuration key to retreive 152 * @param mixed $default if key doesn't exist, return this value 153 * @return mixed parsed value of configuration 154 */ 155 public function getConf($key, $default = FALSE) { 156 157 $value = tpl_getConf($key, $default); 158 159 // switch($key) { 160 161 // } 162 163 return $value; 164 } 165 166 167 /** 168 * Icon 169 * 170 * @author James Collins <james.collins@outlook.com.au> 171 * 172 * @param string $type The type of icon to return 173 * @return string HTML for icon element 174 */ 175 public function icon($type) { 176 return '<i class="fa fa-' . $type . '" aria-hidden="true"></i>'; 177 } 178 179 180 181 182 /** 183 * Add class to first DOM element 184 * 185 * @author James Collins <james.collins@outlook.com.au> 186 * 187 * @param string $content HTML DOM 188 * @param string $class Class to add DOM elements 189 * @return string HTML DOM with class added 190 */ 191 public function elementAddClass($html, $class) { 192 preg_match('/class.*?".*?"/', $html, $matches); 193 if(count($matches) > 0) { 194 preg_match('/[" ]'.$class.'[" ]/', $matches[0], $matches); 195 if(count($matches) == 0) { 196 return preg_replace('/(class.*?=.*?")/', '${1}'.$class.' ', $html, 1); 197 } 198 } else { 199 return preg_replace('/>/', 'class="'.$class.'">', $html, 1); 200 } 201 202 return $html; 203 } 204 205 206 public function includeFile($file, $print = true) { 207 $html = ''; 208 209 if(!$print) { 210 ob_start(); 211 } 212 213 tpl_includeFile($file); 214 215 if(!$print) { 216 $html = ob_get_contents(); 217 ob_end_clean(); 218 } 219 220 return $html; 221 } 222 223 /** 224 * include page from namespace 225 * 226 * @author James Collins <james.collins@outlook.com.au> 227 * 228 * @param string $page namespace to include 229 * @param boolean $print print content 230 * @param boolean $parse parse content before printing/returning 231 * @return string contents of page found 232 */ 233 public function includePage($page, $print = TRUE, $parse = TRUE) 234 { 235 $useACL = TRUE; // TODO Add these as config options? 236 $propagate = TRUE; 237 $checkPropagate = TRUE; // TODO Add these as config options? 238 $html = ''; 239 240 $html = tpl_include_page($page, false, $propagate, $useACL); 241 if($html == '' && $checkPropagate) { 242 $html = tpl_include_page($page, false, false, $useACL); 243 } 244 245 if($html != '' && $parse) { 246 $html = $this->parseContent($html); 247 } 248 249 if($print) echo $html; 250 251 return $html; 252 } 253 254 public function includeLoggedIn() { 255 if (!empty($_SERVER['REMOTE_USER'])) { 256 echo '<li class="user navbar-text text-nowrap">'; 257 tpl_userinfo(); /* 'Logged in as ...' */ 258 echo '</li>'; 259 } 260 } 261 262 263 public function includeMenu($type, $print = true, $class = '') { 264 global $lang; 265 global $USERINFO; 266 $html = '<ul class="mikio-nav ' . $class . '">'; 267 268 switch($type) { 269 270 case 'none'; 271 return ''; 272 273 case 'custom': 274 case 'footer': 275 $items = []; 276 277 if($type == 'footer') { 278 $items = explode(';', $this->getConf('footerCustomMenuText', '')); 279 } else { 280 $items = explode(';', $this->getConf('navbarCustomMenuText', '')); 281 } 282 283 foreach($items as $item) { 284 $parts = explode('|', $item); 285 if($parts > 1) { 286 $html .= '<li class="mikio-nav-item"><a class="mikio-nav-link" href="' . strip_tags($this->getLink(trim($parts[0]))) . '">' . strip_tags(trim($parts[1])) . '</a></li>'; 287 } 288 } 289 290 break; 291 292 case 'dokuwiki': 293 $pageToolsMenu = []; 294 $siteToolsMenu = []; 295 $userToolsMenu = []; 296 297 $showIcons = ($this->getConf('navbarDWMenuType') != 'text'); 298 $showText = ($this->getConf('navbarDWMenuType') != 'icons'); 299 $isDropDown = ($this->getConf('navbarDWMenuCombine') != 'seperate'); 300 301 $items = (new \dokuwiki\Menu\PageMenu())->getItems(); 302 foreach($items as $item) { 303 if($item->getType() != 'top') { 304 $itemHtml = ''; 305 306 $itemHtml .= '<a class="mikio-nav-link ' . ($isDropDown ? 'mikio-dropdown-item' : '') . '" href="'.$item->getLink().'" title="'.$item->getTitle().'">'; 307 if($showIcons) $itemHtml .= '<span class="mikio-icon">'.inlineSVG($item->getSvg()).'</span>'; 308 if($showText || $isDropDown) $itemHtml .= '<span>' . $item->getLabel() . '</span>'; 309 $itemHtml .= '</a>'; 310 311 $pageToolsMenu[] = $itemHtml; 312 } 313 } 314 315 $items = (new \dokuwiki\Menu\SiteMenu())->getItems('action '); 316 foreach($items as $item) { 317 $itemHtml = ''; 318 319 $itemHtml .= '<a class="mikio-nav-link ' . ($isDropDown ? 'mikio-dropdown-item' : '') . '" href="'.$item->getLink().'" title="'.$item->getTitle().'">'; 320 if($showIcons) $itemHtml .= '<span class="mikio-icon">'.inlineSVG($item->getSvg()).'</span>'; 321 if($showText || $isDropDown) $itemHtml .= '<span>' . $item->getLabel() . '</span>'; 322 $itemHtml .= '</a>'; 323 324 $siteToolsMenu[] = $itemHtml; 325 } 326 327 $items = (new \dokuwiki\Menu\UserMenu())->getItems('action'); 328 foreach($items as $item) { 329 $itemHtml = ''; 330 331 $itemHtml .= '<a class="mikio-nav-link ' . ($isDropDown ? 'mikio-dropdown-item' : '') . '" href="'.$item->getLink().'" title="'.$item->getTitle().'">'; 332 if($showIcons) $itemHtml .= '<span class="mikio-icon">'.inlineSVG($item->getSvg()).'</span>'; 333 if($showText || $isDropDown) $itemHtml .= '<span>' . $item->getLabel() . '</span>'; 334 $itemHtml .= '</a>'; 335 336 $userToolsMenu[] = $itemHtml; 337 } 338 339 340 switch($this->getConf('navbarDWMenuCombine')) { 341 case 'dropdown': 342 $html .= '<li id="dokuwiki__pagetools" class="mikio-nav-dropdown">'; 343 $html .= '<a id="mikio_dropdown_pagetools" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">' . ($showIcons ? $this->icon('file') : '') . ($showText ? $lang['page_tools'] : '') . '</a>'; 344 $html .= '<div class="dropdown-menu dropdown-menu-right" aria-labelledby="mikio_dropdown_pagetools">'; 345 346 foreach($pageToolsMenu as $item) { 347 $html .= $item; 348 } 349 350 $html .= '</div>'; 351 $html .= '</li>'; 352 353 $html .= '<li id="dokuwiki__sitetools" class="nav-item dropdown mikio-navbar-dropdown">'; 354 $html .= '<a id="mikio_dropdown_sitetools" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">' . ($showIcons ? $this->icon('gear') : '') . ($showText ? $lang['site_tools'] : '') . '</a>'; 355 $html .= '<div class="dropdown-menu dropdown-menu-right">'; 356 357 foreach($siteToolsMenu as $item) { 358 $html .= $item; 359 } 360 361 $html .= '</div>'; 362 $html .= '</li>'; 363 364 $html .= '<li id="dokuwiki__usertools" class="nav-item dropdown mikio-navbar-dropdown">'; 365 $html .= '<a id="mikio_dropdown_usertools" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">' . ($showIcons ? $this->icon('user') : '') . ($showText ? $lang['user_tools'] : '') . '</a>'; 366 $html .= '<div class="dropdown-menu dropdown-menu-right">'; 367 368 foreach($userToolsMenu as $item) { 369 $html .= $item; 370 } 371 372 $html .= '</div>'; 373 $html .= '</li>'; 374 375 break; 376 377 case 'combine': 378 $html .= '<li class="mikio-nav-dropdown">'; 379 $html .= '<a class="mikio-nav-link" href="#">' . ($showIcons ? $this->icon('wrench') : '') . '' . '</a>'; // TODO change $lang 380 $html .= '<div class="mikio-dropdown">'; 381 382 $html .= '<h6 class="mikio-dropdown-header">' . $lang['page_tools'] . '</h6>'; 383 foreach($pageToolsMenu as $item) { 384 $html .= $item; 385 } 386 387 $html .= '<div class="mikio-dropdown-divider"></div>'; 388 $html .= '<h6 class="mikio-dropdown-header">' . $lang['site_tools'] . '</h6>'; 389 foreach($siteToolsMenu as $item) { 390 $html .= $item; 391 } 392 393 $html .= '<div class="mikio-dropdown-divider"></div>'; 394 $html .= '<h6 class="mikio-dropdown-header">' . $lang['user_tools'] . '</h6>'; 395 foreach($userToolsMenu as $item) { 396 $html .= $item; 397 } 398 399 $html .= '</div>'; 400 $html .= '</li>'; 401 break; 402 403 default: // seperate 404 foreach($userToolsMenu as $item) { 405 $html .= '<li class="nav-item mikio-navbar-seperate">' . $item . '<li>'; 406 } 407 408 foreach($siteToolsMenu as $item) { 409 $html .= '<li class="nav-item mikio-navbar-seperate">' . $item . '<li>'; 410 } 411 412 foreach($pageToolsMenu as $item) { 413 $html .= '<li class="nav-item mikio-navbar-seperate">' . $item . '<li>'; 414 } 415 416 break; 417 } 418 419 420 break; 421 422 } 423 424 $html .= '</ul>'; 425 426 if($print) { 427 echo $html; 428 return ''; 429 } 430 431 return $html; 432 } 433 434 435 /** ---- 436 * print or return the main navbar 437 * 438 * @param boolean $print print the 439 * 440 * @return string generated content 441 */ 442 public function includeNavbar($print = TRUE) { 443 global $conf; 444 $html = ''; 445 $class = ''; 446 447 $html .= '<nav class="mikio-navbar">'; 448 $html .= '<a class="mikio-navbar-brand" href="' . wl() . '">'; 449 if($this->getConf('navbarUseTitleIcon') || $this->getConf('navbarUseTitleText')) { 450 451 // Brand image 452 if($this->getConf('navbarUseTitleIcon')) { 453 $logo = tpl_getMediaFile(array(':wiki:logo.png', ':logo.png', 'images/logo.png', 'images/logo.jpg', 'images/logo.gif'), false); 454 if($logo != '') { 455 $html .= '<img src="' . $logo . '" class="mikio-navbar-brand-image">'; 456 } 457 } 458 459 // Brand title 460 if($this->getConf('navbarUseTitleText')) { 461 $html .= '<div class="mikio-navbar-brand-title">'; 462 $html .= '<div class="mikio-navbar-brand-title-text">' . $conf['title'] . '</div>'; 463 if($this->getConf('navbarUseTaglineText')) { 464 $html .= '<div class="mikio-navbar-brand-title-tagline">' . $conf['tagline'] . '</div>'; 465 } 466 $html .= '</div>'; 467 } 468 } 469 $html .= '</a>'; 470 $html .= '<div class="mikio-navbar-toggle"><svg viewBox="0 0 32 32" preserveAspectRatio="xMidYMin" aria-hidden="true"><path d="M6.001 7.128L6 10.438l19.998-.005L26 7.124zM6.001 21.566L6 24.876l19.998-.006.002-3.308zM6.001 14.341L6 17.65l19.998-.004.002-3.309z"></path></svg></div>'; 471 // $html .= '</div>'; 472 473 // Menus 474 $html .= '<div class="mikio-navbar-collapse">'; 475 476 $html .= '<div class="mikio-nav-item">'; 477 $html .= $this->includeSearch(false); 478 $html .= '</div>'; 479 480 $html .= '<div class="mikio-nav-item">'; 481 $menu = $this->getConf('navbarPosRightRight'); 482 $html .= $this->includeMenu($menu, false); 483 $html .= '</div>'; 484 485 $html .= '</div>'; 486 487 $html .= '</nav>'; 488 489 // Seperate Menubar 490 if($this->getConf('navbarLowerMenu') != 'none') { 491 $class = $this->getConf('navbarLowerBackground'); 492 if($class == 'none') { 493 $class = ''; 494 } else { 495 $class = 'navbar-' . $class; 496 } 497 498 $pos = $this->getConf('navbarLowerMenuPos'); 499 if($pos == 'right') { 500 $class .= ' justify-content-end'; 501 } else if($pos == 'center') { 502 $class .= ' justify-content-center'; 503 } 504 505 $html .= '<nav class="mikio-lower-navbar navbar navbar-expand flex-column flex-md-row ' . $class . '">'; 506 507 $menu = $this->getConf('navbarLowerMenu'); 508 if($menu != 'none' && $menu != 'search') { 509 $html .= '<div class="mikio-nav-item navbar-nav-scroll">'; 510 $html .= $this->includeMenu($menu, false); 511 $html .= '</div>'; 512 $class = ''; 513 } else if($menu == 'search') { 514 $html .= '<div class="mikio-nav-item">'; 515 $html .= $this->includeSearch(false); 516 $html .= '</div>'; 517 $class = ''; 518 } 519 520 $html .= '</nav>'; 521 } 522 523 if($print) { 524 echo $html; 525 return ''; 526 } 527 528 return $html; 529 } 530 531 532 /** ---- 533 * Print or return the sidebar content 534 * 535 * @param string $prefix sidebar prefix to use when searching 536 * @param boolean $print print the generated content to the output buffer 537 * @param boolean $parse parse the content 538 * 539 * @return string generated content 540 */ 541 public function includeSidebar($prefix = '', $print = TRUE, $parse = TRUE) { 542 global $conf, $ID; 543 544 $html = ''; 545 $confPrefix = preg_replace('/[^a-zA-Z0-9]/', '', ucwords($prefix)); 546 $prefix = preg_replace('/[^a-zA-Z0-9]/', '', strtolower($prefix)); 547 548 if($confPrefix == '') $confPrefix = 'Left'; 549 if($prefix == 'Left') $prefix = ''; 550 551 if($this->getConf('sidebarShow' . $confPrefix) && page_findnearest($conf[$prefix . 'sidebar']) != FALSE && p_get_metadata($ID, 'nosidebar', FALSE) == FALSE) { 552 553 $content = $this->includeFile('sidebar' . $prefix . 'header.html', FALSE); 554 if($content != '') $html .= '<div class="mikio-sidebar-header">' . $content . '</div>'; 555 556 if($prefix == '' && $this->getConf('sidebarLeftSearch') == 'top') $html .= $this->includeSearch(FALSE); 557 558 $content = $this->includePage($conf[$prefix . 'sidebar'], FALSE); 559 if($content != '') $html .= '<div class="mikio-sidebar-content">' . $content . '</div>'; 560 561 if($prefix == '' && $this->getConf('sidebarLeftSearch') == 'bottom') $html .= $this->includeSearch(FALSE); 562 563 $content = $this->includeFile('sidebar' . $prefix . 'footer.html', FALSE); 564 if($content != '') $html .= '<div class="mikio-sidebar-footer">' . $content . '</div>'; 565 } 566 567 if($html != '') { 568 $html = '<aside class="mikio-sidebar mikio-sidebar-' . ($prefix == '' ? 'left' : $prefix) . '"><a class="mikio-sidebar-toggle" href="#">' . tpl_getLang('sidebar-title') . '</a><div class="mikio-sidebar-collapse">'. $html . '</div></aside>'; 569 } 570 571 if($parse) $html = $this->includeIcons($html); 572 if($print) echo $html; 573 return $html; 574 } 575 576 577 /** ---- 578 * Print or return the page tools content 579 * 580 * @param boolean $print print the generated content to the output buffer 581 * @param boolean $includeId include the dw__pagetools id in the element 582 * 583 * @return string generated content 584 */ 585 public function includePageTools($print = TRUE, $includeId = FALSE) { 586 $html = ''; 587 588 $html .= '<nav' . ($includeId ? ' id="dw__pagetools"' : '') . ' class="hidden-print dw__pagetools">'; 589 590 $items = (new \dokuwiki\Menu\PageMenu())->getItems(); 591 foreach($items as $item) { 592 $html .= '<a href="'.$item->getLink().'" title="'.$item->getTitle().'"><span class="icon">'.inlineSVG($item->getSvg()).'</span><span class="a11y">'.$item->getLabel().'</span></a>'; 593 } 594 595 $html .= '</nav>'; 596 597 if($print) echo $html; 598 return $html; 599 } 600 601 602 public function includeSearch($print = TRUE, $class = '') { 603 global $lang; 604 $html = ''; 605 606 // $html .= '<div class="mikio-search">'; 607 $html .= '<form class="mikio-search" action="' . wl($ID) . '" accept-charset="utf-8" method="get" role="search">'; 608 $html .= '<input class="mikio-control" autocomplete="off" type="search" placeholder="' . $lang['btn_search'] . '" value="' . (($ACT == 'search') ? htmlspecialchars($QUERY) : '') . '" accesskey="f" title="[F]" />'; 609 $html .= '<button class="mikio-control" type="submit" title="' . $lang['btn_search'] . '">'; 610 if($this->getConf('searchButton') == 'icon') { 611 $html .= $this->icon('search'); 612 } else { 613 $html .= $lang['btn_search']; 614 } 615 $html .= '</button>'; 616 $html .= '</form>'; 617 // $html .= '</div>'; 618 619 // $html .= '<div class="mikio-search ' . $class . '">'; 620 // $html .= '<form action="' . wl($ID) . '" accept-charset="utf-8" class="form-inline search" method="get" role="search">'; 621 // $html .= '<div class="input-group"><input id="sqsearch" autocomplete="off" type="search" placeholder="' . $lang['btn_search'] . '" value="' . (($ACT == 'search') ? htmlspecialchars($QUERY) : '') . '" accesskey="f" name="q" class="form-control" title="[F]" />'; 622 // $html .= '<div class="input-group-append"><button class="btn btn-secondary" type="submit" title="' . $lang['btn_search'] . '">'; 623 // if($this->getConf('searchButton') == 'icon') { 624 // $html .= $this->icon('search'); 625 // } else { 626 // $html .= $lang['btn_search']; 627 // } 628 // $html .= '</button></div></div>'; 629 // $html .= '<input type="hidden" name="do" value="search" />'; 630 // $html .= '</form>'; 631 // $html .= '</div>'; 632 633 if($print) { 634 print $html; 635 return ''; 636 } 637 638 return $html; 639 } 640 641 642 public function includeContent($print = FALSE) { 643 ob_start(); 644 tpl_content(false); 645 $html = ob_get_contents(); 646 ob_end_clean(); 647 648 $html = $this->includeIcons($html); 649 $html = $this->parseContent($html); 650 651 $html .= '<div style="clear:both"></div>'; 652 653 if($print) echo $html; 654 return $html; 655 } 656 657 /** ---- 658 * Print or return footer 659 * 660 * @param boolean $print print footer 661 * @return string html string containing footer 662 */ 663 public function includeFooter($print = TRUE) { 664 global $ACT; 665 666 $html = ''; 667 668 $html .= '<footer class="mikio-footer">'; 669 $html .= '<div class="doc">' . tpl_pageinfo(TRUE) . '</div>'; 670 $html .= $this->includeFile(TRUE); 671 if($ACT == 'show') { 672 $this->includePage('footer'); 673 } 674 675 if($this->getConf('footerCustomMenuText') != '') { 676 $html .= $this->includeMenu('footer', FALSE); 677 } 678 679 if($this->getConf('footerSearch')) { 680 $html .= '<div class="mikio-footer-search">'; 681 $html .= $this->includeSearch(FALSE); 682 $html .= '</div>'; 683 } 684 685 if($this->getConf('pageToolsFooter') != '' && $ACT == 'show') { 686 $html .= $this->includePageTools(FALSE); 687 } 688 689 $meta['licenseType'] = array('multichoice', '_choices' => array('none', 'badge', 'button')); 690 $meta['licenseImageOnly'] = array('onoff'); 691 692 $licenseType = $this->getConf('licenseType'); 693 if($licenseType != 'none') { 694 $html .= tpl_license($licenseType, $this->getConf('licenseImageOnly'), TRUE, TRUE); 695 } 696 697 $html .= '</footer>'; 698 699 if($print) echo $html; 700 return $html; 701 } 702 703 704 /** ---- 705 * Print or return breadcrumb trail 706 * 707 * @param boolean $print print out trail 708 * @param boolean $parse parse trail before printing 709 * @return string html string containing breadcrumbs 710 */ 711 public function includeBreadcrumbs($print = TRUE, $parse = TRUE) { 712 global $conf, $ID, $lang; 713 714 $html = '<div class="mikio-breadcrumbs">'; 715 716 if($conf['breadcrumbs']) { 717 if(!$this->getConf('breadcrumbPrefix') && !$this->getConf('breadcrumbSep')) { 718 ob_start(); 719 tpl_breadcrumbs(); 720 $html .= ob_get_contents(); 721 ob_end_clean(); 722 } else { 723 $sep = '•'; 724 $prefix = $lang['breadcrumb']; 725 726 if($this->getConf('breadcrumbSep')) { 727 $sep = $this->getConf('breadcrumbSepText'); 728 $img = $this->getMediaFile('breadcrumb-sep'); 729 730 if($img !== FALSE) { 731 $sep = '<img src="' . $img . '">'; 732 } 733 } 734 735 if($this->getConf('breadcrumbPrefix')) { 736 $prefix = $this->getConf('breadcrumbPrefixText'); 737 $img = $this->getMediaFile('breadcrumb-prefix'); 738 739 if($img !== FALSE) { 740 $prefix = '<img src="' . $img . '">'; 741 } 742 } 743 744 $crumbs = breadcrumbs(); 745 746 $html .= '<ul>'; 747 if($prefix != '') $html .= '<li class="prefix">' . $prefix . '</li>'; 748 749 $last = count($crumbs); 750 $i = 0; 751 foreach($crumbs as $id => $name) { 752 $i++; 753 $html .= '<li class="sep">' . $sep . '</li>'; 754 $html .= '<li' . ($i == $last ? ' class="curid"' : '') . '>'; 755 $html .= tpl_pagelink($id, NULL, TRUE); 756 $html .= '</li>'; 757 } 758 759 $html .= '</ul>'; 760 } 761 } else if($conf['youarehere']) { 762 if(!$this->getConf('breadcrumbPrefix') && !$this->getConf('breadcrumbSep')) { 763 ob_start(); 764 tpl_youarehere(); 765 $html .= ob_get_contents(); 766 ob_end_clean(); 767 } else { 768 $sep = ' » '; 769 $prefix = $lang['youarehere']; 770 771 if($this->getConf('breadcrumbSep')) { 772 $sep = $this->getConf('breadcrumbSepText'); 773 $img = $this->getMediaFile('breadcrumb-sep'); 774 775 if($img !== FALSE) { 776 $sep = '<img src="' . $img . '">'; 777 } 778 } 779 780 if($this->getConf('breadcrumbPrefix')) { 781 $prefix = $this->getConf('breadcrumbPrefixText'); 782 $img = $this->getMediaFile('breadcrumb-prefix'); 783 784 if($img !== FALSE) { 785 $prefix = '<img src="' . $img . '">'; 786 } 787 } 788 789 $html .= '<ul>'; 790 if($prefix != '') $html .= '<li class="prefix">' . $prefix . '</li>'; 791 $html .= '<li>' . tpl_pagelink(':'.$conf['start'], NULL, TRUE) . '</li>'; 792 793 $parts = explode(':', $ID); 794 $count = count($parts); 795 796 $part = ''; 797 for($i = 0; $i < $count - 1; $i++) { 798 $part .= $parts[$i].':'; 799 $page = $part; 800 if($page == $conf['start']) continue; 801 802 $html .= '<li class="sep">' . $sep . '</li>'; 803 $html .= '<li>' . tpl_pagelink($page, NULL, TRUE) . '</li>'; 804 } 805 806 resolve_pageid('', $page, $exists); 807 if(!(isset($page) && $page == $part.$parts[$i])) { 808 $page = $part.$parts[$i]; 809 if($page != $conf['start']) { 810 $html .= '<li class="sep">' . $sep . '</li>'; 811 $html .= '<li>' . tpl_pagelink($page, NULL, TRUE) . '</li>'; 812 } 813 } 814 815 $html .= '</ul>'; 816 } 817 } 818 819 $html .= '</div>'; 820 821 if($parse) $html = $this->includeIcons($html); 822 if($print) echo $html; 823 return $html; 824 } 825 826 827 828 /** 829 * Print out hero 830 * 831 * @author James Collins <james.collins@outlook.com.au> 832 */ 833 public function includeHero() { 834 global $ACT; 835 global $INFO; 836 837 // file_put_contents('output.txt', print_r($INFO, true)); 838 839 if($ACT == 'show') { 840 if($this->getConf('heroTitle')) { 841 print '<div class="mikio-hero d-flex flex-row">'; 842 print '<div class="mikio-hero-text flex-grow-1">'; 843 if ($ACT == 'show' && $this->getConf('breadcrumbPos') == 'hero') print $this->includeBreadcrumbs(); 844 845 print '<h1 id="mikio-hero-title">'; 846 print tpl_pagetitle(null, true).' '; // No idea why this requires a blank space afterwards to work? 847 print '</h1>'; 848 print '<h2 class="mikio-hero-subtext">'; 849 // print $this->heroSubtitle; // TODO scrape page for hero subtitle 850 print '</h2>'; 851 print '</div>'; 852 853 854 $hero_image = tpl_getMediaFile(array(':' . $INFO['namespace'] . ':hero.png', ':' . $INFO['namespace'] . ':hero.jpg', ':hero.png', ':hero.jpg', ':wiki:hero.png', ':wiki:hero.jpg', 'images/hero.png', 'images/hero.jpg'), false); 855 if($hero_image != '') $hero_image = ' style="background-image:url(\''.$hero_image.'\');"'; 856 857 print '<div class="mikio-hero-image"' . $hero_image . '></div>'; 858 print '</div>'; 859 } 860 } 861 } 862 863 864 /** 865 * Print out TOC 866 * 867 * @param boolean 868 * 869 * @author James Collins <james.collins@outlook.com.au> 870 */ 871 public function includeTOC($parse = TRUE) { 872 $html = ''; 873 874 $tocHtml = tpl_toc(true); 875 876 if($tocHtml != '') { 877 $tocHtml = preg_replace('/<li.*><div.*><a.*><\/a><\/div><\/li>\s*/', '', $tocHtml); 878 879 $html .= '<div class="mikio-toc">'; 880 $html .= $tocHtml; 881 $html .= '</div>'; 882 } 883 884 if($parse) $html = $this->includeIcons($html); 885 echo $html; 886 } 887 888 889 /** ---- 890 * Parse the string and replace icon elements with included icon libraries 891 * 892 * @param string $str content to parse 893 * @return string parsed string 894 */ 895 public function includeIcons($str) { 896 global $ACT, $MIKIO_ICONS; 897 898 $iconTag = $this->getConf('iconTag', 'icon'); 899 900 if($ACT == 'show' || $ACT == 'admin' && count($MIKIO_ICONS) > 0) { 901 $str = preg_replace_callback('/<' . $iconTag . ' ([\w\- #]*)>(?=[^>]*(<|$))/', 902 function ($matches) { 903 global $MIKIO_ICONS; 904 905 $s = $matches[0]; 906 907 if(count($MIKIO_ICONS) > 0) { 908 $icon = $MIKIO_ICONS[0]; 909 910 if(count($matches) > 1) { 911 $e = explode(' ', $matches[1]); 912 913 if(count($e) > 1) { 914 foreach($MIKIO_ICONS as $iconItem) { 915 if(strcasecmp($iconItem['name'], $e[0]) == 0) { 916 $icon = $iconItem; 917 918 $s = $icon['insert']; 919 for($i = 1; $i < 9; $i++) { 920 if(count($e) < $i || $e[$i] == '') { 921 if(isset($icon['$'.$i])) { 922 $s = str_replace('$' . $i, $icon['$'.$i], $s); 923 } 924 } else { 925 $s = str_replace('$' . $i, $e[$i], $s); 926 } 927 } 928 929 $dir = ''; 930 if(isset($icon['dir'])) $dir = $this->baseDir . 'icons/' . $icon['dir'] . '/'; 931 932 $s = str_replace('$0', $dir, $s); 933 934 break; 935 } 936 } 937 } else { 938 $s = str_replace('$1', $matches[1], $icon['insert']); 939 } 940 } 941 } 942 943 $s = preg_replace('/(class=")(.*)"/', '$1mikio-icon $2"', $s, -1, $count); 944 if($count == 0) { 945 $s = preg_replace('/(<\w* )/', '$1class="mikio-icon" ', $s); 946 } 947 948 return $s; 949 }, 950 $str); 951 } 952 953 return $str; 954 } 955 956 /** 957 * Parse HTML for bootstrap 958 * 959 * @author James Collins <james.collins@outlook.com.au> 960 * 961 * @param string $content HTML content to parse 962 * @return string Parsed HTML for bootstrap 963 */ 964 public function parseContent($content) { 965 global $INPUT, $ACT; 966 967 968 969 /* 970 return $content; 971 972 if($INPUT->str('page') == 'config') { 973 // Mikio sections 974 $admin_sections = array( 975 // Section Insert Before Icon 976 'navbar' => array('navbarUseTitleIcon', 'mdi:palette'), 977 ); 978 979 foreach ($admin_sections as $section => $items) { 980 981 $search = $items[0]; 982 $icon = $items[1]; 983 984 // $content = preg_replace('/<tr(.*)>\s+<td(.*)>\s+<span(.*)>(tpl»mikio»' . $search . ')<\/span>/', 985 // '</table></div></fieldset><fieldset id="bootstrap3__' . $section . '"><legend>' . $icon . ' ' . tpl_getLang("config_$section") . '</legend><div class="table-responsive"><table class="table table-hover table-condensed inline"><tr$1><td$2><span$3>$4</span>', $content); 986 987 $content = preg_replace('/<tr(.*)>\s+<td(.*)>\s+<span(.*)>(tpl»mikio»' . $search . ')<\/span>/', 988 '<tr class="default"><td class="mikio-config-table-header" colspan="2">' . tpl_getLang('config_' . $section) . '</td></tr><tr$1><td$2><span$3>$4</span>', $content); 989 990 991 992 993 } 994 } 995*/ 996 997 998 999 $html = new \simple_html_dom; 1000 $html->load($content, true, false); 1001 1002 if (!$html) return $content; 1003 1004 /* Buttons */ 1005 foreach($html->find('#config__manager button') as $node) { 1006 $c = explode(' ', $node->class); 1007 if(!in_array('mikio-button', $c)) $c[] = 'mikio-button'; 1008 $node->class = implode(' ', $c); 1009 } 1010 1011 1012 /* Buttons - Primary */ 1013 foreach($html->find('#config__manager [type=submit]') as $node) { 1014 $c = explode(' ', $node->class); 1015 if(!in_array('mikio-primary', $c)) $c[] = 'mikio-primary'; 1016 $node->class = implode(' ', $c); 1017 } 1018 1019 1020 1021 1022 $content = $html->save(); 1023 $html->clear(); 1024 unset($html); 1025 1026 return $content; 1027 1028 1029 1030 1031 1032 1033 1034 # Hide page title if hero is enabled 1035 if($this->getConf('heroTitle')) { 1036 $pageTitle = tpl_pagetitle(null, true); 1037 1038 foreach($html->find('h1,h2,h3,h4') as $elm) { 1039 if($elm->innertext == $pageTitle) { 1040 $elm->innertext = ''; 1041 break; 1042 } 1043 } 1044 } 1045 1046 # Hero subtitle 1047 foreach($html->find('p') as $elm) { 1048 $i = stripos($elm->innertext, '~~hero-subtitle'); 1049 if($i !== false) { 1050 $j = strpos($elm->innertext, '~~', $i + 2); 1051 if($j !== false) { 1052 if($j > $i + 16) { 1053 $subtitle = substr($elm->innertext, $i + 16, $j - $i - 16); 1054 foreach($html->find('.mikio-hero-subtext') as $subtitleElm) { 1055 $subtitleElm->innertext = $subtitle; 1056 } 1057 } 1058 1059 $elm->innertext = substr($elm->innertext, $j + 2); 1060 break; 1061 } 1062 } 1063 } 1064 1065 // Buttons 1066 // foreach($html->find('.button') as $elm) { 1067 // if ($elm->tag == 'form') { 1068 // continue; 1069 // } 1070 // $elm->class .= ' btn'; 1071 // } 1072 1073 foreach($html->find('#config__manager [type=submit], #edbtn__save, #dw__login [type=submit]') as $elm) { 1074 if(stripos($elm->class, 'btn') === FALSE) { 1075 $elm->class .= ' btn btn-primary'; 1076 } 1077 } 1078 1079 foreach($html->find('button, [type=button], [type=submit], [type=reset]') as $elm) { 1080 if(stripos($elm->class, 'btn') === FALSE) { 1081 $elm->class .= ' btn btn-outline-secondary'; 1082 } 1083 } 1084 1085 foreach($html->find('.btn_secedit [type=submit]') as $elm) { 1086 $elm->class .= ' btn-sm'; 1087 } 1088 1089 1090 # Section Edit icons 1091 foreach($html->find('.secedit.editbutton_section button') as $elm) { 1092 $elm->innertext = '<i class="fa fa-edit" aria-hidden="true"></i> ' . $elm->innertext; 1093 } 1094 1095 // Success 1096 foreach($html->find('div.success') as $elm) { 1097 $elm->class = 'alert alert-success'; //str_ireplace('success', 'alert alert-success', $elm->class); 1098 } 1099 1100 // Error 1101 foreach($html->find('div.error') as $elm) { 1102 $elm->class = 'alert alert-danger'; //str_ireplace('success', 'alert alert-success', $elm->class); 1103 } 1104 1105 // Forms 1106 foreach($html->find('input, select') as $elm) { 1107 $elm->class .= ' form-control'; 1108 } 1109 1110 foreach($html->find('h1, h2, h3, h4, h5') as $elm) { 1111 preg_match_all('/<(.*)>/iU', $elm->innertext, $matches); 1112 if(count($matches) >= 2 && (count($matches[0]) == count($matches[1]))) { 1113 for($i = 0; $i < count($matches[0]); $i++) { 1114 $icon = ''; 1115 1116 $class = explode(' ', $matches[1][$i]); 1117 if(count($class) >= 2) { 1118 $icon = '<i class="fa fa-' . $class[1] . '"></i>'; 1119 } 1120 1121 $elm->innertext = str_ireplace($matches[0][$i], $icon, $elm->innertext); 1122 } 1123 } 1124 } 1125 1126 // Tables 1127 foreach($html->find('table') as $elm) { 1128 $elm->class .= ' table-striped'; 1129 } 1130 1131 // Tabs 1132 foreach($html->find('ul.tabs') as $tabs) { 1133 $tabs->class = 'nav nav-tabs'; 1134 foreach($tabs->find('li') as $tab) { 1135 $tab->class .= ' nav-item'; 1136 1137 foreach($tab->find('a') as $link) { 1138 if(stripos($tab->class, 'active') !== FALSE) { 1139 $link->class .= 'active'; 1140 } 1141 1142 $link->class .= ' nav-link'; 1143 } 1144 } 1145 } 1146 1147 // Toolbar 1148 foreach($html->find('.tool__bar') as $toolbar) { 1149 $toolbar->class .= ' btn-group'; 1150 } 1151 1152 // Configuration Manager 1153 if($INPUT->str('page') == 'config') { 1154 1155 // Additional save buttons 1156 foreach ($html->find('#config__manager') as $cm) { 1157 $saveButtons = ''; 1158 1159 foreach($cm->find('p') as $elm) { 1160 $saveButtons = $elm->outertext; 1161 $elm->outertext = ''; 1162 } 1163 1164 foreach($cm->find('fieldset') as $elm) { 1165 $elm->innertext .= $saveButtons; 1166 } 1167 } 1168 1169 } 1170 1171 $content = $html->save(); 1172 $html->clear(); 1173 unset($html); 1174 1175 // Remove icons 1176 // $content = preg_replace('/<icon .*>[ +]/', '', $content); 1177 $content = $this->includeIcons($content); 1178 1179 return $content; 1180 } 1181 1182 1183 /*** GET LINK ***/ 1184 public function getLink($str) { 1185 $i = strpos($str, '://'); 1186 if($i !== false) return $str; 1187 1188 return wl($str); 1189 } 1190 1191 1192 public function userCanEdit() { 1193 global $INFO; 1194 global $ID; 1195 1196 $wiki_file = wikiFN($ID); 1197 if (@!file_exists($wiki_file)) return true; 1198 if ($INFO['isadmin'] || $INFO['ismanager']) return true; 1199 $meta_file = metaFN($ID, '.meta'); 1200 if (!$INFO['meta']['user']) return true; 1201 if ($INFO['client'] == $INFO['meta']['user']) return true; 1202 1203 return false; 1204 } 1205 1206 1207 public function getMediaFile($image) { 1208 global $INFO; 1209 $prefix = array(':'.$INFO['namespace'].':', ':', ':wiki:'); 1210 $ext = array('png', 'jpg', 'gif', 'svg'); 1211 1212 $prefix[] = ':'.$INFO['namespace'].':'; 1213 $prefix[] = ':'; 1214 $prefix[] = ':wiki:'; 1215 $theme = $this->getConf('customTheme'); 1216 if($theme != '') $prefix[] = $this->tplDir . 'themes/' . $theme . '/images/'; 1217 $prefix[] = 'images/'; 1218 1219 $search = array(); 1220 foreach($prefix as $pitem) { 1221 foreach($ext as $eitem) { 1222 $search[] = $pitem . $image . '.' . $eitem; 1223 } 1224 } 1225 1226 $img = ''; 1227 $file = ''; 1228 $url = ''; 1229 $ismedia = false; 1230 $found = false; 1231 1232 foreach($search as $img) { 1233 if(substr($img, 0, 1) == ':') { 1234 $file = mediaFN($img); 1235 $ismedia = true; 1236 } else { 1237 $file = tpl_incdir().$img; 1238 $ismedia = false; 1239 } 1240 1241 if(file_exists($file)) { 1242 $found = true; 1243 break; 1244 } 1245 } 1246 1247 if(!$found) return false; 1248 1249 if($ismedia) { 1250 $url = ml($img, '', true, '', false); 1251 } else { 1252 $url = tpl_basedir().$img; 1253 } 1254 1255 return $url; 1256 } 1257 1258 /** ---- 1259 * Print or return the page title 1260 * 1261 * @param string $page page id or empty string for current page 1262 * @param boolean $print print the generated content to the output buffer 1263 * 1264 * @return string generated content 1265 */ 1266 public function includePageTitle($page = '', $print = TRUE) { 1267 global $ID, $conf; 1268 1269 $html = ''; 1270 1271 if($page == '') $page = $ID; 1272 1273 $html = p_get_first_heading($page); 1274 $html = strip_tags($html); 1275 $html = preg_replace('/\s+/', ' ', $html); 1276 $html .= ' [' . strip_tags($conf['title']) . ']'; 1277 $html = trim($html); 1278 1279 if($print) echo $html; 1280 return $html; 1281 } 1282} 1283 1284 1285 1286global $TEMPLATE; 1287$TEMPLATE = \dokuwiki\template\mikio\Template::getInstance(); 1288