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   = '&minus;';
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