1<?php
2/**
3*
4* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
5* @author Myron Turner <mturner@cc.umanitoba.ca>
6*/
7
8if (!defined('DOKU_INC')) define('DOKU_INC', realpath(dirname(__FILE__) . '/../../../') . '/');
9if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/');
10if(!defined('HTMLOK_WIKI_PATH'))define ('HTMLOK_WIKI_PATH', DOKU_INC . 'data/pages/');
11if(!defined('DOKU_CONF')) define('DOKU_CONF',DOKU_INC.'conf/');
12define('AUTH_USERFILE', DOKU_CONF . 'users.auth.php');
13require_once(DOKU_PLUGIN . 'admin.php');
14
15/**
16* All DokuWiki plugins to extend the admin function
17* need to inherit from this class
18*/
19class admin_plugin_htmlOKay extends DokuWiki_Admin_Plugin
20{
21    var $output;
22    var $ajax_script = 'directory_scan-3.php';
23    var $path = HTMLOK_WIKI_PATH;
24    var $wiki_home = HTMLOK_WIKI_PATH;
25    var $directories = array();
26    var $global_conf;
27    var $plugin_name;
28    var $current_namespace;
29    var $users;
30    var $groups = array();
31    var $scrollbars = false;
32    var $namespace_descriptor = '_ROOT_';
33    var $saved_inf; // either data read from access file at startup or data saved from $_POST
34    var $error_msg;
35    var $user_entries = 0;
36    var $show_debug = false;
37
38     function __construct()
39    {
40        global $conf;
41        $this->plugin_name ='htmlOKay';
42        $this->current_namespace = rtrim($this->path, '/');
43
44        $this->_loadUserData();
45
46        $script = DOKU_PLUGIN . $this->plugin_name . '/' . $this->ajax_script;
47        $document_root = $_SERVER['DOCUMENT_ROOT'];
48        $url = preg_replace('/' . preg_quote($document_root, '/') . '/', "", $script);
49
50
51        $this->ajax_script = $url;
52        $this->global_conf = $conf;
53        $this->init();
54        $this->traverseDirTree($this->path, 'outputpath', 'outputpath');
55    }
56
57    /**
58    * return some info
59    */
60    function getInfo()
61    {
62        return array('author' => 'Myron Turner',
63            'email' => 'Myron_Turner@shaw.ca',
64            'date' => '2015-09-03',
65            'name' => 'HTML Access Manager',
66            'desc' => 'sets permissions for html write/edit access',
67            'url' => 'http://www.mturner.org/htmlaccess/',
68            );
69    }
70
71    /**
72    * return sort order for position in admin menu
73    */
74    function getMenuSort()
75    {
76        return 929;
77    }
78
79    function getMenuText($language) {
80         return 'HTML Access Manager';
81    }
82
83
84    /**
85    * handle user request
86    */
87    function handle()
88    {
89        if (!isset($_REQUEST['abs_path'])) // first time through
90        {
91            $this->namespace_file = preg_replace('/\:/', '#', $this->namespace_descriptor);
92            $data_file = DOKU_PLUGIN . $this->plugin_name . '/conf/access/' . $this->namespace_file;
93            $this->saved_inf = io_readFile($data_file, false); // 'false' returns uncleaned string for unserialize
94            if(isset($this->saved_inf)) {
95                $this->saved_inf = unserialize($this->saved_inf);
96            }
97
98            return;
99        }
100
101                             // Saving access files begins here
102        $this->filespecs = $_POST['filespecs'];
103        $this->request = $_REQUEST;
104        $this->output = $_POST;
105        $this->current_namespace = $_POST['abs_path'];
106        $this->namespace_descriptor = $this->directories[$this->current_namespace]['namespace'];
107
108        $this->namespace_file = preg_replace('/\:/', '#', $this->namespace_descriptor);
109
110        if (!isset($_POST['group']) && !isset($_POST['user']))
111        {
112            $this->error_msg = "HTML Permissions for " . $this->namespace_file ." have been removed.";
113
114        }
115        elseif ($this->filespecs[0] == 'none')
116        {
117            $this->error_msg = "Incomplete data:  No files selected";
118            return;
119        }
120
121        $data_file = DOKU_PLUGIN . $this->plugin_name . '/conf/access/' . $this->namespace_file;
122        $this->namespace_file = $data_file;
123
124        $inf = $this->get_output_array();
125        if(!$inf) $inf = array();
126        $this->saved_inf = $inf;
127        $inf = serialize($inf);
128        io_saveFile($data_file, $inf, false);
129
130
131    }
132
133    function get_output_array()
134    {
135        $new_inf = array();
136        $keys = array_keys($this->output);
137        if (!in_array('group', $keys) && !in_array('user', $keys)) return false;
138
139        $levels = array('none' => 0, 'strict' => 1, 'medium' => 2, 'lax' => 3, 'su' => 4);
140        $display = 4;
141        foreach($this->output as $item => $val)
142        {
143            if ($item != 'filespecs' && $item != 'group' && $item != 'user' && $item != 'abs_path') continue;
144
145            if ($item == 'group' || $item == 'user') // get lowest HTML permissions for display
146            {
147                foreach($val as $name => $level) // display is in effect when HTML viewed by non-editor
148                {
149                    if ($levels[$level] < $display)
150                    {
151                        $display = $levels[$level];
152                    }
153                }
154            }
155
156            $new_inf[$item] = $val;
157        }
158
159        $levels = array_keys($levels);
160        $new_inf['display'] = $levels[$display];
161        $new_inf['namespace'] = $this->namespace_file;
162        return $new_inf;
163    }
164
165    function init()
166    {
167        $this->wiki_home = rtrim($this->wiki_home, '/');
168        $this->directories[$this->wiki_home]['name'] = '_ROOT_';
169        $this->directories[$this->wiki_home]['namespace'] = '_ROOT_';
170        $this->wiki_home = ltrim($this->wiki_home, '/');
171        $this->wiki_home = preg_quote($this->wiki_home, '/');
172    }
173
174    /**
175    * Load all user data
176    *
177    * loads the user file into a datastructure
178    *
179    *    adapted from DokuWiki plain.class.php
180    */
181    function _loadUserData()
182    {
183        $this->users = array();
184
185        if (!@file_exists(AUTH_USERFILE)) return;
186
187        $lines = file(AUTH_USERFILE);
188        foreach($lines as $line)
189        {
190            $line = preg_replace('/#.*$/', '', $line); //ignore comments
191            $line = trim($line);
192            if (empty($line)) continue;
193
194            $row = explode(":", $line, 5);
195            $groups = explode(",", $row[4]);
196
197            $this->users[$row[0]]['name'] = urldecode($row[2]);
198            $this->users[$row[0]]['mail'] = $row[3];
199            $this->users[$row[0]]['grps'] = $groups;
200
201            foreach($groups as $grp)
202            {
203                $this->groups[$grp][] = $row[0];
204            }
205        }
206    }
207
208    /**
209    * Constructs namespace from directory path
210    *    Called by output_path when constructing $directories array
211    */
212    function get_namespace($path)
213    {
214        $namespace_string = trim($path, '/');
215        $namespace_string = preg_replace('/^' . $this->wiki_home . '/', "", $namespace_string);
216        $namespace_string = preg_replace('%/%', ':', $namespace_string);
217        return ltrim($namespace_string, ':');
218    }
219
220    /**
221    * Adapted from http://www.safalra.com/programming/php/directry-tree-traversal.php
222    */
223    function traverseDirTree($base, $fileFunc, $dirFunc = null, $afterDirFunc = null)
224    {
225       if(!is_readable ($base)) {
226           msg("$base is not readable (htmlOkay, line 225)",2);
227           return;
228        }
229        $subdirectories = opendir($base);
230        while (($subdirectory = readdir($subdirectories)) !== false)
231        {
232            $path = $base . $subdirectory;
233            if (is_file($path))
234            {
235                if ($fileFunc !== null) $this->$fileFunc($path);
236            }
237            else
238            {
239                if ($dirFunc !== null) $this->$dirFunc($path);
240                if (($subdirectory != '.') && ($subdirectory != '..'))
241                {
242                    $this->traverseDirTree($path . '/', $fileFunc, $dirFunc, $afterDirFunc);
243                }
244                if ($afterDirFunc !== null) $this->$afterDirFunc($path);
245            }
246        }
247        closedir($subdirectories);
248    }
249
250    function outputPath($path)
251    {
252        $name = basename($path);
253        if ($name == '.' || $name == '..') return;
254
255        if (is_dir($path))
256        {
257            $this->directories[$path] = array();
258            $this->directories[$path]['name'] = $name;
259            $this->directories[$path]['namespace'] = $this->get_namespace($path);
260            $this->directories[$path]['files'] = array();
261        } elseif (is_file($path))
262        {
263            $dir = dirname($path);
264            $this->directories[$dir]['files'][] = $name;
265        }
266    }
267
268    function get_directory_options()
269    {
270        $options = array();
271
272        foreach($this->directories as $dir => $info)
273        {
274            if (!isset($info['namespace'])) continue;
275            $selected = "";
276            if ($dir == $this->current_namespace)
277            {
278                $selected = 'SELECTED';
279            }
280            $options[] = "<option value=\"$dir\"  $selected>" . $info['namespace'] . '</option>' ;
281        }
282
283        return $options;
284    }
285
286    function get_group_options()
287    {
288        $options = array();
289
290        $groups = $this->groups;
291        foreach($groups as $group => $val)
292        {
293            list($checked_strict, $checked_medium, $checked_lax, $su) = $this->get_checked($group, $this->saved_inf['group']);
294
295            $options[] = "<td class='centeralign'><input type='radio' value='strict' $checked_strict name='group[$group]' />" . "<td class='centeralign'><input type='radio' value='medium' $checked_medium name='group[$group]' />" . "<td class='centeralign'><input type='radio' value='lax' $checked_lax name='group[$group]' />" . "<th><a href='javascript:show_this(\"group[$group]\");'>R</a></th>" . "<td>$group</td>";
296        }
297
298        return $options;
299    }
300
301    function get_file_options($dir)
302    {
303        $options = array();
304
305        $default_selected = true;
306        $options[] = '<option value="none" style="color:white; background-color:white;">' . 'No Files Selected&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ' . '</option>' ;
307        $options[] = '<option value="all">All</option>' ;
308        $files = $this->directories[$dir]['files'];
309        foreach($files as $file)
310        {
311            $selected = "";
312            if (isset($this->saved_inf['filespecs']))
313            {
314                if (in_array ($file, $this->saved_inf['filespecs']))
315                {
316                    $default_selected = false;
317                    $selected = "selected";
318                }
319            }
320            $options[] = "<option value='$file'  $selected>" . $file . '</option>' ;
321        }
322        if ($default_selected)
323        {
324            $options[0] = '<option value="none" SELECTED style="color:white; background-color:white;">' . 'No Files Selected&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ' . '</option>' ;
325        }
326        return $options;
327    }
328    // find previously checked radio buttons for form's user and group elements
329    function get_checked($name, $info_array)
330    {
331        $checked_strict = "";
332        $checked_medium = "";
333        $checked_lax = "";
334        $checked_su = "";
335
336        if (isset($info_array))
337        {
338            if (array_key_exists($name, $info_array))
339            {
340                switch ($info_array[$name])
341                {
342                    case 'strict':
343                        $checked_strict = 'checked';
344                        break;
345                    case 'medium':
346                        $checked_medium = 'checked';
347                        break;
348                    case 'lax':
349                        $checked_lax = 'checked';
350                    case 'su':
351                        $checked_su = 'checked';
352
353                        break;
354                }
355            }
356        }
357
358        return array($checked_strict, $checked_medium, $checked_lax, $checked_su);
359    }
360
361    function get_user_options()
362    {
363        $options = array();
364
365        foreach($this->users as $user => $user_array)
366        {
367            $groups = implode(',', $user_array['grps']);
368
369            list($checked_strict, $checked_medium, $checked_lax, $checked_su) = $this->get_checked($user, $this->saved_inf['user']);
370
371            $options[] = '<td class="centeralign">' . '<input type="radio" name="user[' . $user . ']" value="strict" ' . $checked_strict . ' /></td>' . '<td class="centeralign">' . '<input type="radio" name="user[' . $user . ']"  value= "medium" ' . $checked_medium . ' /></td>' . '<td class="centeralign">' . '<input type="radio" name="user[' . $user . ']"  value= "lax" ' . $checked_lax . ' /></td>' . '<td class="centeralign">' . '<input type="radio" name="user[' . $user . ']"  value= "su" ' . $checked_su . ' /></td>' . "<th><a href='javascript:show_this(\"user[$user]\");'>R</a></th>" . "\n<td>$user</td><td>" . $user_array['name'] . '</td><td>' . '<a href="mailto:' . $user_array['mail'] . '"  class="email">' . $user_array['mail'] . '</a></td><td>' . $groups . '</td>';
372        }
373
374        /* causes javascript to call scrollbars_htmlOKay() on loading
375           IE doesn't require scrollbars because overflow doesn't overwrite elements
376           beneath the users table but instead pushes them down the page
377         */
378        if (count($options) > 4 && !preg_match('/MSIE\s+\d+/', $_SERVER['HTTP_USER_AGENT']))
379        {
380            $this->scrollbars = true;
381            $this->user_entries = count($options);
382        }
383
384        return $options;
385    }
386
387    /**
388    * Output Javascript for html file
389    */
390    function print_scripts($url)
391    {
392        $path = HTMLOK_WIKI_PATH;
393        $url = '/' . ltrim($url, '/');
394
395        echo <<<SCRIPTS
396
397    <script language="javascript"><!--//--><![CDATA[//><!--
398    //--><!]]></script>
399
400SCRIPTS;
401    }
402
403    /**
404    * output appropriate html
405    */
406    function html()
407    {
408        global $ID;
409
410        $this->print_scripts($this->ajax_script);
411
412        echo "<div id='htmlOK_div' style='width:100%'>\n";
413        $this->debug(false,false);
414        ptln('<CENTER><H1>Embedded HTML Access Manager</H1></CENTER>');
415        if ($this->error_msg)
416        {
417            print "<center><h4>$this->error_msg</h4></center>";
418        }
419        ptln('<div style="width: 85%;margin: 0; margin: auto">');
420
421        ptln('<TABLE align="center" width="80%"><TR><TD>');
422       echo $this->locale_xhtml('selection');
423        ptln("\n</TABLE>\n");
424
425
426        /* Start Form */
427        ptln("\n" . '<form action="' . wl($ID) . '" method="POST" name="nsdata"' . ' >');
428        ptln('<input type="hidden" name="do"   value="admin" />' . "\n"
429             . '<input type="hidden" name="page" value="' . $this->plugin_name . '"  />');
430
431        /*  Namespace Table */
432
433        ptln('<table cellpadding="8"  class="inline">');
434
435        $this->write_SELECT('Namespace', 'abs_path', 'get_directory_options', "");
436        $this->write_SELECT('Files', 'filespecs[]',
437            'get_file_options', rtrim($this->current_namespace, '/'),
438            'multiple size="3" ', 'results'
439            );
440
441        echo "</table>\n";
442
443        /* Buttons */
444        ptln('<div  class="bar" style="width:30%; margin: 0 auto;">');
445        ptln('<INPUT TYPE="SUBMIT" class="button"  VALUE="Save" />');
446        ptln('<INPUT TYPE="BUTTON" class="button"  VALUE="Reset"  onclick="reset_htmlOKay(window.document[\'nsdata\']);" />');
447        ptln('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<INPUT TYPE="BUTTON" class="button" id = "htmlOK_scrollbutton" VALUE="Scroll" onclick="scrollbars_htmlOKay();" />');
448        ptln('</div>');
449
450        ptln('<div id="htmlOK_user_table"><table cellpadding="5" cellspacing="16" class="htmlOK_data" border=0>'); // Start outer table
451        /* Groups Table */
452
453        ptln('<tr><td valign="top"><table cellpadding="8" class="inline">');
454        ptln('<TR><TH colspan="4">Policy</TH><TH rowspan="2">Groups</TH>');
455        ptln('<TR><TH>H</TH><TH>M</TH><TH>L</TH><TH>Reset</TH></tr>');
456        $options = $this->get_group_options();
457        foreach($options as $option)
458        {
459            ptln("<TR>$option");
460        }
461        ptln('</table>'); // End Groups table
462
463        /* Users Table */
464        ptln('<td><table cellpadding="8"  class="inline">' . '<tr><th colspan="5">Policy</th><th rowspan="2">User</th><th rowspan="2">Real Name</th>' . ' <th rowspan="2">Email</th><th rowspan="2">Groups</th>');
465        ptln('<TR><TH>H</TH><TH>M</TH><TH>L</TH><TH>U</TH><TH>Reset</TH>');
466
467        $options = $this->get_user_options();
468        foreach($options as $option)
469        {
470            ptln('<tr class="user_info">' . $option);
471        }
472        ptln('</table>'); // End users table
473
474        /* Close Table, close Form */
475        ptln("\n</TABLE></div>\n");
476        ptln("</form><br />\n");
477
478        echo "</div></div>\n"; // close htmlOK_div
479
480        if ($this->scrollbars)
481        {
482            ptln('<script language="javascript"> user_table_size_htmlOKay(' . $this->user_entries . '); </script>');
483        }
484    }
485
486    /**
487    * $th:     heading
488    * $name:   name of the Select
489    * $options_func:  the name of the options function from which to get optons and values
490    * $param:  optional parameter to be passed in to options_func
491    * $select_type:  optional multiple and size
492    * $id: for file options that are being replaced vi the AJAX call
493    */
494    function write_SELECT($th, $name, $options_func, $param = "", $select_type = "", $id = "")
495    {
496        $button_fields = array('abs_path' => '<INPUT TYPE ="BUTTON"  class="button"  value = "Select" onclick="getNSdata_htmlOKay(window.document[\'nsdata\']);" />',
497            'filespecs[]' => '&nbsp;Use <b>Ctrl</b> or <b>Options</b> key to <br />multiple select from:&nbsp;<br />&nbsp;<span id="current_ns">&nbsp;'
498             . $this->directories[$this->current_namespace]['namespace'] . '</span>',
499            'groupspecs[]' => "Button"
500            );
501
502        if ($id)
503        {
504            $id = ' id="' . $id . '" ';
505        }
506        ptln('<TR><th valign="middle" class="leftalign">' . "\n$th\n" . '<td valign="middle" class="centeralign">');
507        ptln('<SELECT name="' . $name . '" class="edit" ' . $select_type . $id . '>');
508
509        $options = $this->$options_func($param);
510        foreach($options as $option)
511        {
512            ptln($option);
513        }
514
515        $class = "";
516        if ($name == 'abs_path')
517        {
518            $class = 'class = "bar" ';
519        }
520
521        ptln('</SELECT> <TD colspan="2" valign="middle" align="center"' . $class . '>');
522        ptln($button_fields[$name] . '</td>');
523    }
524
525    function debug($users = false, $groups = false)
526    {
527        if(!$this->show_debug) return;
528
529        global $INFO;
530	    global $conf;
531
532        echo '<pre>';
533        echo "<h4>\$INFO</h4>";
534        print_r($INFO);
535        echo "<h4>\$conf</h4>";
536        print_r($conf);
537        echo "<h4>Saved inf:</h4>";
538        print_r($this->saved_inf);
539        echo "<h4>request:</h4>";
540        print_r($this->request);
541
542        echo "<b>File:</b> $this->namespace_file \n";
543
544        if($groups) {
545            echo "<h4>Group(s)</h4>";
546            print_r ($this->groups);
547        }
548
549        if($users) {
550            echo "<h4>User(s):</h4>";
551            print_r ($this->users);
552        }
553
554
555
556        echo "Output: <br>";print_r($this->output);
557        echo "Script: " .$this->ajax_script . " <--> Path:  $this->ajax_path_temp\n";
558        echo "</pre>";
559    }
560}
561
562?>
563