xref: /template/mikio/mikio.php (revision ab45ba7165dc08ce8ac280c60e4318ef9df9d00d)
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('/&lt;' . $iconTag . ' ([\w\- #]*)&gt;(?=[^>]*(<|$))/',
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('/&lt;(.*)&gt;/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('/&lt;icon .*&gt;[ +]/', '', $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