1<?php 2/** 3 * DokuWiki Plugin translationbuddy (Syntax Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author H�kan Sandell <sandell.hakan@gmail.com> 7 */ 8 9// must be run within Dokuwiki 10if (!defined('DOKU_INC')) die(); 11 12if (!defined('DOKU_LF')) define('DOKU_LF', "\n"); 13if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t"); 14if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 15 16require_once DOKU_PLUGIN.'syntax.php'; 17 18class syntax_plugin_translationbuddy extends DokuWiki_Syntax_Plugin { 19 public function getType() { 20 return 'substition'; 21 } 22 23 public function getPType() { 24 return 'block'; 25 } 26 27 public function getSort() { 28 return 99; 29 } 30 31 public function connectTo($mode) { 32 $this->Lexer->addSpecialPattern('----+ *translationbuddy *-+\n.*?\n----+',$mode,'plugin_translationbuddy'); 33 } 34 35 public function handle($match, $state, $pos, &$handler){ 36 return $this->parseData($match); 37 } 38 39 public function render($mode, &$renderer, $data) { 40 if($mode == 'xhtml') { 41 return $this->_showData($renderer,$data); 42 } 43 return false; 44 } 45 46 /** 47 * Parse syntax data block, return keyed array of values 48 * 49 * You may use the # character to add comments to the block. 50 * Those will be ignored and will neither be displayed nor saved. 51 * If you need to enter # as data, escape it with a backslash (\#). 52 * If you need a backslash, escape it as well (\\) 53 */ 54 function parseData($match){ 55 // get lines 56 $lines = explode("\n",$match); 57 array_pop($lines); 58 array_shift($lines); 59 60 // parse info 61 $data = array(); 62 foreach ( $lines as $line ) { 63 // ignore comments and bullet syntax 64 $line = preg_replace('/(?<![&\\\\])#.*$/','',$line); 65 $line = preg_replace('/^ \* /','',$line); 66 $line = str_replace('\\#','#',$line); 67 $line = trim($line); 68 if(empty($line)) continue; 69 list($key,$value) = preg_split('/\s*:\s*/',$line,2); 70 $key = strtolower($key); 71 if ($data[$key]){ 72 $data[$key] .= ' ' . trim($value); 73 }else{ 74 $data[$key] = trim($value); 75 } 76 } 77 return $data; 78 } 79 80 function _showData(&$R, $data){ 81 global $ID, $conf; 82 83 // default settings 84 if (!is_numeric($data['daysnew'])) $data['daysnew'] = 14; 85 if (!is_numeric($data['outdated'])) $data['outdated'] = 5; 86 if ($data['ignore_ns']) { 87 $data['ignore_ns'] = str_replace(',', ' ', $data['ignore_ns']); 88 $ignore_ns = explode(' ', $data['ignore_ns']); 89 $ignore_ns = array_filter($ignore_ns); 90 } else { 91 $ignore_ns = array(); 92 } 93 if ($data['langs']) { 94 $data['langs'] = str_replace(',', ' ', $data['langs']); 95 $langs = explode(' ', $data['langs']); 96 $langs = array_filter($langs); 97 } elseif ($conf['plugin'] && $conf['plugin']['translation']) { 98 $langs = explode(' ', $conf['plugin']['translation']['translations']); 99 $langs = array_filter($langs); 100 } else { 101 $langs = array(); 102 } 103 104 // get all pages 105 $pages = idx_get_indexer()->getPages(); 106 $items = array(); 107 $new_pages = array(); 108 foreach($pages as $id){ 109 //skip hidden, non existing and restricted files 110 if(isHiddenPage($id)) continue; 111 if(auth_aclcheck($id,'','') < AUTH_READ) continue; 112 113 $page = $id; 114 $ns = $this->baseNS($page); 115 $lc = 'en'; 116 if ($ns && in_array($ns,$langs)) { 117 $lc = $ns; 118 $page = $this->skipBaseNS($page); 119 $ns = $this->baseNS($page); 120 } 121 if (!$ns) $ns = '-'; 122 123 $fn = wikiFN($id); 124 $date = @filemtime($fn); 125 if($date && !in_array($ns, $ignore_ns)) { 126 127 $items[$lc][$ns][$page] = $date; 128 if (filectime($fn) > time() - 60*60*24*$data['daysnew']) { 129 $new_pages[] = $id; 130 } 131 } 132 } 133 sort($new_pages); 134 135 // new page report 136 $R->doc .= '<b>New pages last '.hsc($data['daysnew']).' days</b>'; 137 $R->listu_open(); 138 foreach ($new_pages as $page) { 139 $R->listitem_open(1); 140 $R->listcontent_open(); 141 $R->doc .= '<a href="'.wl($page).'">'.$page.'</a>'; 142 $R->listcontent_close(); 143 $R->listitem_close(); 144 } 145 $R->listu_close(); 146 $R->doc .= '<b>Translation status</b>'; 147 148 // translation effort report 149 $R->doc .= '<table>'; 150 $R->doc .= '<tr><th>Lang</th><th>Namespace</th><th>Pages</th><th>Translated</th><th>Details</th></tr>'; 151 foreach ($items as $lc => $item) { 152 $pageTotal = 0; 153 $first = true; 154 foreach ($item as $ns => $pages) { 155 $R->doc .= '<tr>'; 156 if ($first) { 157 $first = false; 158 $R->doc .= '<td rowspan="'.count($item).'" id="translationbuddy__'.$lc.'">'.$lc.'</td>'; 159 $R->toc_additem('translationbuddy__'.$lc, $lc, $R->lastlevel+1); 160 } 161 $idx = ($ns=='-' ? ($lc=='en'?'-1':$lc) : ($lc=='en'?'':$lc.':').$ns); 162 $R->doc .= '<td><a href="'.wl($ID,'idx='.rawurlencode($idx)).'" >'.$ns.'</a></td>'; 163 $R->doc .= '<td class="rightalign">'.count($pages).'</td>'; 164 if (count($items['en'][$ns]) == 0) { 165 $R->doc .= '<td class="rightalign"> - </td>'; 166 } else { 167 $R->doc .= '<td class="rightalign">'.round(100*count($pages)/count($items['en'][$ns])).'%</td>'; 168 } 169 170 $R->doc .= '<td>'; 171 // outdated 172 $outdated = array(); 173 foreach ($pages as $id => $date) { 174 if ($date < $items['en'][$ns][$id]) { 175 $outdated[] = $id; 176 } 177 } 178 if (count($outdated) > 0) { 179 $itr = 0; 180 $R->doc .= '<b>Outdated:</b><br/>'; 181 do 182 $R->doc .= '<a href="'.wl($lc.':'.$outdated[$itr]).'">'.$outdated[$itr].'</a> '; 183 while (++$itr < $data['outdated'] && $itr < count($outdated)); 184 if ($itr < count($outdated)) { 185 $R->doc .= 'and '.(count($outdated)-$itr).' more.'; 186 } 187 $R->doc .= '<br/> '; 188 } 189 // rouge pages 190 if (count($items['en'][$ns]) > 0) { 191 $extra_pages = array_keys(array_diff_key($pages,$items['en'][$ns])); 192 $func = create_function('$id' , "return '<a href=\"'.wl('$lc:'.\$id).'\">'.\$id.'</a>';"); 193 if (count($extra_pages) > 0) { 194 $extra_pages = array_map($func, $extra_pages); 195 $R->doc .= '<b>Rogue pages:</b><br/> '.implode(', ', $extra_pages).'<br/>'; 196 } 197 } 198 199 $R->doc .= '</td>'; 200 $R->doc .= '</tr>'; 201 $pageTotal += count($pages); 202 } 203 $R->doc .= '<tr>'; 204 $R->doc .= '<th>'.$lc.'</th>'; 205 $R->doc .= '<th>Total</th>'; 206 $R->doc .= '<th class="rightalign">'.$pageTotal.'</th>'; 207 $R->doc .= '<th></th>'; 208 $R->doc .= '<th></th>'; 209 $R->doc .= '</tr>'; 210 } 211 $R->doc .= '</table>'; 212 } 213 214 function baseNS($id){ 215 $pos = strpos((string)$id,':'); 216 if($pos!==false){ 217 return substr((string)$id,0,$pos); 218 } 219 return false; 220 } 221 222 function skipBaseNS($id){ 223 $pos = strpos((string)$id,':'); 224 if($pos!==false){ 225 return substr((string)$id,$pos+1); 226 } 227 return false; 228 } 229} 230 231