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