xref: /template/sprintdoc/Template.php (revision 3e1e2a1f184c1fa9310ac944093fa2809f3f9f49)
1<?php
2
3namespace dokuwiki\template\sprintdoc;
4
5/**
6 * Class Template
7 *
8 * provides additional logic for the sprintdoc template
9 *
10 * @package dokuwiki\template\sprintdoc
11 */
12class Template {
13
14    /** @var array loaded plugins */
15    protected $plugins = array(
16        'sqlite' => null,
17        'tagging' => null,
18        'magicmatcher' => null,
19        'tplinc' => null,
20        'sitemapnavi' => null,
21    );
22
23    /** @var string the type of special navigation to use */
24    protected $nav = '';
25
26
27    /**
28     * Get the singleton instance
29     *
30     * @return Template
31     */
32    public static function getInstance() {
33        static $instance = null;
34        if($instance === null) $instance = new Template();
35        return $instance;
36    }
37
38    /**
39     * Template constructor.
40     */
41    protected function __construct() {
42        $this->initializePlugins();
43        $this->initNavigationCookie();
44
45        /** @var \Doku_Event_Handler */
46        global $EVENT_HANDLER;
47        $EVENT_HANDLER->register_hook('PLUGIN_TPLINC_LOCATIONS_SET', 'BEFORE', $this, 'registerIncludes');
48    }
49
50    /**
51     * Load all the plugins we support directly
52     */
53    protected function initializePlugins() {
54        $this->plugins['sqlite'] = plugin_load('helper', 'sqlite');
55        if($this->plugins['sqlite']) {
56            $this->plugins['tagging'] = plugin_load('helper', 'tagging');
57            $this->plugins['magicmatcher'] = plugin_load('syntax', 'magicmatcher_issuelist');
58        }
59        $this->plugins['tplinc'] = plugin_load('helper', 'tplinc');
60        $this->plugins['sitemapnavi'] = plugin_load('helper', 'sitemapnavi');
61    }
62
63    /**
64     * Makes include position info available to the tplinc plugin
65     *
66     * @param \Doku_Event $event
67     */
68    public function registerIncludes(\Doku_Event $event) {
69        $event->data['footer'] = 'Footer below the page content';
70        $event->data['sidebarfooter'] = 'Footer below the sidebar';
71        $event->data['sidebarheader'] = 'Header above the sidebar';
72    }
73
74    /**
75     * Get the content to include from the tplinc plugin
76     *
77     * prefix and postfix are only added when there actually is any content
78     *
79     * @param string $location
80     * @param string $pre prepend this before the content
81     * @param string $post append this to the content
82     * @return string
83     */
84    public function getInclude($location, $pre = '', $post = '') {
85        if(!$this->plugins['tplinc']) return '';
86        $content = $this->plugins['tplinc']->renderIncludes($location);
87        if($content === '') return '';
88        return $pre . $content . $post;
89    }
90
91    /**
92     * Sets a cookie to remember the requested special navigation
93     */
94    protected function initNavigationCookie() {
95        if ($this->plugins['sitemapnavi'] === null) return;
96        global $INPUT;
97
98        $nav = $INPUT->str('nav');
99        if($nav) {
100            set_doku_pref('nav', $nav);
101            $this->nav = $INPUT->str('nav');
102        } else {
103            $this->nav = get_doku_pref('nav', 'sidebar');
104        }
105    }
106
107    /**
108     * Return the navigation for the sidebar
109     *
110     * Defaults to the standard sidebar mechanism, but supports also the sitemapnavi plugin
111     *
112     * @return string
113     */
114    public function getNavigation() {
115        global $ID;
116        global $conf;
117
118        // id of the current sidebar, each sidebar must have its own state
119        $header = sprintf('<div id="sidebarId" class="%s"></div>', md5(page_findnearest($conf['sidebar'])));
120        // add tabs if multiple navigation types available
121        if ($this->plugins['sitemapnavi'] !== null) {
122            $header .= '<ul class="sidebar-tabs">';
123            $header .= '<li class="' . ($this->nav === 'sidebar' ? 'active' : '') . '">' .
124                '<a href="' . wl($ID, ['nav' => 'sidebar']) . '">'.tpl_getLang('nav_sidebar').'</a></li>';
125            $header .= '<li class="' . ($this->nav === 'sitemap' ? 'active' : '') . '">' .
126                '<a href="' . wl($ID, ['nav' => 'sitemap']) . '">'.tpl_getLang('nav_sitemap').'</a></li>';
127            $header .= '</ul>';
128        }
129
130        // decide what to show
131        if ($this->nav === 'sitemap') {
132            // site tree created by sitemapnavi plugin
133            $nav = '<nav class="nav-sitemapnavi" id="plugin__sitemapnavi">';
134            $nav .= $this->plugins['sitemapnavi']->getSiteMap(':');
135            $nav .= '</nav>';
136        } else {
137            // main navigation, loaded from standard sidebar, fixed up by javascript
138            $nav = '<nav class="nav-main">';
139            // immeadiately hide the navigation (if javascript available)
140            // it will be restyled and made visible again by our script later
141            $nav .= '<script type="application/javascript">
142                        document.getElementsByClassName("nav-main")[0].style.visibility = "hidden";
143                     </script>';
144            $nav .= tpl_include_page($conf['sidebar'], false, true);
145            $nav .= '</nav>';
146        }
147
148        return $header . $nav;
149    }
150
151    /**
152     * Default class defining is the sidebar should collapse
153     *
154     * @return string
155     */
156    public function fullWidthClass() {
157        global $ACT;
158        // no auto collapsing? empty class
159        if (!tpl_getConf('autocollapse')) return '';
160        // mode show? empty class
161        if ($ACT === "show") return '';
162        // anything else? wide content
163        return 'wide-content ';
164    }
165
166    /**
167     * Get all the tabs to display
168     *
169     * @return array
170     */
171    public function getMetaBoxTabs() {
172        global $lang, $INFO;
173        $tabs = array();
174
175        $toc = tpl_toc(true);
176        if($toc) {
177            $tabs[] = array(
178                'id' => 'spr__tab-toc',
179                'label' => $lang['toc'],
180                'tab' => $toc,
181                'count' => null,
182            );
183        }
184
185        if($this->plugins['tagging']) {
186            $tabs[] = array(
187                'id' => 'spr__tab-tags',
188                'label' => tpl_getLang('tab_tags'),
189                'tab' => $this->plugins['tagging']->tpl_tags(false),
190                'count' => count($this->plugins['tagging']->findItems(array('pid' => $INFO['id']), 'tag')),
191            );
192        }
193
194        if ($this->plugins['magicmatcher']) {
195            $tabs[] = array(
196                'id' => 'spr__tab-issues',
197                'label' => tpl_getLang('tab_issues'),
198                'tab' => $this->plugins['magicmatcher']->getIssueListHTML(),
199                'count' =>  $this->plugins['magicmatcher']->getCountIssues(),
200            );
201        }
202
203        return $tabs;
204    }
205
206    /**
207     * Creates an image tag and includes the first found image correctly resized
208     *
209     * @param string $tag
210     * @param array $attributes
211     * @param int $w
212     * @param int $h
213     * @param bool $crop
214     * @return string
215     */
216    public static function getResizedImgTag($tag, $attributes, $w, $h, $crop = true) {
217        $attr = '';
218        $medias = array();
219
220        // the attribute having an array defines where the image goes
221        foreach($attributes as $attribute => $data) {
222            if(is_array($data)) {
223                $medias = $data;
224                $attr = $attribute;
225            }
226        }
227        // if the image attribute could not be found return
228        if(!$attr || !$medias) return '';
229
230        // try all medias until an existing one is found
231        $media = '';
232        foreach($medias as $media) {
233            if(file_exists(mediaFN($media))) break;
234            $media = '';
235        }
236        if($media === '') return '';
237
238        // replace the array
239        $media = ml($media, array('w' => $w, 'h' => $h, 'crop' => (int) $crop), true, '&');
240        $attributes[$attr] = $media;
241
242        // return the full tag
243        return '<' . $tag . ' ' . buildAttributes($attributes) . ' />' . "\n";
244    }
245
246    /**
247     * Embed the main logo
248     *
249     * Tries a few different locations
250     */
251    public function mainLogo() {
252        global $conf;
253
254        // homepage logo should not link to itself (BITV accessibility requirement)
255        $linkit = (strcmp(wl(), $_SERVER['REQUEST_URI']) !== 0);
256        if($linkit) {
257            $title = $conf['title'] . tpl_getLang('adjunct_linked_logo_text');
258        } else {
259            $title = tpl_getLang('adjunct_start_logo_text') . $conf['title'];
260        }
261
262        $desktop = self::getResizedImgTag(
263            'img',
264            array(
265                'class' => 'mobile-hide',
266                'src' => array('wiki:logo-wide.svg', 'wiki:logo.svg', 'wiki:logo-wide.png', 'wiki:logo.png'),
267                'alt' => $title,
268            ),
269            0, 250, false
270        );
271        $mobile = self::getResizedImgTag(
272            'img',
273            array(
274                'class' => 'mobile-only',
275                'src' => array(
276                    'wiki:logo-32x32.svg', 'wiki:favicon.svg', 'wiki:logo-square.svg', 'wiki:logo.svg',
277                    'wiki:logo-32x32.png', 'wiki:favicon.png', 'wiki:logo-square.png', 'wiki:logo.png'
278                ),
279                'alt' => $title,
280            ),
281            32, 32
282        );
283
284        // homepage logo should not link to itself (BITV accessibility requirement)
285        if($linkit) {
286            tpl_link(wl(), $desktop, 'accesskey="h" title="[H]"');
287            tpl_link(wl(), $mobile, 'accesskey="h" title="[H]"');
288        } else {
289            echo $desktop;
290            echo $mobile;
291        }
292    }
293
294    /**
295     * Add the current mode information to the hierarchical breadcrumbs
296     */
297    public function breadcrumbSuffix() {
298        global $ACT;
299        global $lang;
300        global $INPUT;
301        global $ID;
302        global $conf;
303        global $IMG;
304        if($ACT == 'show') return;
305
306        // find an apropriate label for the current mode
307        if($ACT) {
308            $label = tpl_getLang('mode_' . $ACT);
309            if(!$label) {
310                if(isset($lang['btn_' . $ACT])) {
311                    $label = $lang['btn_' . $ACT];
312                } else {
313                    $label = $ACT;
314                }
315            }
316        } else {
317            // actually we would need to create a proper namespace breadcrumb path here,
318            // but this is the most simplest thing we can do for now
319            if(defined('DOKU_MEDIADETAIL')) {
320                $label = hsc(noNS($IMG));
321            } else {
322                return;
323            }
324        }
325
326        if($ACT == 'admin' && $INPUT->has('page')) {
327            $link = wl($ID, array('do' => 'admin'));
328            echo '<bdi> : <a href="' . $link . '"><strong>' . $label . '</strong></a></bdi>';
329
330            /** @var \DokuWiki_Admin_Plugin $plugin */
331            $plugin = plugin_load('admin', $INPUT->str('page'));
332            if(!$plugin) return;
333
334            $label = $plugin->getMenuText($conf['lang']);
335        }
336
337        echo '<bdi><span class="curid"> : <strong>' . $label . '</strong></span></bdi>';
338    }
339}
340