1<?php 2/** 3 * DokuWiki Plugin dokutranslate (Admin Component) 4 * Based on built-in ACL plugin 5 * 6 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 7 * @author Martin Doucha <next_ghost@quick.cz> 8 * @author Andreas Gohr <andi@splitbrain.org> 9 * @author Anika Henke <anika@selfthinker.org> (concepts) 10 * @author Frank Schubert <frank@schokilade.de> (old version) 11 */ 12// must be run within Dokuwiki 13if(!defined('DOKU_INC')) die(); 14 15if (!defined('DOKU_LF')) define('DOKU_LF', "\n"); 16if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t"); 17if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 18 19require_once DOKU_PLUGIN.'admin.php'; 20require_once 'utils.php'; 21 22/** 23 * All DokuWiki plugins to extend the admin function 24 * need to inherit from this class 25 */ 26class admin_plugin_dokutranslate extends DokuWiki_Admin_Plugin { 27 var $acl = null; 28 var $ns = null; 29 30 /** 31 * return prompt for admin menu 32 */ 33 function getMenuText($language) { 34 return $this->getLang('admin_dokutranslate'); 35 } 36 37 /** 38 * return sort order for position in admin menu 39 */ 40 function getMenuSort() { 41 return 1; 42 } 43 44 /** 45 * handle user request 46 * 47 * Initializes internal vars and handles modifications 48 * 49 * @author Andreas Gohr <andi@splitbrain.org> 50 */ 51 function handle() { 52 global $AUTH_ACL; 53 global $ID; 54 global $auth; 55 56 // fresh 1:1 copy without replacements 57 $AUTH_ACL = loadModlist(); 58 59 // namespace given? 60 if(empty($_REQUEST['ns']) || $_REQUEST['ns'] == '*'){ 61 $this->ns = '*'; 62 } else { 63 $this->ns = cleanID($_REQUEST['ns']); 64 } 65 66 // handle modifications 67 if (isset($_REQUEST['cmd']) && checkSecurityToken()) { 68 // scope for modifications 69 if($this->ns == '*'){ 70 $scope = '*'; 71 }else{ 72 $scope = $this->ns.':*'; 73 } 74 75 if (isset($_REQUEST['cmd']['save']) && $scope && isset($_REQUEST['modgroup'])) { 76 // handle additions or single modifications 77 $this->_acl_del($scope); 78 $this->_acl_add($scope, trim($_REQUEST['modgroup'])); 79 } elseif (isset($_REQUEST['cmd']['del']) && $scope) { 80 // handle single deletions 81 $this->_acl_del($scope); 82 } elseif (isset($_REQUEST['cmd']['update'])) { 83 // handle update of the whole file 84 foreach((array) $_REQUEST['del'] as $where){ 85 // remove all rules marked for deletion 86 unset($_REQUEST['acl'][$where]); 87 } 88 89 // prepare lines 90 $lines = array(); 91 92 // keep header 93 foreach($AUTH_ACL as $line){ 94 if($line{0} == '#'){ 95 $lines[] = $line; 96 }else{ 97 break; 98 } 99 } 100 101 foreach((array) $_REQUEST['acl'] as $where => $who){ 102 $who = $auth->cleanGroup($who); 103 $who = auth_nameencode($who,true); 104 $lines[] = "$where\t$who\n"; 105 } 106 107 // save it 108 io_saveFile(DOKUTRANSLATE_MODLIST, join('',$lines)); 109 } 110 111 // reload ACL config 112 $AUTH_ACL = loadModlist(); 113 } 114 115 // initialize ACL array 116 $this->_init_acl_config(); 117 } 118 119 /** 120 * ACL Output function 121 * 122 * print a table with all significant permissions for the 123 * current id 124 * 125 * @author Frank Schubert <frank@schokilade.de> 126 * @author Andreas Gohr <andi@splitbrain.org> 127 */ 128 function html() { 129 global $ID; 130 131 echo '<div id="dokutranslate_manager">'.NL; 132 echo '<h1>'.$this->getLang('admin_dokutranslate').'</h1>'.NL; 133 echo '<div class="level1">'.NL; 134 135 echo '<div id="dokutranslate__tree">'.NL; 136 $this->_html_explorer(); 137 echo '</div>'.NL; 138 139 echo '<div id="dokutranslate__detail">'.NL; 140 $this->_html_detail(); 141 echo '</div>'.NL; 142 echo '</div>'.NL; 143 144 echo '<div class="clearer"></div>'; 145 echo '<h2>'.$this->getLang('current').'</h2>'.NL; 146 echo '<div class="level2">'.NL; 147 $this->_html_table(); 148 echo '</div>'.NL; 149 150 echo '</div>'.NL; 151 } 152 153 /** 154 * returns array with set options for building links 155 * 156 * @author Andreas Gohr <andi@splitbrain.org> 157 */ 158 function _get_opts($addopts=null){ 159 global $ID; 160 $opts = array( 161 'do'=>'admin', 162 'page'=>'dokutranslate', 163 ); 164 if($this->ns) $opts['ns'] = $this->ns; 165 166 if(is_null($addopts)) return $opts; 167 return array_merge($opts, $addopts); 168 } 169 170 /** 171 * Display a tree menu to select a page or namespace 172 * 173 * @author Andreas Gohr <andi@splitbrain.org> 174 */ 175 function _html_explorer(){ 176 global $conf; 177 global $ID; 178 global $lang; 179 180 $dir = $conf['datadir']; 181 $ns = $this->ns; 182 if(empty($ns)){ 183 $ns = dirname(str_replace(':','/',$ID)); 184 if($ns == '.') $ns =''; 185 }elseif($ns == '*'){ 186 $ns =''; 187 } 188 $ns = utf8_encodeFN(str_replace(':','/',$ns)); 189 190 $data = $this->_get_tree($ns); 191 192 // wrap a list with the root level around the other namespaces 193 array_unshift($data, array( 'level' => 0, 'id' => '*', 'type' => 'd', 194 'open' =>'true', 'label' => '['.$lang['mediaroot'].']')); 195 196 echo html_buildlist($data,'dokutranslate', 197 array($this,'_html_list_acl'), 198 array($this,'_html_li_acl')); 199 200 } 201 202 /** 203 * get a combined list of media and page files 204 * 205 * @param string $folder an already converted filesystem folder of the current namespace 206 * @param string $limit limit the search to this folder 207 */ 208 function _get_tree($folder,$limit=''){ 209 global $conf; 210 211 // read tree structure from pages and media 212 $data = array(); 213 search($data,$conf['datadir'],'search_index',array('ns' => $folder),$limit); 214 215 # Filter out pages, leave only namespaces 216 $count = count($data); 217 218 for ($i = 0; $i < $count; $i++) { 219 if ($data[$i]['type'] != 'd') { 220 unset($data[$i]); 221 } 222 } 223 224 return $data; 225 } 226 227 /** 228 * Display the current ACL for selected where/who combination with 229 * selectors and modification form 230 * 231 * @author Andreas Gohr <andi@splitbrain.org> 232 */ 233 function _html_detail(){ 234 echo '<form action="'.wl().'" method="post" accept-charset="utf-8"><div class="no">'.NL; 235 236 echo '<div id="dokutranslate__user">'; 237 $this->_html_modform(); 238 echo '</div>'.NL; 239 240 echo '<input type="hidden" name="ns" value="'.hsc($this->ns).'" />'.NL; 241 echo '<input type="hidden" name="do" value="admin" />'.NL; 242 echo '<input type="hidden" name="page" value="dokutranslate" />'.NL; 243 echo '<input type="hidden" name="sectok" value="'.getSecurityToken().'" />'.NL; 244 echo '</div></form>'.NL; 245 } 246 247 function _html_modform() { 248 global $lang; 249 250 $ns = $this->ns . ($this->ns == '*' ? '' : ':*'); 251 252 echo $this->getLang('modgroup').' '; 253 echo '<input type="text" name="modgroup" class="edit" value="'.hsc(isset($this->acl[$ns]) ? $this->acl[$ns] : '').'" />'.NL; 254 255 if(!isset($this->acl[$ns])){ 256 echo '<input type="submit" name="cmd[save]" class="button" value="'.$lang['btn_save'].'" />'.NL; 257 }else{ 258 echo '<input type="submit" name="cmd[save]" class="button" value="'.$lang['btn_update'].'" />'.NL; 259 echo '<input type="submit" name="cmd[del]" class="button" value="'.$lang['btn_delete'].'" />'.NL; 260 } 261 } 262 263 /** 264 * Item formatter for the tree view 265 * 266 * User function for html_buildlist() 267 * 268 * @author Andreas Gohr <andi@splitbrain.org> 269 */ 270 function _html_list_acl($item){ 271 global $ID; 272 $ret = ''; 273 // what to display 274 if($item['label']){ 275 $base = $item['label']; 276 }else{ 277 $base = ':'.$item['id']; 278 $base = substr($base,strrpos($base,':')+1); 279 } 280 281 // highlight? 282 if ($item['id'] == $this->ns) { 283 $cl = ' cur'; 284 } 285 286 // namespace or page? 287 if($item['open']){ 288 $img = DOKU_BASE.'lib/images/minus.gif'; 289 $alt = '−'; 290 }else{ 291 $img = DOKU_BASE.'lib/images/plus.gif'; 292 $alt = '+'; 293 } 294 $ret .= '<img src="'.$img.'" alt="'.$alt.'" />'; 295 $ret .= '<a href="'.wl('',$this->_get_opts(array('ns'=>$item['id'],'sectok'=>getSecurityToken()))).'" class="idx_dir'.$cl.'">'; 296 $ret .= $base; 297 $ret .= '</a>'; 298 299 return $ret; 300 } 301 302 303 function _html_li_acl($item){ 304 return '<li class="level' . $item['level'] . ' ' . 305 ($item['open'] ? 'open' : 'closed') . '">'; 306 } 307 308 309 /** 310 * Get current ACL settings as multidim array 311 * 312 * @author Andreas Gohr <andi@splitbrain.org> 313 */ 314 function _init_acl_config(){ 315 global $AUTH_ACL; 316 317 $this->acl = parseModlist($AUTH_ACL); 318 } 319 320 /** 321 * Display all currently set permissions in a table 322 * 323 * @author Andreas Gohr <andi@splitbrain.org> 324 */ 325 function _html_table(){ 326 global $lang; 327 global $ID; 328 329 echo '<form action="'.wl().'" method="post" accept-charset="utf-8"><div class="no">'.NL; 330 331 if($this->ns){ 332 echo '<input type="hidden" name="ns" value="'.hsc($this->ns).'" />'.NL; 333 } 334 335 echo '<input type="hidden" name="do" value="admin" />'.NL; 336 echo '<input type="hidden" name="page" value="dokutranslate" />'.NL; 337 echo '<input type="hidden" name="sectok" value="'.getSecurityToken().'" />'.NL; 338 echo '<table class="inline">'; 339 echo '<tr>'; 340 echo '<th>'.$this->getLang('where').'</th>'; 341 echo '<th>'.$this->getLang('who').'</th>'; 342 echo '<th>'.$lang['btn_delete'].'</th>'; 343 echo '</tr>'; 344 345 foreach($this->acl as $where => $who){ 346 echo '<tr>'; 347 echo '<td>'; 348 echo '<span class="dokutranslatens">'.hsc($where).'</span>'; 349 echo '</td>'; 350 351 echo '<td>'; 352 echo '<span class="dokutranslategroup">'.hsc($who).'</span>'; 353 echo '</td>'; 354 355 echo '<td align="center">'; 356 echo '<input type="hidden" name="acl['.hsc($where).']" value="'.hsc($who).'" />'; 357 echo '<input type="checkbox" name="del[]" value="'.hsc($where).'" />'; 358 echo '</td>'; 359 echo '</tr>'; 360 } 361 362 echo '<tr>'; 363 echo '<th align="right" colspan="3">'; 364 echo '<input type="submit" value="'.$this->getLang('delsel').'" name="cmd[update]" class="button" />'; 365 echo '</th>'; 366 echo '</tr>'; 367 echo '</table>'; 368 echo '</div></form>'.NL; 369 } 370 371 /** 372 * adds new acl-entry to conf/acl.auth.php 373 * 374 * @author Frank Schubert <frank@schokilade.de> 375 */ 376 function _acl_add($acl_scope, $acl_user){ 377 $acl_config = loadModlist(); 378 $acl_user = auth_nameencode($acl_user,true); 379 380 $new_acl = "$acl_scope\t$acl_user\n"; 381 $acl_config[] = $new_acl; 382 383 return io_saveFile(DOKUTRANSLATE_MODLIST, join('',$acl_config)); 384 } 385 386 /** 387 * remove acl-entry from conf/acl.auth.php 388 * 389 * @author Frank Schubert <frank@schokilade.de> 390 */ 391 function _acl_del($acl_scope){ 392 $acl_config = loadModlist(); 393 $acl_pattern = '^'.preg_quote($acl_scope,'/').'\s+.*$'; 394 395 // save all non!-matching 396 $new_config = preg_grep("/$acl_pattern/", $acl_config, PREG_GREP_INVERT); 397 398 return io_saveFile(DOKUTRANSLATE_MODLIST, join('',$new_config)); 399 } 400} 401