1<?php 2 3use dokuwiki\Extension\SyntaxPlugin; 4use dokuwiki\HTTP\DokuHTTPClient; 5 6/** 7 * DokuWiki Plugin gh (Syntax Component) 8 * 9 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 10 * @author Andreas Gohr <andi@splitbrain.org> 11 */ 12class syntax_plugin_gh extends SyntaxPlugin 13{ 14 /** 15 * Extension to highlighting language mapping 16 * 17 * When a extension is not found here it's assumed the extension name equals the language 18 * 19 * @var array 20 */ 21 protected $ext2lang = [ 22 'as' => 'actionscript3', 23 'bas' => 'gwbasic', 24 'h' => 'c', 25 'hpp' => 'cpp', 26 'hs' => 'haskell', 27 'htm' => 'html5', 28 'html' => 'html5', 29 'js' => 'javascript', 30 'pas' => 'pascal', 31 'pl' => 'perl6', 32 'py' => 'python', 33 'rb' => 'ruby', 34 'sh' => 'bash', 35 'yml' => 'yaml' 36 ]; 37 38 /** 39 * @return string Syntax mode type 40 */ 41 public function getType() 42 { 43 return 'substition'; 44 } 45 46 /** 47 * @return string Paragraph type 48 */ 49 public function getPType() 50 { 51 return 'block'; 52 } 53 54 /** 55 * @return int Sort order - Low numbers go before high numbers 56 */ 57 public function getSort() 58 { 59 return 155; 60 } 61 62 /** 63 * Connect lookup pattern to lexer. 64 * 65 * @param string $mode Parser mode 66 */ 67 public function connectTo($mode) 68 { 69 $this->Lexer->addSpecialPattern('{{gh>[^}]*}}', $mode, 'plugin_gh'); 70 } 71 72 /** @inheritDoc */ 73 public function handle($match, $state, $pos, Doku_Handler $handler) 74 { 75 $match = trim(substr($match, 5, -2)); 76 [$url, $lines] = sexplode(' ', $match, 2); 77 [$from, $to] = sexplode('-', $lines, 2); 78 79 $data = ['from' => (int)$from, 'to' => (int)$to]; 80 81 if (preg_match('/([\w.\-]+)\/([\w-]+\/[\w-]+(\/[\w-]+)?)\/blob\/([\w-]+)\/(.*)$/', $url, $m)) { 82 $data['base'] = $m[1]; 83 $data['repo'] = $m[2]; 84 $data['blob'] = $m[4]; 85 $data['file'] = $m[5]; 86 } 87 88 return $data; 89 } 90 91 /** @inheritDoc */ 92 public function render($mode, Doku_Renderer $renderer, $data) 93 { 94 if ($mode != 'xhtml') return false; 95 96 if (!$data['base']) return false; 97 if (!$data['repo']) return false; 98 if (!$data['blob']) return false; 99 if (!$data['file']) return false; 100 101 global $ID; 102 global $INPUT; 103 104 if ($data['base'] == 'github.com') { 105 $raw = 'https://raw.githubusercontent.com/' . $data['repo'] . '/' . $data['blob'] . '/' . $data['file']; 106 } else { 107 $raw = 'https://' . $data['base'] . '/' . $data['repo'] . '/raw/' . $data['blob'] . '/' . $data['file']; 108 } 109 $url = 'https://' . $data['base'] . '/' . $data['repo'] . '/blob/' . $data['blob'] . '/' . $data['file']; 110 111 // check if there's a usable cache 112 $text = false; 113 $cache = getCacheName($raw, '.ghplugin'); 114 $tcache = @filemtime($cache); 115 $tpage = @filemtime(wikiFN($ID)); 116 if ($tcache && $tpage && !$INPUT->bool('purge')) { 117 $now = time(); 118 if ($now - $tcache < ($now - $tpage) * 2) { 119 // use cache when it's younger than twice the age of the page 120 $text = io_readFile($cache); 121 } 122 } 123 124 // no cache loaded, get from HTTP 125 if (!$text) { 126 $http = new DokuHTTPClient(); 127 $text = $http->get($raw); 128 129 if ($text) { 130 // save to cache 131 io_saveFile($cache, $text); 132 } elseif ($tcache) { 133 // HTTP failed but there's an old cache - use it 134 $text = io_readFile($cache); 135 } 136 } 137 138 // WTF? there's nothing. we're done here 139 if (!$text) return true; 140 141 // apply line ranges 142 if ($data['from'] || $data['to']) { 143 $len = $data['to'] - $data['from']; 144 if ($len <= 0) $len = null; 145 146 $lines = explode("\n", $text); 147 $lines = array_slice($lines, $data['from'], $len); 148 $text = implode("\n", $lines); 149 } 150 151 // add icon 152 [$ext] = mimetype($data['file'], false); 153 $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 154 $class = 'mediafile mf_' . $class; 155 156 // add link 157 $renderer->doc .= '<dl class="file">' . DOKU_LF; 158 $renderer->doc .= '<dt><a href="' . $url . '" class="' . $class . '">'; 159 $renderer->doc .= hsc($data['file']); 160 $renderer->doc .= '</a></dt>' . DOKU_LF . '<dd>'; 161 162 if (isset($this->ext2lang[$ext])) { 163 $lang = $this->ext2lang[$ext]; 164 } else { 165 $lang = $ext; 166 } 167 168 $renderer->file($text, $lang); 169 $renderer->doc .= '</dd>'; 170 $renderer->doc .= '</dl>'; 171 return true; 172 } 173} 174