]>
Commit | Line | Data |
---|---|---|
1 | <?php | |
2 | class Pref_Users extends Handler_Protected { | |
3 | function before($method) { | |
4 | if (parent::before($method)) { | |
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 | ||
14 | function csrf_ignore($method) { | |
15 | $csrf_ignored = array("index", "edit", "userdetails"); | |
16 | ||
17 | return array_search($method, $csrf_ignored) !== false; | |
18 | } | |
19 | ||
20 | function edit() { | |
21 | global $access_level_names; | |
22 | ||
23 | print '<div dojoType="dijit.layout.TabContainer" style="height : 400px"> | |
24 | <div dojoType="dijit.layout.ContentPane" title="'.__('Edit user').'">'; | |
25 | ||
26 | print "<form id=\"user_edit_form\" onsubmit='return false' dojoType=\"dijit.form.Form\">"; | |
27 | ||
28 | $id = (int) clean($_REQUEST["id"]); | |
29 | ||
30 | print_hidden("id", "$id"); | |
31 | print_hidden("op", "pref-users"); | |
32 | print_hidden("method", "editSave"); | |
33 | ||
34 | $sth = $this->pdo->prepare("SELECT * FROM ttrss_users WHERE id = ?"); | |
35 | $sth->execute([$id]); | |
36 | ||
37 | if ($row = $sth->fetch()) { | |
38 | ||
39 | $login = $row["login"]; | |
40 | $access_level = $row["access_level"]; | |
41 | $email = $row["email"]; | |
42 | ||
43 | $sel_disabled = ($id == $_SESSION["uid"] || $login == "admin") ? "disabled" : ""; | |
44 | ||
45 | print "<div class=\"dlgSec\">".__("User")."</div>"; | |
46 | print "<div class=\"dlgSecCont\">"; | |
47 | ||
48 | if ($sel_disabled) { | |
49 | print_hidden("login", "$login"); | |
50 | } | |
51 | ||
52 | print "<input size=\"30\" style=\"font-size : 16px\" | |
53 | dojoType=\"dijit.form.ValidationTextBox\" required=\"1\" | |
54 | $sel_disabled | |
55 | name=\"login\" value=\"$login\">"; | |
56 | ||
57 | print "</div>"; | |
58 | ||
59 | print "<div class=\"dlgSec\">".__("Authentication")."</div>"; | |
60 | print "<div class=\"dlgSecCont\">"; | |
61 | ||
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 | } | |
72 | ||
73 | print "<hr/>"; | |
74 | ||
75 | print "<input dojoType=\"dijit.form.TextBox\" type=\"password\" size=\"20\" placeholder=\"Change password\" | |
76 | name=\"password\">"; | |
77 | ||
78 | print "</div>"; | |
79 | ||
80 | print "<div class=\"dlgSec\">".__("Options")."</div>"; | |
81 | print "<div class=\"dlgSecCont\">"; | |
82 | ||
83 | print "<input dojoType=\"dijit.form.TextBox\" size=\"30\" name=\"email\" placeholder=\"E-mail\" | |
84 | value=\"$email\">"; | |
85 | ||
86 | print "</div>"; | |
87 | ||
88 | print "</table>"; | |
89 | ||
90 | print "</form>"; | |
91 | ||
92 | } | |
93 | ||
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 | ||
101 | print "<div class=\"dlgButtons\"> | |
102 | <button dojoType=\"dijit.form.Button\" type=\"submit\">". | |
103 | __('Save')."</button> | |
104 | <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('userEditDlg').hide()\">". | |
105 | __('Cancel')."</button></div>"; | |
106 | ||
107 | return; | |
108 | } | |
109 | ||
110 | function userdetails() { | |
111 | $id = (int) clean($_REQUEST["id"]); | |
112 | ||
113 | $sth = $this->pdo->prepare("SELECT login, | |
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 | |
120 | WHERE id = ?"); | |
121 | $sth->execute([$id]); | |
122 | ||
123 | if ($row = $sth->fetch()) { | |
124 | print "<table width='100%'>"; | |
125 | ||
126 | $last_login = make_local_datetime( | |
127 | $row["last_login"], true); | |
128 | ||
129 | $created = make_local_datetime( | |
130 | $row["created"], true); | |
131 | ||
132 | $stored_articles = $row["stored_articles"]; | |
133 | ||
134 | print "<tr><td>".__('Registered')."</td><td>$created</td></tr>"; | |
135 | print "<tr><td>".__('Last logged in')."</td><td>$last_login</td></tr>"; | |
136 | ||
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"]; | |
142 | ||
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>"; | |
145 | ||
146 | print "</table>"; | |
147 | ||
148 | print "<h1>".__('Subscribed feeds')."</h1>"; | |
149 | ||
150 | $sth = $this->pdo->prepare("SELECT id,title,site_url FROM ttrss_feeds | |
151 | WHERE owner_uid = ? ORDER BY title"); | |
152 | $sth->execute([$id]); | |
153 | ||
154 | print "<ul class=\"userFeedList\">"; | |
155 | ||
156 | while ($line = $sth->fetch()) { | |
157 | ||
158 | $icon_file = ICONS_URL."/".$line["id"].".ico"; | |
159 | ||
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 | } | |
165 | ||
166 | print "<li>$feed_icon <a href=\"".$line["site_url"]."\">".$line["title"]."</a></li>"; | |
167 | ||
168 | } | |
169 | ||
170 | print "</ul>"; | |
171 | ||
172 | ||
173 | } else { | |
174 | print "<h1>".__('User not found')."</h1>"; | |
175 | } | |
176 | ||
177 | } | |
178 | ||
179 | function editSave() { | |
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"]); | |
185 | ||
186 | if ($password) { | |
187 | $salt = substr(bin2hex(get_random_bytes(125)), 0, 250); | |
188 | $pwd_hash = encrypt_password($password, $salt, true); | |
189 | $pass_query_part = "pwd_hash = ".$this->pdo->quote($pwd_hash).", | |
190 | salt = ".$this->pdo->quote($salt).","; | |
191 | } else { | |
192 | $pass_query_part = ""; | |
193 | } | |
194 | ||
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]); | |
198 | ||
199 | } | |
200 | ||
201 | function remove() { | |
202 | $ids = explode(",", clean($_REQUEST["ids"])); | |
203 | ||
204 | foreach ($ids as $id) { | |
205 | if ($id != $_SESSION["uid"] && $id != 1) { | |
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]); | |
214 | } | |
215 | } | |
216 | } | |
217 | ||
218 | function add() { | |
219 | ||
220 | $login = trim(clean($_REQUEST["login"])); | |
221 | $tmp_user_pwd = make_password(8); | |
222 | $salt = substr(bin2hex(get_random_bytes(125)), 0, 250); | |
223 | $pwd_hash = encrypt_password($tmp_user_pwd, $salt, true); | |
224 | ||
225 | $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE | |
226 | login = ?"); | |
227 | $sth->execute([$login]); | |
228 | ||
229 | if (!$sth->fetch()) { | |
230 | ||
231 | $sth = $this->pdo->prepare("INSERT INTO ttrss_users | |
232 | (login,pwd_hash,access_level,last_login,created, salt) | |
233 | VALUES (?, ?, 0, null, NOW(), ?)"); | |
234 | $sth->execute([$login, $pwd_hash, $salt]); | |
235 | ||
236 | $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE | |
237 | login = ? AND pwd_hash = ?"); | |
238 | $sth->execute([$login, $pwd_hash]); | |
239 | ||
240 | if ($row = $sth->fetch()) { | |
241 | ||
242 | $new_uid = $row['id']; | |
243 | ||
244 | print format_notice(T_sprintf("Added user <b>%s</b> with password <b>%s</b>", | |
245 | $login, $tmp_user_pwd)); | |
246 | ||
247 | initialize_user($new_uid); | |
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 | ||
259 | static function resetUserPassword($uid, $show_password) { | |
260 | ||
261 | $pdo = Db::pdo(); | |
262 | ||
263 | $sth = $pdo->prepare("SELECT login, email | |
264 | FROM ttrss_users WHERE id = ?"); | |
265 | $sth->execute([$uid]); | |
266 | ||
267 | if ($row = $sth->fetch()) { | |
268 | ||
269 | $login = $row["login"]; | |
270 | $email = $row["email"]; | |
271 | ||
272 | $new_salt = substr(bin2hex(get_random_bytes(125)), 0, 250); | |
273 | $tmp_user_pwd = make_password(8); | |
274 | ||
275 | $pwd_hash = encrypt_password($tmp_user_pwd, $new_salt, true); | |
276 | ||
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]); | |
281 | ||
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'; | |
289 | ||
290 | if ($email) { | |
291 | require_once "lib/MiniTemplator.class.php"; | |
292 | ||
293 | $tpl = new MiniTemplator; | |
294 | ||
295 | $tpl->readTemplateFromFile("templates/resetpass_template.txt"); | |
296 | ||
297 | $tpl->setVariable('LOGIN', $login); | |
298 | $tpl->setVariable('NEWPASS', $tmp_user_pwd); | |
299 | ||
300 | $tpl->addBlock('message'); | |
301 | ||
302 | $message = ""; | |
303 | ||
304 | $tpl->generateOutputToString($message); | |
305 | ||
306 | $mail = new ttrssMailer(); | |
307 | ||
308 | $rc = $mail->quickMail($email, $login, | |
309 | __("[tt-rss] Password change notification"), | |
310 | $message, false); | |
311 | ||
312 | if (!$rc) print_error($mail->ErrorInfo); | |
313 | } | |
314 | ||
315 | } | |
316 | } | |
317 | ||
318 | function resetPass() { | |
319 | $uid = clean($_REQUEST["id"]); | |
320 | Pref_Users::resetUserPassword($uid, true); | |
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 | ||
332 | $user_search = trim(clean($_REQUEST["search"])); | |
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\"> | |
343 | <button dojoType=\"dijit.form.Button\" onclick=\"updateUsersList()\">". | |
344 | __('Search')."</button> | |
345 | </div>"; | |
346 | ||
347 | $sort = clean($_REQUEST["sort"]); | |
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 | ||
362 | print "<button dojoType=\"dijit.form.Button\" onclick=\"addUser()\">".__('Create user')."</button>"; | |
363 | ||
364 | print " | |
365 | <button dojoType=\"dijit.form.Button\" onclick=\"editSelectedUser()\">". | |
366 | __('Edit')."</button dojoType=\"dijit.form.Button\"> | |
367 | <button dojoType=\"dijit.form.Button\" onclick=\"removeSelectedUsers()\">". | |
368 | __('Remove')."</button dojoType=\"dijit.form.Button\"> | |
369 | <button dojoType=\"dijit.form.Button\" onclick=\"resetSelectedUserPass()\">". | |
370 | __('Reset password')."</button dojoType=\"dijit.form.Button\">"; | |
371 | ||
372 | PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, | |
373 | "hook_prefs_tab_section", "prefUsersToolbar"); | |
374 | ||
375 | print "</div>"; #toolbar | |
376 | print "</div>"; #pane | |
377 | print "<div id=\"pref-user-content\" dojoType=\"dijit.layout.ContentPane\" region=\"center\">"; | |
378 | ||
379 | $sort = validate_field($sort, | |
380 | ["login", "access_level", "created", "num_feeds", "created", "last_login"], "login"); | |
381 | ||
382 | if ($sort != "login") $sort = "$sort DESC"; | |
383 | ||
384 | $sth = $this->pdo->prepare("SELECT | |
385 | tu.id, | |
386 | login,access_level,email, | |
387 | ".SUBSTRING_FOR_DATE."(last_login,1,16) as last_login, | |
388 | ".SUBSTRING_FOR_DATE."(created,1,16) as created, | |
389 | (SELECT COUNT(id) FROM ttrss_feeds WHERE owner_uid = tu.id) AS num_feeds | |
390 | FROM | |
391 | ttrss_users tu | |
392 | WHERE | |
393 | (:search = '' OR login LIKE :search) AND tu.id > 0 | |
394 | ORDER BY $sort"); | |
395 | $sth->execute([":search" => $user_search ? "%$user_search%" : ""]); | |
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> | |
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> | |
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 | ||
410 | while ($line = $sth->fetch()) { | |
411 | ||
412 | $uid = $line["id"]; | |
413 | ||
414 | print "<tr id=\"UMRR-$uid\">"; | |
415 | ||
416 | $line["login"] = htmlspecialchars($line["login"]); | |
417 | ||
418 | $line["created"] = make_local_datetime($line["created"], false); | |
419 | $line["last_login"] = make_local_datetime($line["last_login"], false); | |
420 | ||
421 | print "<td align='center'><input onclick='toggleSelectRow2(this);' | |
422 | dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" | |
423 | id=\"UMCHK-$uid\"></td>"; | |
424 | ||
425 | $onclick = "onclick='editUser($uid, event)' title='".__('Click to edit')."'"; | |
426 | ||
427 | print "<td $onclick><img src='images/user.png' class='markedPic' alt=''> " . $line["login"] . "</td>"; | |
428 | ||
429 | if (!$line["email"]) $line["email"] = " "; | |
430 | ||
431 | print "<td $onclick>" . $access_level_names[$line["access_level"]] . "</td>"; | |
432 | print "<td $onclick>" . $line["num_feeds"] . "</td>"; | |
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 | ||
443 | if ($lnum == 0) { | |
444 | if (!$user_search) { | |
445 | print_warning(__('No users defined.')); | |
446 | } else { | |
447 | print_warning(__('No matching users found.')); | |
448 | } | |
449 | } | |
450 | ||
451 | print "</div>"; #pane | |
452 | ||
453 | PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB, | |
454 | "hook_prefs_tab", "prefUsers"); | |
455 | ||
456 | print "</div>"; #container | |
457 | ||
458 | } | |
459 | } |