1<?php 2 3/** 4 * $Id: syntax.php 18 2017-06-19 15:22:35Z denis $ 5 * @based on Source Plugin by Chris Smith <chris@jalakai.co.uk> 6 * DokuWiki Plugin src (Syntax Component) 7 * $Id: syntax.php 18 2017-06-19 15:22:35Z denis $ 8 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 9 * @author DenisVS <deniswebcomm@gmail.com> 10 * @link https://www.dokuwiki.org/plugin:src 11 */ 12// must be run within Dokuwiki 13if (!defined('DOKU_INC')) 14 die(); 15 16class syntax_plugin_src extends DokuWiki_Syntax_Plugin { 17 18 /** 19 * @return string Syntax mode type 20 */ 21 public function getType() { 22 return 'substition'; 23 } 24 25 /** 26 * @return string Paragraph type 27 */ 28 public function getPType() { 29 return 'block'; 30 } 31 32 /** 33 * @return int Sort order - Low numbers go before high numbers 34 */ 35 public function getSort() { 36 return 200; 37 //return 321; 38 } 39 40 /** 41 * Connect lookup pattern to lexer. 42 * 43 * @param string $mode Parser mode 44 */ 45 public function connectTo($mode) { 46 $this->Lexer->addSpecialPattern('\{\{src.+?\}\}', $mode, 'plugin_src'); 47// $this->Lexer->addEntryPattern('<FIXME>',$mode,'plugin_src'); 48 } 49 50// public function postConnect() { 51// $this->Lexer->addExitPattern('</FIXME>','plugin_src'); 52// } 53 54 /** 55 * Handle matches of the src syntax 56 * 57 * @param string $match The match of the syntax 58 * @param int $state The state of the handler 59 * @param int $pos The position in the document 60 * @param Doku_Handler $handler The handler 61 * @return array Data for the renderer 62 */ 63 public function handle($match, $state, $pos, Doku_Handler &$handler) { 64 $match = (preg_replace("/\s+/", " ", substr($match, 5, -2))); // Чистим от повторяющихся пробелов 65 $t = explode(' -', $match); //бьём строку параметров на отдельные 66 foreach ($t as $key => $value) { 67 if (!empty($value)) { 68 $k = explode(' ', $value); //если параметр есть, разбить на праметр-значение через пробел 69 $tempData[$k['0']] = trim($k['1']); 70 $data[$k['0']] = trim($k['1']); 71 } 72 } 73 if (isset($tempData['p'])) { 74 $tempArray = explode(':', $tempData['p']); 75 $data['start'] = $tempArray[0]; 76 $data['end'] = $tempArray[1]; 77 unset($data['p']); 78 unset($tempData); 79 unset($tempArray); 80 } 81 return $data; 82 } 83 84 /** 85 * Render xhtml output or metadata 86 * 87 * @param string $mode Renderer mode (supported modes: xhtml) 88 * @param Doku_Renderer $renderer The renderer 89 * @param array $data The data from the handler() function 90 * @return bool If rendering was successful. 91 */ 92 public function render($mode, Doku_Renderer $renderer, $data) { 93 global $INFO; 94 global $conf; 95 96 $namespaceTypecasted = $this->_mb_str_replace(':', '/', $INFO['namespace']); //относительный путь в пространстве имён 97 $file = $this->_wikiPathFileToAbsolute($data['f'], $namespaceTypecasted, $conf['mediadir']); 98 $fileName = pathinfo($file, PATHINFO_BASENAME); 99 100 if ($mode != 'xhtml') { 101 return false; 102 } 103 104 $code = $this->_assembling($this->_fetchFile($file, $data['start'], $data['end'])); 105 106 if (empty($data['l'])) { 107 $lang = pathinfo($file, PATHINFO_EXTENSION); //синтаксис по расширению 108 } 109 else { 110 $lang = $data['l']; 111 } 112 113 if (!empty($data['e'])) { 114 $code = mb_convert_encoding($code, "utf-8", $data['e']); //применяем перекодировку 115 } 116 117 // highlighting by Geshi 118 $geshi = new GeSHi($code, $lang, DOKU_INC . 'inc/geshi'); 119 $geshi->set_encoding('utf-8'); 120 $geshi->enable_classes(); 121 $geshi->set_header_type(GESHI_HEADER_PRE); 122 $geshi->set_link_target($conf['target']['extern']); 123 // when you like to use your own wrapper, remove GeSHi's wrapper element 124 // we need to use a GeSHi wrapper to avoid <BR> throughout the highlighted text 125 $code = trim(preg_replace('!^<pre[^>]*>|</pre>$!', '', $geshi->parse_code()), "\n\r"); 126 $code = $this->_mb_str_replace(" \n", "", $code); 127// var_dump($data); 128 $renderer->doc .= '<dt><a href="_media/'.$INFO['namespace'].':'.$fileName.'" title="Download '.$fileName.'" class="mediafile mf_' . $lang . '">' . $fileName . '</a></dt>'; 129 $renderer->doc .= '<dt>'; 130 $renderer->doc .= '<pre class="code">'; 131 $renderer->doc .= $code; 132 $renderer->doc .= '</pre>'; 133 $renderer->doc .= '</dt>'; 134 135 return true; 136 } 137 138 function _fetchFile($fileName, $start = 0, $end = 0) { 139 $content = file($fileName); 140 if ($end != 0) { 141 for ($i = 0; $i < $end + 1; $i++) { 142 $content2[] = $content[$i]; 143 } 144 } 145 else { 146 $content2 = $content; 147 } 148 unset($content); 149 if ($start != 0) { 150 for ($i = $start; $i < sizeof($content2); $i++) { 151 $content3[] = $content2[$i]; 152 } 153 } 154 else { 155 $content3 = $content2; 156 } 157 unset($content2); 158 return $content3; 159 } 160 161 /** 162 * Преобразование массива в строку 163 * @param array $contentFromArray 164 * Массив с контентом построчно 165 * @return string 166 * Неформатированный текст на выходе 167 */ 168 function _assembling($contentFromArray) { 169 $this->contentFromArray = $contentFromArray; 170 $this->txtContent = FALSE; 171 foreach ($this->contentFromArray as $key => $val) { 172 $this->txtContent .= $val . "\n"; 173 } 174 $this->txtContent = substr($this->txtContent, 0, -1); ///< Отрубаем последний перенос 175 return $this->txtContent; 176 } 177 178 /** 179 * Аналог str_replace для мультибайтных строк 180 * @param string $needle Вхождение 181 * @param string $replacement Замена 182 * @param string $haystack Строка на входе 183 * @return string Строка на выходе 184 */ 185 function _mb_str_replace($needle, $replacement, $haystack) { 186 $needle_len = mb_strlen($needle); 187 $replacement_len = mb_strlen($replacement); 188 $pos = mb_strpos($haystack, $needle); 189 while ($pos !== false) { 190 $haystack = mb_substr($haystack, 0, $pos) . $replacement 191 . mb_substr($haystack, $pos + $needle_len); 192 $pos = mb_strpos($haystack, $needle, $pos + $replacement_len); 193 } 194 return $haystack; 195 } 196 197 /** 198 * Функция подъёма по директориям выше на заданное количество ступеней (слэшей). 199 * Обрубает лишние низлежащие. Дефолтно 1 уровень. 200 * @param string $url Исходный URL 201 * @param int $level Уровень, на который надо подняться 202 * @return string Результирующий URL 203 */ 204 function _dirUp($url, $level = 1) { 205 $i = 0; 206 do { 207 $pos = mb_strrpos($url, '/'); 208 $url = mb_substr($url, 0, $pos); 209 $i++; 210 } while ($level > $i); 211 return $url; 212 } 213 214 /** 215 * Преобразование пути к файлу из викиформата в абсолютный стантдартный. 216 * @param string $filePath путь к файлу в Wiki формате 217 * @param string $namespace текущий namespace 218 * @param string $startDir Директория, от которой строить путь. 219 */ 220 function _wikiPathFileToAbsolute($filePath, $namespace, $startDir) { 221 // From current level 222 if (preg_match('/^(((\.:{0,1}){0,1}(\w+\.*\-*)+)|\.(\:)?(\w+\-*\:*)+)\z/smu', $filePath)) { 223 $filePath = preg_replace('/^(\.)(\:)?((\w+\-*\:*)+\z)/sm', '$3', $filePath); 224 $filePath = preg_replace('/^((\.:{0,1}){0,1}((\w+\.*\-*)+)\z)/smu', '$3', $filePath); 225 $filePath = $this->_mb_str_replace(':', '/', $filePath); //путь к файлу (запись согласно namespaces) в пространстве имён 226 $filePath = $startDir . '/' . $namespace . '/' . $filePath; 227 } 228 // From root level 229 else if (preg_match('/^(\w+\-*\.*\:{0,5})+(\:+)(\w+\.*\-*\:{0,5})*\z|^(\:(\w+\.*\-*\:{0,5})*)\z/smu', $filePath)) { 230 $filePath = preg_replace('/^(\:*)((\w+\.*\-*\:{0,5})*\z)/smu', '$2', $filePath); 231 $filePath = $this->_mb_str_replace(':', '/', $filePath); //путь к файлу (запись согласно namespaces) в пространстве имён 232 $filePath = $startDir . '/' . '' . $filePath; 233 } 234 // From parent level 235 else if (preg_match('/^(\.:)?(\.\.):?(\w+\.*\-*\:{0,5})*\z/smu', $filePath)) { 236 $filePath = preg_replace('/^(\.:)?(\.\.):?((\w+\.*\-*\:{0,5})*\z)/smu', '$3', $filePath); 237 $filePath = $this->_mb_str_replace(':', '/', $filePath); //путь к файлу (запись согласно namespaces) в пространстве имён 238 if ($this->dirUp($namespace) != '') 239 $midSlash = '/'; 240 $filePath = $startDir . '/' . $this->dirUp($namespace) . $midSlash . $filePath; 241 } 242 return $filePath; 243 } 244 245} 246