1<?php 2/** 3 * DokuWiki Syntax Plugin Web Component. 4 * 5 */ 6if (!defined('DOKU_INC')) { 7 die(); 8} 9 10if (!defined('DOKU_PLUGIN')) { 11 define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); 12} 13 14require_once(__DIR__ . '/../webcomponent.php'); 15 16/** 17 * All DokuWiki plugins to extend the parser/rendering mechanism 18 * need to inherit from this class 19 * 20 * The name of the class must follow a pattern (don't change it) 21 * ie: 22 * syntax_plugin_PluginName_ComponentName 23 */ 24class syntax_plugin_webcomponent_dropdown extends DokuWiki_Syntax_Plugin 25{ 26 27 const INTERNAL_LINK_PATTERN = "\[\[.*?\]\](?!\])"; 28 private $linkCounter = 0; 29 private $dropdownCounter = 0; 30 31 /** 32 * Syntax Type. 33 * 34 * Needs to return one of the mode types defined in $PARSER_MODES in parser.php 35 * @see DokuWiki_Syntax_Plugin::getType() 36 */ 37 function getType() 38 { 39 return 'formatting'; 40 } 41 42 /** 43 * @return array 44 * Allow which kind of plugin inside 45 * 46 * No one of array('container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs') 47 * because we manage self the content and we call self the parser 48 */ 49 public function getAllowedTypes() 50 { 51 return array('formatting'); 52 } 53 54 /** 55 * How Dokuwiki will add P element 56 * 57 * * 'normal' - The plugin can be used inside paragraphs 58 * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 59 * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 60 * 61 * @see DokuWiki_Syntax_Plugin::getPType() 62 */ 63 function getPType() 64 { 65 return 'block'; 66 } 67 68 /** 69 * @see Doku_Parser_Mode::getSort() 70 * Higher number than the teaser-columns 71 * because the mode with the lowest sort number will win out 72 */ 73 function getSort() 74 { 75 return 200; 76 } 77 78 /** 79 * Create a pattern that will called this plugin 80 * 81 * @see Doku_Parser_Mode::connectTo() 82 * @param string $mode 83 */ 84 function connectTo($mode) 85 { 86 87 88 $pattern = webcomponent::getLookAheadPattern(self::getTag()); 89 $this->Lexer->addEntryPattern($pattern, $mode, 'plugin_' . webcomponent::PLUGIN_NAME . '_' . $this->getPluginComponent()); 90 91 // Link 92 $this->Lexer->addPattern(self::INTERNAL_LINK_PATTERN, 'plugin_' . webcomponent::PLUGIN_NAME . '_' . $this->getPluginComponent()); 93 94 95 } 96 97 public function postConnect() 98 { 99 100 101 $this->Lexer->addExitPattern('</' . self::getTag() . '>', 'plugin_' . webcomponent::PLUGIN_NAME . '_' . $this->getPluginComponent()); 102 103 // Link 104 $this->Lexer->addPattern(self::INTERNAL_LINK_PATTERN, 'plugin_' . webcomponent::PLUGIN_NAME . '_' . $this->getPluginComponent()); 105 106 } 107 108 /** 109 * 110 * The handle function goal is to parse the matched syntax through the pattern function 111 * and to return the result for use in the renderer 112 * This result is always cached until the page is modified. 113 * @see DokuWiki_Syntax_Plugin::handle() 114 * 115 * @param string $match 116 * @param int $state 117 * @param int $pos 118 * @param Doku_Handler $handler 119 * @return array|bool 120 */ 121 function handle($match, $state, $pos, Doku_Handler $handler) 122 { 123 124 switch ($state) { 125 126 case DOKU_LEXER_ENTER: 127 128 // Suppress the tag name 129 $match = utf8_substr($match, strlen(self::getTag()) + 1, -1); 130 $parameters = webcomponent::parseMatch($match); 131 return array($state, $parameters); 132 133 case DOKU_LEXER_UNMATCHED : 134 135 // Normally we don't get any here 136 return array($state, $match); 137 138 case DOKU_LEXER_MATCHED : 139 140 $parameters = array(); 141 142 if (preg_match('/' . self::INTERNAL_LINK_PATTERN . '/msSi', $match . DOKU_LF)) { 143 // We have a internal link, we parse it (code form the function internallink in handler.php) 144 // 145 // Strip the opening and closing markup 146 $link = preg_replace(array('/^\[\[/', '/\]\]$/u'), '', $match); 147 148 // Split title from URL 149 $link = explode('|', $link, 2); 150 if (!isset($link[1])) { 151 $link[1] = null; 152 } 153 $link[0] = trim($link[0]); 154 // we expect only a local link 155 $parameters['locallink'][$this->linkCounter]['pageid'] = $link[0]; 156 $parameters['locallink'][$this->linkCounter]['content'] = $link[1]; 157 $this->linkCounter++; 158 159 } 160 161 return array($state, $parameters); 162 163 case DOKU_LEXER_EXIT : 164 165 return array($state, ''); 166 167 168 } 169 170 return array(); 171 172 } 173 174 /** 175 * Render the output 176 * @see DokuWiki_Syntax_Plugin::render() 177 * 178 * @param string $mode 179 * @param Doku_Renderer $renderer 180 * @param array $data - what the function handle() return'ed 181 * @return bool 182 */ 183 function render($mode, Doku_Renderer $renderer, $data) 184 { 185 186 if ($mode == 'xhtml') { 187 188 /** @var Doku_Renderer_xhtml $renderer */ 189 list($state, $parameters) = $data; 190 switch ($state) { 191 192 case DOKU_LEXER_ENTER : 193 $this->dropdownCounter++; 194 $dropDownId = "dropDown".$this->dropdownCounter; 195 $name = 'Name'; 196 if (array_key_exists("name", $parameters)) { 197 $name = $parameters["name"]; 198 } 199 $renderer->doc .= '<li class="nav-item dropdown">' 200 . DOKU_TAB . '<a id="'.$dropDownId.'" href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Title">'.$name.'</a>' 201 . DOKU_TAB . '<div class="dropdown-menu" aria-labelledby="'.$dropDownId.'">'; 202 break; 203 204 case DOKU_LEXER_UNMATCHED : 205 206 $renderer->doc .= $renderer->_xmlEntities($parameters); 207 break; 208 209 case DOKU_LEXER_MATCHED: 210 211 212 if (array_key_exists('locallink', $parameters)) { 213 214 foreach ($parameters['locallink'] as $link) { 215 $pageId = $link['pageid']; 216 $content = $link['content']; 217 218 $renderer->doc .= '<a href="' . wl($pageId) . '" class="dropdown-item"'; 219 if (!page_exists($pageId)){ 220 $renderer->doc .= ' style="color: #d30;border-bottom: 1px dashed;"'; 221 } 222 $renderer->doc .= '>' . $renderer->_xmlEntities($content) . '</a>'; 223 } 224 225 } 226 break; 227 228 case DOKU_LEXER_EXIT : 229 230 $renderer->doc .= '</div></li>'; 231 232 // Counter on NULL 233 $this->linkCounter = 0; 234 break; 235 } 236 return true; 237 } 238 return false; 239 } 240 241 242 public static function getTag() 243 { 244 list(/* $t */, /* $p */, /* $n */, $c) = explode('_', get_called_class(), 4); 245 return (isset($c) ? $c : ''); 246 } 247 248 249} 250