1<?php 2// must be run within Dokuwiki 3if(!defined('DOKU_INC')) die(); 4 5if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 6require_once(DOKU_PLUGIN.'syntax.php'); 7 8// implode, explode delimiter 9define('GITLOG_DELIMITER', '|'); 10 11class syntax_plugin_gitlog extends DokuWiki_Syntax_Plugin 12{ 13 function getType() 14 { 15 return 'substition'; 16 } 17 18 function getSort() 19 { 20 return 999; 21 } 22 23 /** 24 * Registers the regular expressions 25 * @param mixed $mode 26 * @return void 27 */ 28 function connectTo($mode) 29 { 30 $this->Lexer->addSpecialPattern('<gitlog:.+?>', $mode, 'plugin_gitlog'); 31 } 32 33 /** 34 * Prepares the matched syntax for use in the renderer 35 * @param mixed $match 36 * @param mixed $state 37 * @param mixed $pos 38 * @param Doku_Handler $handler 39 * @return array 40 */ 41 function handle($match, $state, $pos, Doku_Handler $handler) 42 { 43 // default value 44 $parameters = array(); 45 46 // regex 47 preg_match_all('#(\w+)\s*=\s*"(.*?)"#', $match, $return); 48 49 if (is_array($return) && isset($return[1]) && is_array($return[1])) 50 foreach($return[1] as $index => $name) 51 { 52 $parameters[$name] = $return[2][$index]; 53 } 54 55 return $parameters; 56 } 57 58 /** 59 * Renders Plugin Output 60 * @param string $mode 61 * @param Doku_Renderer $renderer 62 * @param array $data 63 * @return bool 64 */ 65 function render($mode, Doku_Renderer $renderer, $data) 66 { 67 if($mode == 'xhtml') 68 { 69 try { 70 71 // check if repository is set 72 if( ! isset($data['repository'])) { 73 throw new Exception('no repository set', 1); 74 } 75 76 // check limit parameter 77 if(isset($data['limit']) && is_numeric($data['limit'])) { 78 $limit = (int)($data['limit']); 79 } else { 80 $limit = 10; 81 } 82 83 // check bare parameter 84 if (empty($data['bare'])) { 85 $bare=false; 86 } else { 87 $bare=true; 88 } 89 90 // if a dir parameter is set, use this instead of config value 91 if ( ! empty($data['dir']) ) { 92 $repository = $this->clean_git_dir($data['dir']).$data['repository']; 93 } else { 94 $repository = $this->clean_git_dir($this->getConf('root_dir')).$data['repository']; 95 } 96 97 // check if path is invalid 98 if ( ! is_dir(dirname($repository)) ) { 99 throw new Exception('repository path not valid >> '.$repository, 1); 100 } 101 102 // get the git log and changed files 103 $log = $this->git_log($repository, $limit, $bare); 104 105 // start rendering 106 $renderer->doc .= '<ul class="gitlogplugin">'; 107 108 foreach($log as $row) 109 { 110 $renderer->doc .= '<li class="commit"><div class="message">'; 111 $renderer->doc .= hsc($row['message']); 112 $renderer->doc .= '</div><div class="meta">'; 113 $renderer->doc .= hsc($row['author']).' : '.date($this->getConf('date_format'), $row['timestamp']); 114 $renderer->doc .= '</div>'; 115 116 // render changed file list if any 117 if ( ! empty($row['changedfiles']) ) { 118 119 $renderer->doc .= ' <a href="#" class="seechanges">[See Changes]</a>'; 120 121 $renderer->doc .= '<ul class="changedfiles">'; 122 foreach ($row['changedfiles'] as $changedfile) { 123 $renderer->doc .= '<li>'.hsc($changedfile).'</li>'; 124 } 125 $renderer->doc .= '</ul>'; 126 } 127 128 $renderer->doc .= '</li>'; 129 130 } 131 132 $renderer->doc .= '</ul>'; 133 134 } catch (Exception $e) { 135 136 $renderer->doc .= 'Error: ' . $e->getMessage(); 137 return true; 138 139 } 140 141 return true; 142 } 143 144 return false; 145 } 146 147 /** 148 * Main Function to get log and changed files 149 * @param string $repo 150 * @param integer $limit 151 * @param boolean $bare 152 * @return array 153 */ 154 function git_log($repo, $limit = 10, $bare=false) 155 { 156 $format = array('%H', '%at', '%an', '%s'); 157 $params = implode(GITLOG_DELIMITER, $format); 158 $data = $this->run_git('log --pretty=format:"'.$params.'" -'.$limit, $repo, $bare); 159 $result = array(); 160 161 foreach($data as $line) 162 { 163 // explode 164 $columns = explode(GITLOG_DELIMITER, $line); 165 166 // run git show command 167 $changedfiles = $this->run_git('show --pretty="format:" --name-only '.$columns[0], $repo, $bare); 168 169 $row = array( 170 'commit' => $columns[0], 171 'timestamp' => $columns[1], 172 'author' => $columns[2], 173 'message' => $columns[3], 174 'changedfiles' => $this->cleanup_git_show($changedfiles), 175 ); 176 177 $result[] = $row; 178 } 179 180 return $result; 181 } 182 183 /** 184 * Runs a git command 185 * @param string $command 186 * @param string $repo 187 * @param boolean $bare 188 * @return mixed 189 */ 190 function run_git($command, $repo, $bare=false) 191 { 192 // if not bare, add git folder 193 if ( ! $bare ) { 194 $repo .= DIRECTORY_SEPARATOR . '.git'; 195 } 196 197 $output = array(); 198 $ret = 0; 199 $c = $this->getConf('git_exec').' --git-dir="'.$repo.'" '.$command; 200 exec($c, $output, $ret); 201 202 if ($ret != 0) { 203 204 //an error 205 206 $exceptionmessage = "The following command failed:<br>"; 207 $exceptionmessage .= $c . "<br>"; 208 $exceptionmessage .= "Please check configuration and/or path!"; 209 210 throw new Exception($exceptionmessage, 1); 211 212 } 213 214 return $output; 215 } 216 217 /** 218 * Removes empty elements from Array 219 * @param array $input 220 * @return array 221 */ 222 function cleanup_git_show(Array $input) 223 { 224 return array_filter($input, array($this, 'remove_empty')); 225 } 226 227 /** 228 * Array filter, removes empty elements 229 * @param mixed $value 230 * @return mixed 231 */ 232 function remove_empty($value) 233 { 234 return !empty($value) || $value === 0; 235 } 236 237 /** 238 * Cleans the git_dir string and 239 * removes possible errors 240 * @param string $value 241 * @return string 242 */ 243 function clean_git_dir($value) 244 { 245 $value = trim($value, "'"); 246 return rtrim($value, "\/").DIRECTORY_SEPARATOR; 247 } 248}