1<?php 2/** 3 * DokuWiki Plugin groupmanager (Syntax Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author Harald Ronge <harald@turtur.nl> 7 * @original author Alex Forencich <alex@alexforencich.com> 8 * 9 * Syntax: 10 * ~~groupmanager|[groups to manage]|[allowed users and groups]~~ 11 * 12 * Examples: 13 * ~~groupmanager|posters|@moderators~~ 14 * Members of group 'posters' can be managed by group 'moderators' 15 * 16 * ~~groupmanager|groupa, groupb|joe, @admin~~ 17 * Members of groups 'groupa' and 'groupb' can be managed by user 'joe' 18 * members of the 'admin' group 19 * 20 * Note: superuser groups can only be managed by super users, 21 * forbidden groups can be configured, 22 * and users cannot remove themselves from the group that lets them access 23 * the group manager (including admins) 24 * 25 * Note: if require_conf_namespace config option is set, then plugin looks in 26 * conf_namespace:$ID for configuration. Plugin will also check config 27 * namespace if a placeholder tag is used (~~groupmanager~~). This is the 28 * default configuration for security reasons. 29 * 30 */ 31 32// must be run within Dokuwiki 33if (!defined('DOKU_INC')) die(); 34 35if (!defined('DOKU_LF')) define('DOKU_LF', "\n"); 36if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t"); 37if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); 38if (!defined('GROUPMANAGER_IMAGES')) define('GROUPMANAGER_IMAGES', DOKU_BASE . 'lib/plugins/groupmanager/images/'); 39 40require_once DOKU_PLUGIN . 'syntax.php'; 41 42function remove_item_by_value($val, $arr, $preserve = true) 43{ 44 if (empty($arr) || !is_array($arr)) { 45 return false; 46 } 47 foreach (array_keys($arr, $val) as $key) { 48 unset($arr[$key]); 49 } 50 return ($preserve) ? $arr : array_values($arr); 51} 52 53 54class syntax_plugin_groupmanager extends DokuWiki_Syntax_Plugin 55{ 56 /** 57 * Plugin information 58 */ 59 var $_auth = null; // auth object 60 var $_user_total = 0; // number of registered users 61 var $_filter = array(); // user selection filter(s) 62 var $_start = 0; // index of first user to be displayed 63 var $_last = 0; // index of the last user to be displayed 64 var $_pagesize = 20; // number of users to list on one page 65 var $DefaultGroup = ''; 66 var $grplst = array(); 67 var $userlist = array(); 68 69 /** 70 * Constructor 71 */ 72 function syntax_plugin_groupmanager() 73 { 74 global $auth; 75 76 $this->setupLocale(); 77 78 if (!isset($auth)) { 79 $this->disabled = $this->lang['noauth']; 80 } else if (!$auth->canDo('getUsers')) { 81 $this->disabled = $this->lang['nosupport']; 82 } else { 83 84 // we're good to go 85 $this->_auth = & $auth; 86 87 } 88 } 89 90 function getInfo() 91 { 92 return array( 93 'author' => 'Harald Ronge', 94 'email' => 'harald@turtur.nl', 95 'date' => '2013-05-26', 96 'name' => 'Group Manager Syntax plugin', 97 'desc' => 'Embeddable group manager, based on groupmgr from Alex Forencich and usermanager from Christopher Smith', 98 'url' => 'http://www.dokuwiki.org/plugin:groupmanager/', 99 'original author' => 'Alex Forencich', 100 'original email' => 'alex@alexforencich.com' 101 ); 102 } 103 104 /** 105 * Plugin type 106 */ 107 function getType() 108 { 109 return 'substition'; 110 } 111 112 /** 113 * PType 114 */ 115 function getPType() 116 { 117 return 'normal'; 118 } 119 120 /** 121 * Sort order 122 */ 123 function getSort() 124 { 125 return 160; 126 } 127 128 /** 129 * Register syntax handler 130 */ 131 function connectTo($mode) 132 { 133 $this->Lexer->addSpecialPattern('~~groupmanager\|[^~]*?~~', $mode, 'plugin_groupmanager'); 134 $this->Lexer->addSpecialPattern('~~groupmanager~~', $mode, 'plugin_groupmanager'); 135 } 136 137 /** 138 * Handle match 139 */ 140// is called without config, but do not know by whom, possibly with literal match 141 function handle($match, $state, $pos, Doku_Handler $handler) 142 { 143// groupmanager only 144 $data = array(null, $state, $pos); 145 146 if (strlen($match) == 16) 147 return $data; 148 149 // Strip away tag 150 $match = substr($match, 15, -2); 151 152 // split arguments 153 $ar = explode("|", $match); 154 155 $match = array(); 156 157 // reorganize into array 158 foreach ($ar as $it) { 159 $ar2 = explode(",", $it); 160 foreach ($ar2 as &$it2) 161 $it2 = trim($it2); 162 $match[] = $ar2; 163 } 164 165 // pass to render method 166 $data[0] = $match; 167 168 return $data; 169 } 170 171 /** 172 * Render it 173 */ 174 function render($mode, Doku_Renderer $renderer, $data) 175 { 176 // start usermanager 177 global $auth; 178 global $lang; 179 global $INFO; 180 global $conf; 181 global $ID; 182 183 // start groupmanager 184 185 if ($mode == 'xhtml') { 186 187 //TurTur, if submit and security token does not match stop anyway 188 if(isset($_REQUEST['fn']) && !checkSecurityToken()) return false; 189 190 // need config namespace? 191 $allow_add_user = $conf_namespace = $this->getConf('allow_add_user'); 192 $allow_delete_user = $conf_namespace = $this->getConf('allow_delete_user'); 193 $conf_namespace = $this->getConf('conf_namespace'); 194 195 if ($this->getConf('require_conf_namespace')) { 196 if (!$conf_namespace) return false; 197 else $data[0] = null; // set it to null, it will be reloaded anyway 198 } 199 200 // empty tag? 201 if (is_null($data[0]) || count($data[0]) == 0) { 202 // load from conf namespace 203 // build page name 204 $conf_page = ""; 205 if (substr($ID, 0, strlen($conf_namespace)) != $conf_namespace) { 206 $conf_page .= $conf_namespace; 207 if (substr($conf_page, -1) != ':') $conf_page .= ":"; 208 } 209 $conf_page .= $ID; 210 211 // get file name 212 $fn = wikiFN($conf_page); 213 214 if (!file_exists($fn)) 215 return false; 216 217 // read file 218 $page = file_get_contents($fn); 219 220 // find config tag 221 222 $i = preg_match('/~~groupmanager\|[^~]*?~~/', $page, $match); 223 224 if ($i == 0) 225 return false; 226 227 // parse config 228 $match = substr($match[0], 15, -2); 229 230 $ar = explode("|", $match); 231 $match = array(); 232 233 // reorganize into array 234 foreach ($ar as $it) { 235 $ar2 = explode(",", $it); 236 foreach ($ar2 as &$it2) 237 $it2 = trim($it2); 238 $match[] = $ar2; 239 } 240 241 // pass to render method 242 $data[0] = $match; 243 } 244 245 // don't render if an argument hasn't been specified 246 if (!isset($data[0][0]) || !isset($data[0][1])) 247 return false; 248 249 $this->grplst = $data[0][0]; 250 $authlst = $data[0][1]; 251 252 // parse forbidden groups 253 $forbiddengrplst = array(); 254 $str = $this->getConf('forbidden_groups'); 255 if (isset($str)) { 256 $arr = explode(",", $str); 257 foreach ($arr as $val) { 258 $val = trim($val); 259 $forbiddengrplst[] = $val; 260 } 261 } 262 263 // parse admin groups 264 $admingrplst = array(); 265 if (isset($conf['superuser'])) { 266 $arr = explode(",", $conf['superuser']); 267 foreach ($arr as $val) { 268 $val = trim($val); 269 if ($val[0] == "@") { 270 $val = substr($val, 1); 271 $admingrplst[] = $val; 272 } 273 } 274 } 275 276 // forbid admin groups if user is not a superuser 277 if (!$INFO['isadmin']) { 278 foreach ($admingrplst as $val) { 279 $forbiddengrplst[] = $val; 280 } 281 } 282 283 // remove forbidden groups from group list 284 foreach ($forbiddengrplst as $val) { 285 $this->grplst = remove_item_by_value($val, $this->grplst, false); 286 } 287 if (count($this->grplst) > 0) $this->DefaultGroup = $this->grplst[0]; 288 289 // build array of user's credentials 290 $check = array($_SERVER['REMOTE_USER']); 291 if (is_array($INFO['userinfo'])) { 292 foreach ($INFO['userinfo']['grps'] as $val) { 293 $check[] = "@" . $val; 294 } 295 } 296 297 // does user have permission? 298 // Also, save authenticated group for later 299 $authbygrp = ""; 300 $ok = 0; 301 foreach ($authlst as $val) { 302 if (in_array($val, $check)) { 303 $ok = 1; 304 if ($val[0] == "@") { 305 $authbygrp = substr($val, 1); 306 } 307 } 308 } 309 310 // continue if user has explicit permission or is an admin 311 if ($INFO['isadmin'] || $ok) { 312 // authorized 313 $status = 0; 314 315 // Begin inserted from usermanager 316 317 if (is_null($this->_auth)) return false; 318 319 // extract the command and any specific parameters 320 // submit button name is of the form - fn[cmd][param(s)] 321 322 $fn = $_REQUEST['fn']; 323 324 if (is_array($fn)) { 325 $cmd = key($fn); 326 $param = is_array($fn[$cmd]) ? key($fn[$cmd]) : null; 327 } else { 328 $cmd = $fn; 329 $param = null; 330 } 331 332 if ($cmd != "search") { 333 if (!empty($_REQUEST['start'])) { 334 $this->_start = $_REQUEST['start']; 335 } 336 $this->_filter = $this->_retrieveFilter(); 337 $this->_setFilter("new"); 338 } 339 340 switch ($cmd) { 341 case "add" : 342 if ($allow_add_user) { 343 $this->_addUser(); 344 } else msg($this->lang['add_without_form'], -1); 345 break; 346 case "update" : 347 $this->_deleteUser(); 348 break; 349 /* 350 //case "add" : if ($allow_add_user) {$this->_addUser()} else msg('Trying to add user without form!',-1); break;/* 351 case "modify" : $this->_modifyUser(); break; 352 case "edit" : $this->_editUser($param); break; 353 */ 354 case "search" : 355 $this->_setFilter($param); 356 $this->_start = 0; 357 break; 358 } 359 /* 360 else { 361 $this->_setFilter($param); 362 $this->_start = 0; 363 } 364 */ 365 366 367 $this->_user_total = $this->_auth->canDo('getUserCount') ? $this->_auth->getUserCount($this->_filter) : -1; 368 369 // page handling 370 switch ($cmd) { 371 case 'start' : 372 $this->_start = 0; 373 break; 374 // case 'update' : $this->_start = $this->_start; break; //do nothing 375 case 'prev' : 376 $this->_start -= $this->_pagesize; 377 break; 378 case 'next' : 379 $this->_start += $this->_pagesize; 380 break; 381 case 'last' : 382 $this->_start = $this->_user_total; 383 break; 384 } 385 $this->_validatePagination(); 386 387 // we are parsing a submitted comment... 388 if (isset($_REQUEST['comment'])) 389 return false; 390 391 // disable caching 392 $renderer->info['cache'] = false; 393 394 if (!method_exists($auth, "retrieveUsers")) return false; 395 396 if (is_null($this->_auth)) { 397 print $this->lang['badauth']; 398 return false; 399 } 400 401 // watch out: plain authentication takes limit = 0 for get all users 402 // MySQL will take 0 to retrieve none (makes sense), so the code below did not work 403 // $users = $auth->retrieveUsers(0, 10000, array()); 404 $this->userlist = $this->_auth->retrieveUsers($this->_start, $this->_pagesize, $this->_filter); 405 $page_buttons = $this->_pagination(); 406 $colspan = 4 + count($this->grplst) + ($allow_delete_user?0:-1) ; 407 // open form 408 $renderer->doc .= "<form method=\"post\" action=\"" . htmlspecialchars($_SERVER['REQUEST_URI']) 409 . "\" name=\"groupmanager\" enctype=\"application/x-www-form-urlencoded\">"; 410 //TurTur 411 $renderer->doc .= formSecurityToken(false); 412 413 // open table and print header 414 if ($this->_user_total > 0) { 415 $renderer->doc .= "<p>" . sprintf($this->lang['summary'], $this->_start + 1, $this->_last, $this->_user_total, $this->_auth->getUserCount()) . "</p>"; 416 } else { 417 $renderer->doc .= "<p>" . sprintf($this->lang['nonefound'], $this->_auth->getUserCount()) . "</p>"; 418 } 419 420 $renderer->doc .= "<table class=\"inline\" >\n"; //width=\"95%\" style=\"max-width: 500px; overflow:scroll;\">\n"; 421 // $renderer->doc .= " <tbody>\n"; 422 $renderer->doc .= " <tbody>"; 423 424 $renderer->doc .= " <tr><td colspan=\"" . $colspan . "\" class=\"centeralign\" STYLE='border-bottom: 3px solid #ccc'>"; 425 $renderer->doc .= " <span class=\"medialeft\" >"; 426 $renderer->doc .= " <input type=\"image\" src=\"" . GROUPMANAGER_IMAGES . "search.png\" onmouseover=\"this.src='" . GROUPMANAGER_IMAGES . "search_hilite.png'\" onmouseout=\"this.src='" . GROUPMANAGER_IMAGES . "search.png'\" STYLE=\"float: left; padding-right: 5px;\" name=\"fn[search][new]\" title=\"" . $this->lang['search_prompt'] . "\" alt=\"" . $this->lang['search'] . "\" />"; 427 $renderer->doc .= " <input type=\"image\" src=\"" . GROUPMANAGER_IMAGES . "workgroup.png\" onmouseover=\"this.src='" . GROUPMANAGER_IMAGES . "workgroup_hilite.png'\" onmouseout=\"this.src='" . GROUPMANAGER_IMAGES . "workgroup.png'\" STYLE=\"float: left; padding-right: 5px;\" name=\"fn[search][show_default]\" title=\"" . $this->lang['search_default_group'] . "\" alt=\"" . $this->lang['search_default_group'] . "\" />"; 428 $renderer->doc .= " <input type=\"image\" src=\"" . GROUPMANAGER_IMAGES . "everybody.png\" onmouseover=\"this.src='" . GROUPMANAGER_IMAGES . "everybody_hilite.png'\" onmouseout=\"this.src='" . GROUPMANAGER_IMAGES . "everybody.png'\" STYLE=\"float: left; padding-right: 5px;\" name=\"fn[search][clear]\" title=\"" . $this->lang['clear'] . "\" alt=\"" . $this->lang['clear'] . "\" />"; 429 $renderer->doc .= " </span>"; 430 $renderer->doc .= " <span class=\"mediaright\">"; 431 $renderer->doc .= " <input type=\"submit\" name=\"fn[start]\" " . $page_buttons['start'] . " class=\"button\" value=\"" . $this->lang['start'] . "\" />"; 432 $renderer->doc .= " <input type=\"submit\" name=\"fn[prev]\" " . $page_buttons['prev'] . " class=\"button\" value=\"" . $this->lang['prev'] . "\" />"; 433 $renderer->doc .= " <input type=\"submit\" name=\"fn[next]\" " . $page_buttons['next'] . " class=\"button\" value=\"" . $this->lang['next'] . "\" />"; 434 $renderer->doc .= " <input type=\"submit\" name=\"fn[last]\" " . $page_buttons['last'] . " class=\"button\" value=\"" . $this->lang['last'] . "\" />"; 435 436 $renderer->doc .= " </span>"; 437 $renderer->doc .= " </td></tr>"; 438 $renderer->doc .= " <tr>"; 439 //if delete column is hidden, Filter-header is part of the same column 440 if ($allow_delete_user) {$renderer->doc .= " <td STYLE='border-bottom: 3px solid #ccc; color: #FF9900'> Filter:</td>";} 441 $renderer->doc .= " <td class=\"centeralign\" style=\"border-bottom: 3px solid #ccc\"><input type=\"text\" style=\"width:90%; color: #FF9900';\" name=\"userid\" class=\"edit\" value=\"" . $this->_htmlFilter('user') . "\" /></td>"; 442 $renderer->doc .= " <td class=\"centeralign\" style=\"border-bottom: 3px solid #ccc\"><input type=\"text\" style=\"width:90%; color: #FF9900';\" name=\"username\" class=\"edit\" value=\"" . $this->_htmlFilter('name') . "\" /></td>"; 443 $renderer->doc .= " <td style=\"border-bottom: 3px solid #ccc\"><input type=\"text\" style=\"width:90%; color: #FF9900';\" name=\"usermail\" class=\"edit\" value=\"" . $this->_htmlFilter('mail') . "\" /></td>"; 444 $renderer->doc .= " <td colspan=\"" . $colspan . "\" class=\"centeralign\" style=\"border-bottom: 3px solid #ccc\">"; 445 $renderer->doc .= " <span> <input type=\"text\" style=\"width:95%; color: #FF9900';\" name=\"usergroups\" class=\"edit\" value=\"" . $this->_htmlFilter('grps') . "\" /></span></td>"; 446 $renderer->doc .= " </tr>\n"; 447 448 $renderer->doc .= " <tr>\n"; 449 if ($allow_delete_user) $renderer->doc .= " <th style='color: #FF9900'>" . $this->lang['delete'] . "</th>\n"; 450 $renderer->doc .= " <th>" . $this->lang['user_id'] . "</th>\n"; 451 $renderer->doc .= " <th>" . $this->lang['user_name'] . "</th>\n"; 452 $renderer->doc .= " <th>" . $this->lang['user_mail'] . "</th>\n"; 453 // loop through available groups 454 foreach ($this->grplst as $g) { 455 $renderer->doc .= " <th>" . ucwords(str_replace("_", " ", str_replace("wg_", "", htmlspecialchars($g)))) . "</th>\n"; 456 } 457 458 $renderer->doc .= " </tr>\n"; 459 460 461 // loop through users 462 foreach ($this->userlist as $name => $u) { 463 // print user info 464 $renderer->doc .= " <tr>\n"; 465 if ($allow_delete_user) $renderer->doc .= "<td class=\"centeralign\"><input type=\"checkbox\" name=\"delete[" . $name . "]\" /></td>"; 466 467 $renderer->doc .= " <td>" . htmlspecialchars($name); 468 469 // need tag so user isn't pulled out of a group if it was added 470 // between initial page load and update 471 472 //change TurTur: 473 //$hn = md5($name); // caused trouble, on dreamhost md5 did not work 474 //output is better readable too 475 //end change 476 477 $renderer->doc .= "<input type=\"hidden\" name=\"id_" . $name . "\" value=\"1\" />"; 478 $renderer->doc .= "</td>\n"; 479 $renderer->doc .= " <td>" . htmlspecialchars($u['name']) . "</td>\n"; 480 $renderer->doc .= " <td>"; 481 $renderer->emaillink($u['mail']); 482 483 $renderer->doc .= "</td>\n"; 484 // loop through groups 485 foreach ($this->grplst as $g) { 486 $renderer->doc .= " <td class=\"centeralign\">"; 487 488 $chk = "chk_" . $name . "_" . $g; 489 490 // does this box need to be disabled? 491 // prevents user from taking himself out of an important group 492 $disabled = 0; 493 // if this box applies to a current group membership of the current user, continue check 494 if (in_array($g, $u['grps']) && $_SERVER['REMOTE_USER'] == $name) { 495 // if user is an admin and group is an admin group, disable 496 if ($INFO['isadmin'] && in_array($g, $admingrplst)) { 497 $disabled = 1; 498 // if user was authenticated by this group, disable 499 } else if (strlen($authbygrp) > 0 && $g == $authbygrp) { 500 $disabled = 1; 501 } 502 } 503 504 // update user group membership 505 // only update if something changed 506 // keep track of status 507 $update = array(); 508 if (!$disabled && $_POST["id_" . $name]) { 509 if ($_POST[$chk]) { 510 if (!in_array($g, $u['grps'])) { 511 $u['grps'][] = $g; 512 $update['grps'] = $u['grps']; 513 } 514 } else { 515 if (in_array($g, $u['grps'])) { 516 $u['grps'] = remove_item_by_value($g, $u['grps'], false); 517 $update['grps'] = $u['grps']; 518 } 519 } 520 if (count($update) > 0) { 521 if ($this->_auth->triggerUserMod('modify',array($name, $update))) { 522 io_saveFile($conf['cachedir'] . '/sessionpurge', time()); //invalidate all sessions 523 if ($status == 0) $status = 1; 524 } else { 525 $status = 2; 526 } 527 } 528 } 529 530 // display check box 531 $renderer->doc .= "<input type=\"checkbox\" name=\"" . $chk . "\""; 532 if (in_array($g, $u['grps'])) { 533 $renderer->doc .= " checked=\"true\""; 534 } 535 if ($disabled) { 536 $renderer->doc .= " disabled=\"true\""; 537 } 538 539 $renderer->doc .= " />"; 540 541 $renderer->doc .= "</td>\n"; 542 } 543 $renderer->doc .= " </tr>\n\n"; 544 } 545 546 $renderer->doc .= " </tbody>\n"; 547 548 $renderer->doc .= " <tbody>"; 549 $renderer->doc .= " <tr><td colspan=\"" . $colspan . "\" class=\"centeralign\" STYLE='border-top: 3px solid #ccc'>"; 550 $renderer->doc .= " <span class=\"medialeft\">"; 551 $renderer->doc .= " <input type=\"image\" src=\"" . GROUPMANAGER_IMAGES . "search.png\" onmouseover=\"this.src='" . GROUPMANAGER_IMAGES . "search_hilite.png'\" onmouseout=\"this.src='" . GROUPMANAGER_IMAGES . "search.png'\" STYLE=\"float: left; padding-right: 5px;\" name=\"fn[search][new]\" title=\"" . $this->lang['search_prompt'] . "\" alt=\"" . $this->lang['search'] . "\" />"; 552 $renderer->doc .= " <input type=\"image\" src=\"" . GROUPMANAGER_IMAGES . "workgroup.png\" onmouseover=\"this.src='" . GROUPMANAGER_IMAGES . "workgroup_hilite.png'\" onmouseout=\"this.src='" . GROUPMANAGER_IMAGES . "workgroup.png'\" STYLE=\"float: left; padding-right: 5px;\" name=\"fn[search][show_default]\" title=\"" . $this->lang['search_default_group'] . "\" alt=\"" . $this->lang['search_default_group'] . "\" />"; 553 $renderer->doc .= " <input type=\"image\" src=\"" . GROUPMANAGER_IMAGES . "everybody.png\" onmouseover=\"this.src='" . GROUPMANAGER_IMAGES . "everybody_hilite.png'\" onmouseout=\"this.src='" . GROUPMANAGER_IMAGES . "everybody.png'\" STYLE=\"float: left; padding-right: 5px;\" name=\"fn[search][clear]\" title=\"" . $this->lang['clear'] . "\" alt=\"" . $this->lang['clear'] . "\" />"; 554 $renderer->doc .= " </span>"; 555 $renderer->doc .= " <span class=\"mediaright\">"; 556 $renderer->doc .= " <input type=\"submit\" name=\"fn[start]\" " . $page_buttons['start'] . " class=\"button\" value=\"" . $this->lang['start'] . "\" />"; 557 $renderer->doc .= " <input type=\"submit\" name=\"fn[prev]\" " . $page_buttons['prev'] . " class=\"button\" value=\"" . $this->lang['prev'] . "\" />"; 558 $renderer->doc .= " <input type=\"submit\" name=\"fn[next]\" " . $page_buttons['next'] . " class=\"button\" value=\"" . $this->lang['next'] . "\" />"; 559 $renderer->doc .= " <input type=\"submit\" name=\"fn[last]\" " . $page_buttons['last'] . " class=\"button\" value=\"" . $this->lang['last'] . "\" />"; 560 561 $renderer->doc .= " </span>"; 562 $renderer->doc .= " </td></tr>"; 563 564 $renderer->doc .= " </tbody>"; 565 566 $renderer->doc .= "</table>\n"; 567 568 $renderer->doc .= "<input type=\"hidden\" name=\"start\" value=\"" . $this->_start . "\" />"; 569 // update button 570 $renderer->doc .= "<div><input type=\"submit\" name=\"fn[update]\" " . $page_buttons['update'] . " class=\"button\" value=\"" . $this->lang['btn_update_group'] . "\" /></div>"; 571 572 $renderer->doc .= "</form>"; 573 574 575 if ($this->_auth->canDo('addUser') && $allow_add_user) { 576 $style = $this->_add_user ? " class=\"add_user\"" : ""; 577 $renderer->doc .= "<div" . $style . ">"; 578 $renderer->doc .= $this->locale_xhtml('add'); 579 $renderer->doc .= " <div class=\"level2\">"; 580 581 $UserData['grps'][0] = $this->DefaultGroup; 582 $this->_htmlUserForm($renderer, 'add', null, $UserData, 4); 583 584 $renderer->doc .= " </div>"; 585 $renderer->doc .= "</div>"; 586 } 587 588 // display relevant status message 589 if ($status == 1) { 590 msg($this->lang['updatesuccess'], 1); 591 } else if ($status == 2) { 592 msg($this->lang['updatefailed'], -1); 593 } 594 } else { 595 // not authorized 596 $renderer->doc .= "<p>" . $this->lang['notauthorized'] . "</p>\n"; 597 } 598 599 return true; 600 } 601 return false; 602 } 603 604 605 function _htmlUserForm(&$renderer, $cmd, $user = '', $userdata = array(), $indent = 0) 606 { 607 global $conf; 608 global $ID; 609 610 $name = $mail = $groups = ''; 611 $notes = array(); 612 613 extract($userdata); 614 if (!empty($grps)) $groups = join(',', $grps); 615 616 if (!$user) { 617 //$groups will contain the default group when users are added 618 $notes[] = sprintf($this->lang['note_group'], $groups); 619 } 620 621 $renderer->doc .= "<form action=\"" . wl($ID) . "\" method=\"post\">"; 622 $renderer->doc .= formSecurityToken(false); 623 $renderer->doc .= " <table class=\"inline\" width='75%'>"; 624 $renderer->doc .= " <thead>"; 625 $renderer->doc .= " <tr><th>" . $this->lang["field"] . "</th><th>" . $this->lang["value"] . "</th></tr>"; 626 $renderer->doc .= " </t>"; 627 $renderer->doc .= " <tbody>"; 628 629 $this->_htmlInputField($renderer, $cmd . "_userid", "userid", $this->lang["user_id"], $user, $this->_auth->canDo("modLogin"), $indent + 6); 630 $this->_htmlInputField($renderer, $cmd . "_userpass", "userpass", $this->lang["user_pass"], "", $this->_auth->canDo("modPass"), $indent + 6); 631 $this->_htmlInputField($renderer, $cmd . "_username", "username", $this->lang["user_name"], $name, $this->_auth->canDo("modName"), $indent + 6); 632 $this->_htmlInputField($renderer, $cmd . "_usermail", "usermail", $this->lang["user_mail"], $mail, $this->_auth->canDo("modMail"), $indent + 6); 633 $renderer->doc .= "<input type='hidden' id='" . $cmd . "_usergroups' name='usergroups' value='" . $groups . "'"; 634 635 if ($this->_auth->canDo("modPass")) { 636 $notes[] = $this->lang['note_pass']; 637 if ($user) { 638 $notes[] = $this->lang['note_notify']; 639 } 640 641 $renderer->doc .= "<tr><td><label for=\"" . $cmd . "_usernotify\" >" . $this->lang["user_notify"] . ": </label></td><td><input type=\"checkbox\" id=\"" . $cmd . "_usernotify\" name=\"usernotify\" value=\"1\" /></td></tr>"; 642 } 643 644 $renderer->doc .= " </tbody>"; 645 $renderer->doc .= " <tbody>"; 646 $renderer->doc .= " <tr>"; 647 $renderer->doc .= " <td colspan=\"2\">"; 648 649 $this->_htmlFilterSettings($renderer, $indent + 10); 650 651 $renderer->doc .= " <input type=\"submit\" name=\"fn[" . $cmd . "]\" class=\"button\" value=\"" . $this->lang[$cmd] . "\" />"; 652 $renderer->doc .= " </td>"; 653 $renderer->doc .= " </tr>"; 654 $renderer->doc .= " </tbody>"; 655 $renderer->doc .= " </table>"; 656 657 foreach ($notes as $note) 658 $renderer->doc .= "<div class=\"fn\">" . $note . "</div>"; 659 660 $renderer->doc .= "</form>"; 661 } 662 663 function _htmlInputField(&$renderer, $id, $name, $label, $value, $cando, $indent = 0) 664 { 665 $class = $cando ? '' : ' class="disabled"'; 666 $disabled = $cando ? '' : ' disabled="disabled"'; 667 $renderer->doc .= str_pad('', $indent); 668 669 if ($name == 'userpass') { 670 $fieldtype = 'password'; 671 $autocomp = 'autocomplete="off"'; 672 } else { 673 $fieldtype = 'text'; 674 $autocomp = ''; 675 } 676 677 678 $renderer->doc .= "<tr $class>"; 679 $renderer->doc .= "<td style='width: 30%'><label for=\"$id\" >$label: </label></td>"; 680 $renderer->doc .= "<td>"; 681 if ($cando) { 682 $renderer->doc .= "<input type=\"$fieldtype\" id=\"$id\" name=\"$name\" value=\"$value\" class=\"edit\" $autocomp style='width: 95%'/>"; 683 } else { 684 $renderer->doc .= "<input type=\"hidden\" name=\"$name\" value=\"$value\" />"; 685 $renderer->doc .= "<input type=\"$fieldtype\" id=\"$id\" name=\"$name\" value=\"$value\" class=\"edit disabled\" disabled=\"disabled\" />"; 686 } 687 $renderer->doc .= "</td>"; 688 $renderer->doc .= "</tr>"; 689 } 690 691 function _addUser() 692 { 693 if (!checkSecurityToken()) return false; 694 if (!$this->_auth->canDo('addUser')) return false; 695 696 list($user, $pass, $name, $mail, $grps) = $this->_retrieveUser(); 697 if (empty($user)) return false; 698 699 if ($this->_auth->canDo('modPass')) { 700 if (empty($pass)) { 701 if (!empty($_REQUEST['usernotify'])) { 702 $pass = auth_pwgen(); 703 } else { 704 msg($this->lang['user_must_be_notified_with_generated_pwd'], -1); 705 msg($this->lang['add_fail'], -1); 706 return false; 707 } 708 } 709 } else { 710 if (!empty($pass)) { 711 msg($this->lang['add_fail'], -1); 712 return false; 713 } 714 } 715 716 if ($this->_auth->canDo('modName')) { 717 if (empty($name)) { 718 msg($this->lang['add_fail'], -1); 719 return false; 720 } 721 } else { 722 if (!empty($name)) { 723 return false; 724 } 725 } 726 727 if ($this->_auth->canDo('modMail')) { 728 if (empty($mail)) { 729 msg($this->lang['mail_required'], -1); 730 msg($this->lang['add_fail'], -1); 731 return false; 732 } 733 } else { 734 if (!empty($mail)) { 735 return false; 736 } 737 } 738 739 if ($ok = $this->_auth->triggerUserMod('create', array($user, $pass, $name, $mail, $grps))) { 740 741 msg($this->lang['add_ok'], 1); 742 743 if (!empty($_REQUEST['usernotify']) && $pass) { 744 $this->_notifyUser($user, $pass); 745 } 746 } else { 747 msg($this->lang['add_fail'], -1); 748 } 749 750 return $ok; 751 } 752 753 /** 754 * Delete user 755 */ 756 function _deleteUser() 757 { 758 global $conf; 759 //$MayDelete = false; 760 761 if (!checkSecurityToken()) return false; 762 if (!$this->_auth->canDo('delUser')) return false; 763 764 $selected = $_REQUEST['delete']; 765 if (!is_array($selected) || empty($selected)) return false; 766 $selected = array_keys($selected); 767 768 if (in_array($_SERVER['REMOTE_USER'], $selected)) { 769 msg($this->lang['cant_delete_yourself'], -1); 770 return false; 771 } 772 773 //user may only be deleted if not member of any group other than the current working group roles 774 foreach ($selected as $selection) { 775 $currentfilter['user'] = $selection; 776 $currentuser = $this->_auth->retrieveUsers(0, 100000, $currentfilter); 777 $currentgroups = $currentuser[$selection]['grps']; 778 //user may only be part of working group parts 779 //if (count($currentgroups) <= count($this->grplst)) { commented out, If the user is in more groups than this page manages it would skip the test to see if the user was in any other groups! 780 foreach ($currentgroups as $g) { 781 if (!in_array($g, $this->grplst)) { 782 msg($this->lang['cant_delete_if_more_groups'], -1); 783 return false; 784 } 785 } 786 //} 787 } 788 789 $count = $this->_auth->triggerUserMod('delete', array($selected)); 790 if ($count == count($selected)) { 791 $text = str_replace('%d', $count, $this->lang['delete_ok']); 792 msg("$text.", 1); 793 } else { 794 $part1 = str_replace('%d', $count, $this->lang['delete_ok']); 795 $part2 = str_replace('%d', (count($selected) - $count), $this->lang['delete_fail']); 796 msg("$part1, $part2", -1); 797 } 798 799 // invalidate all sessions 800 io_saveFile($conf['cachedir'] . '/sessionpurge', time()); 801 802 return true; 803 } 804 805 /** 806 * send password change notification email 807 */ 808 function _notifyUser($user, $password) 809 { 810 811 if ($sent = auth_sendPassword($user, $password)) { 812 msg($this->lang['notify_ok'], 1); 813 } else { 814 msg($this->lang['notify_fail'], -1); 815 } 816 817 return $sent; 818 } 819 820 function _htmlFilter($key) 821 { 822 if (empty($this->_filter)) return ''; 823 return (isset($this->_filter[$key]) ? hsc($this->_filter[$key]) : ''); 824 } 825 826 function _htmlFilterSettings(&$renderer, $indent = 0) 827 { 828 829 $renderer->doc .= "<input type=\"hidden\" name=\"start\" value=\"" . $this->_start . "\" />"; 830 831 foreach ($this->_filter as $key => $filter) { 832 $renderer->doc .= "<input type=\"hidden\" name=\"filter[" . $key . "]\" value=\"" . hsc($filter) . "\" />"; 833 } 834 } 835 836 /** 837 * retrieve & clean user data from the form 838 * 839 * @return array(user, password, full name, email, array(groups)) 840 */ 841 function _retrieveUser($clean = true) 842 { 843 global $auth; 844 845 $user[0] = ($clean) ? $auth->cleanUser($_REQUEST['userid']) : $_REQUEST['userid']; 846 $user[1] = $_REQUEST['userpass']; 847 $user[2] = $_REQUEST['username']; 848 $user[3] = $_REQUEST['usermail']; 849 $user[4] = explode(',', $_REQUEST['usergroups']); 850 851 $user[4] = array_map('trim', $user[4]); 852 if ($clean) $user[4] = array_map(array($auth, 'cleanGroup'), $user[4]); 853 $user[4] = array_filter($user[4]); 854 $user[4] = array_unique($user[4]); 855 if (!count($user[4])) $user[4] = null; 856 857 return $user; 858 } 859 860 function _setFilter($op) 861 { 862 863 $this->_filter = array(); 864 865 switch ($op) { 866 case 'clear': 867 break; 868 869 case 'new': 870 list($user, $pass, $name, $mail, $grps) = $this->_retrieveUser(false); 871 if (!empty($user)) $this->_filter['user'] = str_replace(' ', '_', $user); 872 if (!empty($name)) $this->_filter['name'] = $name; 873 if (!empty($mail)) $this->_filter['mail'] = $mail; 874 if (!empty($grps)) $this->_filter['grps'] = str_replace(' ', '_', join('|', $grps)); 875 //if (!empty($grps)) $this->_filter['grps'] = join('|',$grps); 876 break; 877 case show_default: 878 $this->_filter['grps'] = $this->DefaultGroup; 879 break; 880 } 881 882 } 883 884 function _retrieveFilter() 885 { 886 887 $t_filter = $_REQUEST['filter']; 888 if (!is_array($t_filter)) return array(); 889 890 // messy, but this way we ensure we aren't getting any additional crap from malicious users 891 $filter = array(); 892 893 if (isset($t_filter['user'])) $filter['user'] = $t_filter['user']; 894 if (isset($t_filter['name'])) $filter['name'] = $t_filter['name']; 895 if (isset($t_filter['mail'])) $filter['mail'] = $t_filter['mail']; 896 if (isset($t_filter['grps'])) $filter['grps'] = $t_filter['grps']; 897 898 return $filter; 899 } 900 901 function _validatePagination() 902 { 903 904 if ($this->_start >= $this->_user_total) { 905 $this->_start = $this->_user_total - $this->_pagesize; 906 } 907 if ($this->_start < 0) $this->_start = 0; 908 909 $this->_last = min($this->_user_total, $this->_start + $this->_pagesize); 910 } 911 912 /* 913 * return an array of strings to enable/disable pagination buttons 914 */ 915 function _pagination() 916 { 917 918 $disabled = 'disabled="disabled"'; 919 920 $buttons['start'] = $buttons['prev'] = ($this->_start == 0) ? $disabled : ''; 921 922 if ($this->_user_total == -1) { 923 $buttons['last'] = $disabled; 924 $buttons['next'] = ''; 925 } else { 926 $buttons['last'] = $buttons['next'] = (($this->_start + $this->_pagesize) >= $this->_user_total) ? $disabled : ''; 927 } 928 929 $buttons['update'] = ''; 930 931 return $buttons; 932 } 933} 934 935// vim:ts=4:sw=4:et:enc=utf-8: 936