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 ' . '</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 ' . '</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(' <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[]' => ' Use <b>Ctrl</b> or <b>Options</b> key to <br />multiple select from: <br /> <span id="current_ns"> ' 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