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