1<?php
2
3namespace dokuwiki\template\notos;
4
5use dokuwiki\Menu\Item\Login;
6use dokuwiki\Menu\MenuInterface;
7use dokuwiki\template\twigstarter\CustomControllerInterface;
8use dokuwiki\template\twigstarter\TemplateController;
9
10/**
11 * Notos custom methods
12 */
13class CustomController implements CustomControllerInterface
14{
15    protected $tpl;
16
17    /** @inheritDoc */
18    public function __construct(TemplateController $tpl)
19    {
20        $this->tpl = $tpl;
21    }
22
23    /**
24     * Renders the navigational list items
25     *
26     * We only support two levels. Deeper levels that might be returned from the navigation
27     * control page will simply be ignored here
28     *
29     * @param string $class additional classes to add to the navigation
30     * @return string
31     */
32    public function renderNavigation($class = '')
33    {
34        global $ID;
35        global $ACT;
36        $html = '';
37
38        if ($ACT !== 'show') return $html;
39
40        $controlPage = tpl_getConf('navigation_page');
41        if (!page_exists($controlPage)) return $html;
42
43        $pages = $this->parseNavigation(wikiFN($controlPage));
44
45        $html .= '<ul class="navtabs ' . $class . '">';
46        foreach ($pages as $page) {
47            if ($this->isActive($page['page'], $ID)) {
48                $active = ' active';
49            } else {
50                $active = '';
51            }
52
53            $html .= '<li class="primary' . $active . '">';
54            $html .= $this->navItemHTML($page);
55            $html .= '<span class="opener">' . inlineSVG(__DIR__ . '/ico/plus-circle-outline.svg') . '</span>';
56            $html .= '</li>';
57
58            // second level is a second item, because we reorder with flex box later
59            if (isset($page['sub'])) {
60                $html .= '<li class="secondary' . $active . '">';
61                $html .= '<ul>';
62                foreach ($page['sub'] as $subpage) {
63                    $html .= '<li>';
64                    $html .= $this->navItemHTML($subpage);
65                    $html .= '</li>';
66                }
67                $html .= '</ul>';
68                $html .= '</li>';
69            }
70        }
71        $html .= '</ul>';
72
73        return $html;
74    }
75
76    /**
77     * Print a single Navigation Item
78     *
79     * @param $item
80     * @return string
81     */
82    protected function navItemHTML($item)
83    {
84        if ($item['type'] == 'internal') {
85            return html_wikilink($item['page'], $item['title']);
86        }
87        $attr = buildAttributes([
88            'href' => $item['page'],
89            'title' => $item['page'],
90            'class' => 'external',
91        ]);
92        return "<a $attr>" . hsc($item['title']) . '</a>';
93    }
94
95    /**
96     * Is the current parent page "active" depending on the second one?
97     *
98     * @param string $parent
99     * @param string $page
100     * @return bool
101     * @todo this should have tests
102     */
103    protected function isActive($parent, $page)
104    {
105        if ($parent === $page) return true;
106        $parentNS = explode(':', $parent);
107        array_pop($parentNS);
108        $pageParts = explode(':', $page);
109        $pageParts = array_splice($pageParts, 0, count($parentNS));
110
111        $parent = join(':', $parentNS);
112        $page = join(':', $pageParts);
113
114        if ($parent === $page) return true;
115        return false;
116    }
117
118    /**
119     * Parses the given page for a nested, unordered list
120     *
121     * Returns the list as nested array
122     *
123     * @param string $controlPageFile
124     * @return array
125     */
126    public function parseNavigation($controlPageFile)
127    {
128        $instructions = p_cached_instructions($controlPageFile);
129
130        $result = [];
131        $pointers = [&$result]; // point to current list
132        $pidx = 0; // index of last pointer
133
134        foreach ($instructions as $instruction) {
135            switch ($instruction[0]) {
136                case 'listu_open':
137                    // find index of last item in current list
138                    $eidx = count($pointers[$pidx]) - 1;
139                    // open a new list in the last item of the current list
140                    $pointers[$pidx][$eidx]['sub'] = [];
141                    // store that new list in the pointer stack
142                    $pointers[] = &$pointers[$pidx][$eidx]['sub'];
143                    // increase pointer index
144                    $pidx++;
145                    break;
146                case 'listu_close':
147                    // close a list
148                    array_pop($pointers);
149                    $pidx--;
150                    break;
151                case 'internallink':
152                    // resolve ID
153                    global $ID;
154                    $resolver = new \dokuwiki\File\PageResolver($ID);
155                    $page = $resolver->resolveId($instruction[1][0]);
156                    // append to current list
157                    $pointers[$pidx][] = [
158                        'type' => 'internal',
159                        'page' => $page,
160                        'title' => $instruction[1][1],
161                    ];
162                    break;
163                case 'externallink':
164                    // append to current list
165                    $pointers[$pidx][] = [
166                        'type' => 'external',
167                        'page' => $instruction[1][0],
168                        'title' => $instruction[1][1],
169                    ];
170                    break;
171            }
172        }
173
174        // clean up by moving everything up into a root array
175        if (isset($result[-1])) {
176            $result = $result[-1]['sub'];
177        }
178
179        return $result;
180    }
181
182    /**
183     * Initializes and returns one of the menus
184     *
185     * @param string $type
186     * @return MenuInterface
187     */
188    public function menu($type)
189    {
190        $class = '\\dokuwiki\\Menu\\' . ucfirst($type) . 'Menu';
191        if (class_exists($class)) {
192            return new $class();
193        } else {
194            return new NotosMenu($type);
195        }
196    }
197
198    /**
199     * Get Login/Logout Button
200     * @return string
201     */
202    public function loginButton()
203    {
204        try {
205            return (new Login())->asHtmlLink();
206        } catch (\RuntimeException $ignored) {
207            // item not available
208            return '';
209        }
210    }
211}
212