]>
Commit | Line | Data |
---|---|---|
66665fba | 1 | <?php |
369dbc19 | 2 | class Pref_Users extends Handler_Protected { |
17f9d200 JK |
3 | function before($method) { |
4 | if (parent::before($method)) { | |
66665fba AD |
5 | if ($_SESSION["access_level"] < 10) { |
6 | print __("Your access level is insufficient to open this tab."); | |
7 | return false; | |
8 | } | |
9 | return true; | |
10 | } | |
11 | return false; | |
12 | } | |
13 | ||
8484ce22 | 14 | function csrf_ignore($method) { |
d9afd9b2 | 15 | $csrf_ignored = array("index", "edit", "userdetails"); |
8484ce22 AD |
16 | |
17 | return array_search($method, $csrf_ignored) !== false; | |
18 | } | |
19 | ||
66665fba AD |
20 | function edit() { |
21 | global $access_level_names; | |
22 | ||
bf9cc9ad AD |
23 | print '<div dojoType="dijit.layout.TabContainer" style="height : 400px"> |
24 | <div dojoType="dijit.layout.ContentPane" title="'.__('Edit user').'">'; | |
25 | ||
222a61c2 | 26 | print "<form id=\"user_edit_form\" onsubmit='return false' dojoType=\"dijit.form.Form\">"; |
66665fba | 27 | |
e6532439 | 28 | $id = (int) clean($_REQUEST["id"]); |
bf9cc9ad | 29 | |
328118d1 AD |
30 | print_hidden("id", "$id"); |
31 | print_hidden("op", "pref-users"); | |
32 | print_hidden("method", "editSave"); | |
66665fba | 33 | |
c2418a55 AD |
34 | $sth = $this->pdo->prepare("SELECT * FROM ttrss_users WHERE id = ?"); |
35 | $sth->execute([$id]); | |
66665fba | 36 | |
c2418a55 | 37 | if ($row = $sth->fetch()) { |
66665fba | 38 | |
c2418a55 AD |
39 | $login = $row["login"]; |
40 | $access_level = $row["access_level"]; | |
41 | $email = $row["email"]; | |
66665fba | 42 | |
c2418a55 | 43 | $sel_disabled = ($id == $_SESSION["uid"] || $login == "admin") ? "disabled" : ""; |
66665fba | 44 | |
c2418a55 AD |
45 | print "<div class=\"dlgSec\">".__("User")."</div>"; |
46 | print "<div class=\"dlgSecCont\">"; | |
66665fba | 47 | |
c2418a55 AD |
48 | if ($sel_disabled) { |
49 | print_hidden("login", "$login"); | |
50 | } | |
222a61c2 | 51 | |
c2418a55 AD |
52 | print "<input size=\"30\" style=\"font-size : 16px\" |
53 | dojoType=\"dijit.form.ValidationTextBox\" required=\"1\" | |
54 | $sel_disabled | |
55 | name=\"login\" value=\"$login\">"; | |
66665fba | 56 | |
c2418a55 | 57 | print "</div>"; |
66665fba | 58 | |
c2418a55 AD |
59 | print "<div class=\"dlgSec\">".__("Authentication")."</div>"; |
60 | print "<div class=\"dlgSecCont\">"; | |
66665fba | 61 | |
c2418a55 AD |
62 | print __('Access level: ') . " "; |
63 | ||
64 | if (!$sel_disabled) { | |
65 | print_select_hash("access_level", $access_level, $access_level_names, | |
66 | "dojoType=\"dijit.form.Select\" $sel_disabled"); | |
67 | } else { | |
68 | print_select_hash("", $access_level, $access_level_names, | |
69 | "dojoType=\"dijit.form.Select\" $sel_disabled"); | |
70 | print_hidden("access_level", "$access_level"); | |
71 | } | |
66665fba | 72 | |
c2418a55 | 73 | print "<hr/>"; |
66665fba | 74 | |
c2418a55 | 75 | print "<input dojoType=\"dijit.form.TextBox\" type=\"password\" size=\"20\" placeholder=\"Change password\" |
66665fba AD |
76 | name=\"password\">"; |
77 | ||
c2418a55 | 78 | print "</div>"; |
66665fba | 79 | |
c2418a55 AD |
80 | print "<div class=\"dlgSec\">".__("Options")."</div>"; |
81 | print "<div class=\"dlgSecCont\">"; | |
66665fba | 82 | |
c2418a55 | 83 | print "<input dojoType=\"dijit.form.TextBox\" size=\"30\" name=\"email\" placeholder=\"E-mail\" |
66665fba AD |
84 | value=\"$email\">"; |
85 | ||
c2418a55 | 86 | print "</div>"; |
66665fba | 87 | |
c2418a55 | 88 | print "</table>"; |
66665fba | 89 | |
c2418a55 AD |
90 | print "</form>"; |
91 | ||
92 | } | |
66665fba | 93 | |
bf9cc9ad AD |
94 | print '</div>'; #tab |
95 | print "<div href=\"backend.php?op=pref-users&method=userdetails&id=$id\" | |
96 | dojoType=\"dijit.layout.ContentPane\" title=\"".__('User details')."\">"; | |
97 | ||
98 | print '</div>'; | |
99 | print '</div>'; | |
100 | ||
66665fba | 101 | print "<div class=\"dlgButtons\"> |
222a61c2 | 102 | <button dojoType=\"dijit.form.Button\" type=\"submit\">". |
bf9cc9ad | 103 | __('Save')."</button> |
222a61c2 | 104 | <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('userEditDlg').hide()\">". |
bf9cc9ad | 105 | __('Cancel')."</button></div>"; |
66665fba | 106 | |
66665fba AD |
107 | return; |
108 | } | |
109 | ||
bf9cc9ad | 110 | function userdetails() { |
e6532439 | 111 | $id = (int) clean($_REQUEST["id"]); |
bf9cc9ad | 112 | |
c2418a55 | 113 | $sth = $this->pdo->prepare("SELECT login, |
bf9cc9ad AD |
114 | ".SUBSTRING_FOR_DATE."(last_login,1,16) AS last_login, |
115 | access_level, | |
116 | (SELECT COUNT(int_id) FROM ttrss_user_entries | |
117 | WHERE owner_uid = id) AS stored_articles, | |
118 | ".SUBSTRING_FOR_DATE."(created,1,16) AS created | |
119 | FROM ttrss_users | |
c2418a55 AD |
120 | WHERE id = ?"); |
121 | $sth->execute([$id]); | |
bf9cc9ad | 122 | |
c2418a55 AD |
123 | if ($row = $sth->fetch()) { |
124 | print "<table width='100%'>"; | |
bf9cc9ad | 125 | |
c2418a55 AD |
126 | $last_login = make_local_datetime( |
127 | $row["last_login"], true); | |
bf9cc9ad | 128 | |
c2418a55 AD |
129 | $created = make_local_datetime( |
130 | $row["created"], true); | |
bf9cc9ad | 131 | |
c2418a55 | 132 | $stored_articles = $row["stored_articles"]; |
bf9cc9ad | 133 | |
c2418a55 AD |
134 | print "<tr><td>".__('Registered')."</td><td>$created</td></tr>"; |
135 | print "<tr><td>".__('Last logged in')."</td><td>$last_login</td></tr>"; | |
bf9cc9ad | 136 | |
c2418a55 AD |
137 | $sth = $this->pdo->prepare("SELECT COUNT(id) as num_feeds FROM ttrss_feeds |
138 | WHERE owner_uid = ?"); | |
139 | $sth->execute([$id]); | |
140 | $row = $sth->fetch(); | |
141 | $num_feeds = $row["num_feeds"]; | |
bf9cc9ad | 142 | |
c2418a55 AD |
143 | print "<tr><td>".__('Subscribed feeds count')."</td><td>$num_feeds</td></tr>"; |
144 | print "<tr><td>".__('Stored articles')."</td><td>$stored_articles</td></tr>"; | |
bf9cc9ad | 145 | |
c2418a55 | 146 | print "</table>"; |
bf9cc9ad | 147 | |
c2418a55 | 148 | print "<h1>".__('Subscribed feeds')."</h1>"; |
bf9cc9ad | 149 | |
c2418a55 AD |
150 | $sth = $this->pdo->prepare("SELECT id,title,site_url FROM ttrss_feeds |
151 | WHERE owner_uid = ? ORDER BY title"); | |
152 | $sth->execute([$id]); | |
bf9cc9ad | 153 | |
c2418a55 | 154 | print "<ul class=\"userFeedList\">"; |
bf9cc9ad | 155 | |
c2418a55 | 156 | while ($line = $sth->fetch()) { |
bf9cc9ad | 157 | |
c2418a55 | 158 | $icon_file = ICONS_URL."/".$line["id"].".ico"; |
bf9cc9ad | 159 | |
c2418a55 AD |
160 | if (file_exists($icon_file) && filesize($icon_file) > 0) { |
161 | $feed_icon = "<img class=\"tinyFeedIcon\" src=\"$icon_file\">"; | |
162 | } else { | |
163 | $feed_icon = "<img class=\"tinyFeedIcon\" src=\"images/blank_icon.gif\">"; | |
164 | } | |
bf9cc9ad | 165 | |
c2418a55 | 166 | print "<li>$feed_icon <a href=\"".$line["site_url"]."\">".$line["title"]."</a></li>"; |
bf9cc9ad | 167 | |
bf9cc9ad AD |
168 | } |
169 | ||
c2418a55 AD |
170 | print "</ul>"; |
171 | ||
172 | ||
173 | } else { | |
174 | print "<h1>".__('User not found')."</h1>"; | |
bf9cc9ad | 175 | } |
c2418a55 | 176 | |
bf9cc9ad AD |
177 | } |
178 | ||
66665fba | 179 | function editSave() { |
e6532439 AD |
180 | $login = trim(clean($_REQUEST["login"])); |
181 | $uid = clean($_REQUEST["id"]); | |
182 | $access_level = (int) clean($_REQUEST["access_level"]); | |
183 | $email = trim(clean($_REQUEST["email"])); | |
184 | $password = clean($_REQUEST["password"]); | |
66665fba AD |
185 | |
186 | if ($password) { | |
8db5d8ea | 187 | $salt = substr(bin2hex(get_random_bytes(125)), 0, 250); |
098df83b | 188 | $pwd_hash = encrypt_password($password, $salt, true); |
c2418a55 AD |
189 | $pass_query_part = "pwd_hash = ".$this->pdo->quote($pwd_hash).", |
190 | salt = ".$this->pdo->quote($salt).","; | |
66665fba AD |
191 | } else { |
192 | $pass_query_part = ""; | |
193 | } | |
194 | ||
c2418a55 AD |
195 | $sth = $this->pdo->prepare("UPDATE ttrss_users SET $pass_query_part login = ?, |
196 | access_level = ?, email = ?, otp_enabled = false WHERE id = ?"); | |
197 | $sth->execute([$login, $access_level, $email, $uid]); | |
66665fba AD |
198 | |
199 | } | |
200 | ||
201 | function remove() { | |
e6532439 | 202 | $ids = explode(",", clean($_REQUEST["ids"])); |
66665fba AD |
203 | |
204 | foreach ($ids as $id) { | |
205 | if ($id != $_SESSION["uid"] && $id != 1) { | |
c2418a55 AD |
206 | $sth = $this->pdo->prepare("DELETE FROM ttrss_tags WHERE owner_uid = ?"); |
207 | $sth->execute([$id]); | |
208 | ||
209 | $sth = $this->pdo->prepare("DELETE FROM ttrss_feeds WHERE owner_uid = ?"); | |
210 | $sth->execute([$id]); | |
211 | ||
212 | $sth = $this->pdo->prepare("DELETE FROM ttrss_users WHERE id = ?"); | |
213 | $sth->execute([$id]); | |
66665fba AD |
214 | } |
215 | } | |
216 | } | |
217 | ||
218 | function add() { | |
219 | ||
e6532439 | 220 | $login = trim(clean($_REQUEST["login"])); |
66665fba | 221 | $tmp_user_pwd = make_password(8); |
8db5d8ea | 222 | $salt = substr(bin2hex(get_random_bytes(125)), 0, 250); |
098df83b | 223 | $pwd_hash = encrypt_password($tmp_user_pwd, $salt, true); |
66665fba | 224 | |
c2418a55 AD |
225 | $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE |
226 | login = ?"); | |
227 | $sth->execute([$login]); | |
66665fba | 228 | |
c2418a55 | 229 | if (!$sth->fetch()) { |
66665fba | 230 | |
c2418a55 | 231 | $sth = $this->pdo->prepare("INSERT INTO ttrss_users |
098df83b | 232 | (login,pwd_hash,access_level,last_login,created, salt) |
c2418a55 AD |
233 | VALUES (?, ?, 0, null, NOW(), ?)"); |
234 | $sth->execute([$login, $pwd_hash, $salt]); | |
66665fba | 235 | |
c2418a55 AD |
236 | $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE |
237 | login = ? AND pwd_hash = ?"); | |
238 | $sth->execute([$login, $pwd_hash]); | |
66665fba | 239 | |
c2418a55 | 240 | if ($row = $sth->fetch()) { |
66665fba | 241 | |
c2418a55 | 242 | $new_uid = $row['id']; |
66665fba AD |
243 | |
244 | print format_notice(T_sprintf("Added user <b>%s</b> with password <b>%s</b>", | |
245 | $login, $tmp_user_pwd)); | |
246 | ||
a42c55f0 | 247 | initialize_user($new_uid); |
66665fba AD |
248 | |
249 | } else { | |
250 | ||
251 | print format_warning(T_sprintf("Could not create user <b>%s</b>", $login)); | |
252 | ||
253 | } | |
254 | } else { | |
255 | print format_warning(T_sprintf("User <b>%s</b> already exists.", $login)); | |
256 | } | |
257 | } | |
258 | ||
233b74ad | 259 | static function resetUserPassword($uid, $show_password) { |
66665fba | 260 | |
c2418a55 | 261 | $pdo = Db::pdo(); |
66665fba | 262 | |
c2418a55 AD |
263 | $sth = $pdo->prepare("SELECT login, email |
264 | FROM ttrss_users WHERE id = ?"); | |
265 | $sth->execute([$uid]); | |
266 | ||
267 | if ($row = $sth->fetch()) { | |
098df83b | 268 | |
c2418a55 AD |
269 | $login = $row["login"]; |
270 | $email = $row["email"]; | |
66665fba | 271 | |
c2418a55 AD |
272 | $new_salt = substr(bin2hex(get_random_bytes(125)), 0, 250); |
273 | $tmp_user_pwd = make_password(8); | |
098df83b | 274 | |
c2418a55 | 275 | $pwd_hash = encrypt_password($tmp_user_pwd, $new_salt, true); |
66665fba | 276 | |
c2418a55 AD |
277 | $sth = $pdo->prepare("UPDATE ttrss_users |
278 | SET pwd_hash = ?, salt = ?, otp_enabled = false | |
279 | WHERE id = ?"); | |
280 | $sth->execute([$pwd_hash, $new_salt, $uid]); | |
66665fba | 281 | |
c2418a55 AD |
282 | if ($show_password) { |
283 | print T_sprintf("Changed password of user <b>%s</b> to <b>%s</b>", $login, $tmp_user_pwd); | |
284 | } else { | |
285 | print_notice(T_sprintf("Sending new password of user <b>%s</b> to <b>%s</b>", $login, $email)); | |
286 | } | |
287 | ||
288 | require_once 'classes/ttrssmailer.php'; | |
66665fba | 289 | |
c2418a55 AD |
290 | if ($email) { |
291 | require_once "lib/MiniTemplator.class.php"; | |
66665fba | 292 | |
c2418a55 | 293 | $tpl = new MiniTemplator; |
66665fba | 294 | |
c2418a55 | 295 | $tpl->readTemplateFromFile("templates/resetpass_template.txt"); |
66665fba | 296 | |
c2418a55 AD |
297 | $tpl->setVariable('LOGIN', $login); |
298 | $tpl->setVariable('NEWPASS', $tmp_user_pwd); | |
66665fba | 299 | |
c2418a55 | 300 | $tpl->addBlock('message'); |
66665fba | 301 | |
c2418a55 | 302 | $message = ""; |
66665fba | 303 | |
c2418a55 | 304 | $tpl->generateOutputToString($message); |
66665fba | 305 | |
c2418a55 | 306 | $mail = new ttrssMailer(); |
66665fba | 307 | |
c2418a55 AD |
308 | $rc = $mail->quickMail($email, $login, |
309 | __("[tt-rss] Password change notification"), | |
310 | $message, false); | |
66665fba | 311 | |
c2418a55 AD |
312 | if (!$rc) print_error($mail->ErrorInfo); |
313 | } | |
314 | ||
66665fba | 315 | } |
f43e9e97 | 316 | } |
66665fba | 317 | |
f43e9e97 | 318 | function resetPass() { |
e6532439 | 319 | $uid = clean($_REQUEST["id"]); |
a42c55f0 | 320 | Pref_Users::resetUserPassword($uid, true); |
66665fba AD |
321 | } |
322 | ||
323 | function index() { | |
324 | ||
325 | global $access_level_names; | |
326 | ||
327 | print "<div id=\"pref-user-wrap\" dojoType=\"dijit.layout.BorderContainer\" gutters=\"false\">"; | |
328 | print "<div id=\"pref-user-header\" dojoType=\"dijit.layout.ContentPane\" region=\"top\">"; | |
329 | ||
330 | print "<div id=\"pref-user-toolbar\" dojoType=\"dijit.Toolbar\">"; | |
331 | ||
e6532439 | 332 | $user_search = trim(clean($_REQUEST["search"])); |
66665fba AD |
333 | |
334 | if (array_key_exists("search", $_REQUEST)) { | |
335 | $_SESSION["prefs_user_search"] = $user_search; | |
336 | } else { | |
337 | $user_search = $_SESSION["prefs_user_search"]; | |
338 | } | |
339 | ||
340 | print "<div style='float : right; padding-right : 4px;'> | |
341 | <input dojoType=\"dijit.form.TextBox\" id=\"user_search\" size=\"20\" type=\"search\" | |
342 | value=\"$user_search\"> | |
808ef3d4 | 343 | <button dojoType=\"dijit.form.Button\" onclick=\"updateUsersList()\">". |
66665fba AD |
344 | __('Search')."</button> |
345 | </div>"; | |
346 | ||
e6532439 | 347 | $sort = clean($_REQUEST["sort"]); |
66665fba AD |
348 | |
349 | if (!$sort || $sort == "undefined") { | |
350 | $sort = "login"; | |
351 | } | |
352 | ||
353 | print "<div dojoType=\"dijit.form.DropDownButton\">". | |
354 | "<span>" . __('Select')."</span>"; | |
355 | print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">"; | |
356 | print "<div onclick=\"selectTableRows('prefUserList', 'all')\" | |
357 | dojoType=\"dijit.MenuItem\">".__('All')."</div>"; | |
358 | print "<div onclick=\"selectTableRows('prefUserList', 'none')\" | |
359 | dojoType=\"dijit.MenuItem\">".__('None')."</div>"; | |
360 | print "</div></div>"; | |
361 | ||
808ef3d4 | 362 | print "<button dojoType=\"dijit.form.Button\" onclick=\"addUser()\">".__('Create user')."</button>"; |
66665fba AD |
363 | |
364 | print " | |
808ef3d4 | 365 | <button dojoType=\"dijit.form.Button\" onclick=\"editSelectedUser()\">". |
66665fba | 366 | __('Edit')."</button dojoType=\"dijit.form.Button\"> |
808ef3d4 | 367 | <button dojoType=\"dijit.form.Button\" onclick=\"removeSelectedUsers()\">". |
66665fba | 368 | __('Remove')."</button dojoType=\"dijit.form.Button\"> |
808ef3d4 | 369 | <button dojoType=\"dijit.form.Button\" onclick=\"resetSelectedUserPass()\">". |
66665fba AD |
370 | __('Reset password')."</button dojoType=\"dijit.form.Button\">"; |
371 | ||
610fe115 AD |
372 | PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, |
373 | "hook_prefs_tab_section", "prefUsersToolbar"); | |
374 | ||
66665fba AD |
375 | print "</div>"; #toolbar |
376 | print "</div>"; #pane | |
377 | print "<div id=\"pref-user-content\" dojoType=\"dijit.layout.ContentPane\" region=\"center\">"; | |
378 | ||
2cf93c04 AD |
379 | $sort = validate_field($sort, |
380 | ["login", "access_level", "created", "num_feeds", "created", "last_login"], "login"); | |
381 | ||
382 | if ($sort != "login") $sort = "$sort DESC"; | |
66665fba | 383 | |
c2418a55 | 384 | $sth = $this->pdo->prepare("SELECT |
1edff0d4 AD |
385 | tu.id, |
386 | login,access_level,email, | |
66665fba | 387 | ".SUBSTRING_FOR_DATE."(last_login,1,16) as last_login, |
1edff0d4 | 388 | ".SUBSTRING_FOR_DATE."(created,1,16) as created, |
f232aa5a | 389 | (SELECT COUNT(id) FROM ttrss_feeds WHERE owner_uid = tu.id) AS num_feeds |
66665fba | 390 | FROM |
1edff0d4 | 391 | ttrss_users tu |
66665fba | 392 | WHERE |
c2418a55 | 393 | (:search = '' OR login LIKE :search) AND tu.id > 0 |
2cf93c04 AD |
394 | ORDER BY $sort"); |
395 | $sth->execute([":search" => $user_search ? "%$user_search%" : ""]); | |
66665fba AD |
396 | |
397 | print "<p><table width=\"100%\" cellspacing=\"0\" | |
398 | class=\"prefUserList\" id=\"prefUserList\">"; | |
399 | ||
400 | print "<tr class=\"title\"> | |
401 | <td align='center' width=\"5%\"> </td> | |
1edff0d4 AD |
402 | <td width='20%'><a href=\"#\" onclick=\"updateUsersList('login')\">".__('Login')."</a></td> |
403 | <td width='20%'><a href=\"#\" onclick=\"updateUsersList('access_level')\">".__('Access Level')."</a></td> | |
404 | <td width='10%'><a href=\"#\" onclick=\"updateUsersList('num_feeds')\">".__('Subscribed feeds')."</a></td> | |
66665fba AD |
405 | <td width='20%'><a href=\"#\" onclick=\"updateUsersList('created')\">".__('Registered')."</a></td> |
406 | <td width='20%'><a href=\"#\" onclick=\"updateUsersList('last_login')\">".__('Last login')."</a></td></tr>"; | |
407 | ||
408 | $lnum = 0; | |
409 | ||
c2418a55 | 410 | while ($line = $sth->fetch()) { |
66665fba | 411 | |
66665fba AD |
412 | $uid = $line["id"]; |
413 | ||
84391d69 | 414 | print "<tr id=\"UMRR-$uid\">"; |
66665fba AD |
415 | |
416 | $line["login"] = htmlspecialchars($line["login"]); | |
417 | ||
a42c55f0 AD |
418 | $line["created"] = make_local_datetime($line["created"], false); |
419 | $line["last_login"] = make_local_datetime($line["last_login"], false); | |
66665fba | 420 | |
84391d69 AD |
421 | print "<td align='center'><input onclick='toggleSelectRow2(this);' |
422 | dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" | |
423 | id=\"UMCHK-$uid\"></td>"; | |
66665fba AD |
424 | |
425 | $onclick = "onclick='editUser($uid, event)' title='".__('Click to edit')."'"; | |
426 | ||
2f20dd58 | 427 | print "<td $onclick><img src='images/user.png' class='markedPic' alt=''> " . $line["login"] . "</td>"; |
66665fba AD |
428 | |
429 | if (!$line["email"]) $line["email"] = " "; | |
430 | ||
431 | print "<td $onclick>" . $access_level_names[$line["access_level"]] . "</td>"; | |
1edff0d4 | 432 | print "<td $onclick>" . $line["num_feeds"] . "</td>"; |
66665fba AD |
433 | print "<td $onclick>" . $line["created"] . "</td>"; |
434 | print "<td $onclick>" . $line["last_login"] . "</td>"; | |
435 | ||
436 | print "</tr>"; | |
437 | ||
438 | ++$lnum; | |
439 | } | |
440 | ||
441 | print "</table>"; | |
442 | ||
c2418a55 | 443 | if ($lnum == 0) { |
66665fba AD |
444 | if (!$user_search) { |
445 | print_warning(__('No users defined.')); | |
446 | } else { | |
447 | print_warning(__('No matching users found.')); | |
448 | } | |
66665fba AD |
449 | } |
450 | ||
451 | print "</div>"; #pane | |
6065f3ad | 452 | |
1ffe3391 | 453 | PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB, |
6065f3ad AD |
454 | "hook_prefs_tab", "prefUsers"); |
455 | ||
66665fba AD |
456 | print "</div>"; #container |
457 | ||
458 | } | |
ea79a0e0 | 459 | } |