mirror of
https://tt-rss.org/git/tt-rss.git
synced 2024-06-22 11:36:38 +02:00
This change introduces derived classes for ComboButton, DropDownButton and Select that make sure that buttons do not remain focused after their menus are closed. This allows using hotkeys after closing them.
439 lines
13 KiB
PHP
439 lines
13 KiB
PHP
<?php
|
|
class Pref_Users extends Handler_Protected {
|
|
function before($method) {
|
|
if (parent::before($method)) {
|
|
if ($_SESSION["access_level"] < 10) {
|
|
print __("Your access level is insufficient to open this tab.");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function csrf_ignore($method) {
|
|
$csrf_ignored = array("index", "edit", "userdetails");
|
|
|
|
return array_search($method, $csrf_ignored) !== false;
|
|
}
|
|
|
|
function edit() {
|
|
global $access_level_names;
|
|
|
|
print "<form id='user_edit_form' onsubmit='return false' dojoType='dijit.form.Form'>";
|
|
|
|
print '<div dojoType="dijit.layout.TabContainer" style="height : 400px">
|
|
<div dojoType="dijit.layout.ContentPane" title="'.__('Edit user').'">';
|
|
|
|
//print "<form id=\"user_edit_form\" onsubmit='return false' dojoType=\"dijit.form.Form\">";
|
|
|
|
$id = (int) clean($_REQUEST["id"]);
|
|
|
|
print_hidden("id", "$id");
|
|
print_hidden("op", "pref-users");
|
|
print_hidden("method", "editSave");
|
|
|
|
$sth = $this->pdo->prepare("SELECT * FROM ttrss_users WHERE id = ?");
|
|
$sth->execute([$id]);
|
|
|
|
if ($row = $sth->fetch()) {
|
|
|
|
$login = $row["login"];
|
|
$access_level = $row["access_level"];
|
|
$email = $row["email"];
|
|
|
|
$sel_disabled = ($id == $_SESSION["uid"] || $login == "admin") ? "disabled" : "";
|
|
|
|
print "<header>".__("User")."</header>";
|
|
print "<section>";
|
|
|
|
if ($sel_disabled) {
|
|
print_hidden("login", "$login");
|
|
}
|
|
|
|
print "<fieldset>";
|
|
print "<label>" . __("Login:") . "</label>";
|
|
print "<input style='font-size : 16px'
|
|
dojoType='dijit.form.ValidationTextBox' required='1'
|
|
$sel_disabled name='login' value=\"$login\">";
|
|
print "</fieldset>";
|
|
|
|
print "</section>";
|
|
|
|
print "<header>".__("Authentication")."</header>";
|
|
print "<section>";
|
|
|
|
print "<fieldset>";
|
|
|
|
print "<label>" . __('Access level: ') . "</label> ";
|
|
|
|
if (!$sel_disabled) {
|
|
print_select_hash("access_level", $access_level, $access_level_names,
|
|
"dojoType=\"fox.form.Select\" $sel_disabled");
|
|
} else {
|
|
print_select_hash("", $access_level, $access_level_names,
|
|
"dojoType=\"fox.form.Select\" $sel_disabled");
|
|
print_hidden("access_level", "$access_level");
|
|
}
|
|
|
|
print "</fieldset>";
|
|
print "<fieldset>";
|
|
|
|
print "<label>" . __("New password:") . "</label> ";
|
|
print "<input dojoType='dijit.form.TextBox' type='password' size='20' placeholder='Change password'
|
|
name='password'>";
|
|
|
|
print "</fieldset>";
|
|
|
|
print "</section>";
|
|
|
|
print "<header>".__("Options")."</header>";
|
|
print "<section>";
|
|
|
|
print "<fieldset>";
|
|
print "<label>" . __("E-mail:") . "</label> ";
|
|
print "<input dojoType='dijit.form.TextBox' size='30' name='email'
|
|
value=\"$email\">";
|
|
print "</fieldset>";
|
|
|
|
print "</section>";
|
|
|
|
print "</table>";
|
|
|
|
}
|
|
|
|
print '</div>'; #tab
|
|
print "<div href=\"backend.php?op=pref-users&method=userdetails&id=$id\"
|
|
dojoType=\"dijit.layout.ContentPane\" title=\"".__('User details')."\">";
|
|
|
|
print '</div>';
|
|
print '</div>';
|
|
|
|
print "<footer>
|
|
<button dojoType='dijit.form.Button' class='alt-primary' type='submit' onclick=\"dijit.byId('userEditDlg').execute()\">".
|
|
__('Save')."</button>
|
|
<button dojoType='dijit.form.Button' onclick=\"dijit.byId('userEditDlg').hide()\">".
|
|
__('Cancel')."</button>
|
|
</footer>";
|
|
|
|
print "</form>";
|
|
|
|
return;
|
|
}
|
|
|
|
function userdetails() {
|
|
$id = (int) clean($_REQUEST["id"]);
|
|
|
|
$sth = $this->pdo->prepare("SELECT login,
|
|
".SUBSTRING_FOR_DATE."(last_login,1,16) AS last_login,
|
|
access_level,
|
|
(SELECT COUNT(int_id) FROM ttrss_user_entries
|
|
WHERE owner_uid = id) AS stored_articles,
|
|
".SUBSTRING_FOR_DATE."(created,1,16) AS created
|
|
FROM ttrss_users
|
|
WHERE id = ?");
|
|
$sth->execute([$id]);
|
|
|
|
if ($row = $sth->fetch()) {
|
|
print "<table width='100%'>";
|
|
|
|
$last_login = make_local_datetime(
|
|
$row["last_login"], true);
|
|
|
|
$created = make_local_datetime(
|
|
$row["created"], true);
|
|
|
|
$stored_articles = $row["stored_articles"];
|
|
|
|
print "<tr><td>".__('Registered')."</td><td>$created</td></tr>";
|
|
print "<tr><td>".__('Last logged in')."</td><td>$last_login</td></tr>";
|
|
|
|
$sth = $this->pdo->prepare("SELECT COUNT(id) as num_feeds FROM ttrss_feeds
|
|
WHERE owner_uid = ?");
|
|
$sth->execute([$id]);
|
|
$row = $sth->fetch();
|
|
$num_feeds = $row["num_feeds"];
|
|
|
|
print "<tr><td>".__('Subscribed feeds count')."</td><td>$num_feeds</td></tr>";
|
|
print "<tr><td>".__('Stored articles')."</td><td>$stored_articles</td></tr>";
|
|
|
|
print "</table>";
|
|
|
|
print "<h1>".__('Subscribed feeds')."</h1>";
|
|
|
|
$sth = $this->pdo->prepare("SELECT id,title,site_url FROM ttrss_feeds
|
|
WHERE owner_uid = ? ORDER BY title");
|
|
$sth->execute([$id]);
|
|
|
|
print "<ul class=\"panel panel-scrollable list list-unstyled\">";
|
|
|
|
while ($line = $sth->fetch()) {
|
|
|
|
$icon_file = ICONS_URL."/".$line["id"].".ico";
|
|
|
|
if (file_exists($icon_file) && filesize($icon_file) > 0) {
|
|
$feed_icon = "<img class=\"icon\" src=\"$icon_file\">";
|
|
} else {
|
|
$feed_icon = "<img class=\"icon\" src=\"images/blank_icon.gif\">";
|
|
}
|
|
|
|
print "<li>$feed_icon <a href=\"".$line["site_url"]."\">".$line["title"]."</a></li>";
|
|
|
|
}
|
|
|
|
print "</ul>";
|
|
|
|
|
|
} else {
|
|
print "<h1>".__('User not found')."</h1>";
|
|
}
|
|
|
|
}
|
|
|
|
function editSave() {
|
|
$login = trim(clean($_REQUEST["login"]));
|
|
$uid = clean($_REQUEST["id"]);
|
|
$access_level = (int) clean($_REQUEST["access_level"]);
|
|
$email = trim(clean($_REQUEST["email"]));
|
|
$password = clean($_REQUEST["password"]);
|
|
|
|
if ($password) {
|
|
$salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
|
|
$pwd_hash = encrypt_password($password, $salt, true);
|
|
$pass_query_part = "pwd_hash = ".$this->pdo->quote($pwd_hash).",
|
|
salt = ".$this->pdo->quote($salt).",";
|
|
} else {
|
|
$pass_query_part = "";
|
|
}
|
|
|
|
$sth = $this->pdo->prepare("UPDATE ttrss_users SET $pass_query_part login = ?,
|
|
access_level = ?, email = ?, otp_enabled = false WHERE id = ?");
|
|
$sth->execute([$login, $access_level, $email, $uid]);
|
|
|
|
}
|
|
|
|
function remove() {
|
|
$ids = explode(",", clean($_REQUEST["ids"]));
|
|
|
|
foreach ($ids as $id) {
|
|
if ($id != $_SESSION["uid"] && $id != 1) {
|
|
$sth = $this->pdo->prepare("DELETE FROM ttrss_tags WHERE owner_uid = ?");
|
|
$sth->execute([$id]);
|
|
|
|
$sth = $this->pdo->prepare("DELETE FROM ttrss_feeds WHERE owner_uid = ?");
|
|
$sth->execute([$id]);
|
|
|
|
$sth = $this->pdo->prepare("DELETE FROM ttrss_users WHERE id = ?");
|
|
$sth->execute([$id]);
|
|
}
|
|
}
|
|
}
|
|
|
|
function add() {
|
|
$login = trim(clean($_REQUEST["login"]));
|
|
$tmp_user_pwd = make_password();
|
|
$salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
|
|
$pwd_hash = encrypt_password($tmp_user_pwd, $salt, true);
|
|
|
|
if (!$login) return; // no blank usernames
|
|
|
|
$sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE
|
|
login = ?");
|
|
$sth->execute([$login]);
|
|
|
|
if (!$sth->fetch()) {
|
|
|
|
$sth = $this->pdo->prepare("INSERT INTO ttrss_users
|
|
(login,pwd_hash,access_level,last_login,created, salt)
|
|
VALUES (?, ?, 0, null, NOW(), ?)");
|
|
$sth->execute([$login, $pwd_hash, $salt]);
|
|
|
|
$sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE
|
|
login = ? AND pwd_hash = ?");
|
|
$sth->execute([$login, $pwd_hash]);
|
|
|
|
if ($row = $sth->fetch()) {
|
|
|
|
$new_uid = $row['id'];
|
|
|
|
print T_sprintf("Added user %s with password %s",
|
|
$login, $tmp_user_pwd);
|
|
|
|
initialize_user($new_uid);
|
|
|
|
} else {
|
|
|
|
print T_sprintf("Could not create user %s", $login);
|
|
|
|
}
|
|
} else {
|
|
print T_sprintf("User %s already exists.", $login);
|
|
}
|
|
}
|
|
|
|
static function resetUserPassword($uid, $format_output = false) {
|
|
|
|
$pdo = Db::pdo();
|
|
|
|
$sth = $pdo->prepare("SELECT login FROM ttrss_users WHERE id = ?");
|
|
$sth->execute([$uid]);
|
|
|
|
if ($row = $sth->fetch()) {
|
|
|
|
$login = $row["login"];
|
|
|
|
$new_salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
|
|
$tmp_user_pwd = make_password();
|
|
|
|
$pwd_hash = encrypt_password($tmp_user_pwd, $new_salt, true);
|
|
|
|
$sth = $pdo->prepare("UPDATE ttrss_users
|
|
SET pwd_hash = ?, salt = ?, otp_enabled = false
|
|
WHERE id = ?");
|
|
$sth->execute([$pwd_hash, $new_salt, $uid]);
|
|
|
|
$message = T_sprintf("Changed password of user %s to %s", "<strong>$login</strong>", "<strong>$tmp_user_pwd</strong>");
|
|
|
|
if ($format_output)
|
|
print_notice($message);
|
|
else
|
|
print $message;
|
|
|
|
}
|
|
}
|
|
|
|
function resetPass() {
|
|
$uid = clean($_REQUEST["id"]);
|
|
Pref_Users::resetUserPassword($uid);
|
|
}
|
|
|
|
function index() {
|
|
|
|
global $access_level_names;
|
|
|
|
print "<div dojoType='dijit.layout.BorderContainer' gutters='false'>";
|
|
print "<div style='padding : 0px' dojoType='dijit.layout.ContentPane' region='top'>";
|
|
print "<div dojoType='fox.Toolbar'>";
|
|
|
|
$user_search = trim(clean($_REQUEST["search"]));
|
|
|
|
if (array_key_exists("search", $_REQUEST)) {
|
|
$_SESSION["prefs_user_search"] = $user_search;
|
|
} else {
|
|
$user_search = $_SESSION["prefs_user_search"];
|
|
}
|
|
|
|
print "<div style='float : right; padding-right : 4px;'>
|
|
<input dojoType='dijit.form.TextBox' id='user_search' size='20' type='search'
|
|
value=\"$user_search\">
|
|
<button dojoType='dijit.form.Button' onclick='Users.reload()'>".
|
|
__('Search')."</button>
|
|
</div>";
|
|
|
|
$sort = clean($_REQUEST["sort"]);
|
|
|
|
if (!$sort || $sort == "undefined") {
|
|
$sort = "login";
|
|
}
|
|
|
|
print "<div dojoType='fox.form.DropDownButton'>".
|
|
"<span>" . __('Select')."</span>";
|
|
print "<div dojoType='dijit.Menu' style='display: none'>";
|
|
print "<div onclick=\"Tables.select('prefUserList', true)\"
|
|
dojoType='dijit.MenuItem'>".__('All')."</div>";
|
|
print "<div onclick=\"Tables.select('prefUserList', false)\"
|
|
dojoType='dijit.MenuItem'>".__('None')."</div>";
|
|
print "</div></div>";
|
|
|
|
print "<button dojoType='dijit.form.Button' onclick='Users.add()'>".__('Create user')."</button>";
|
|
|
|
print "
|
|
<button dojoType='dijit.form.Button' onclick='Users.editSelected()'>".
|
|
__('Edit')."</button dojoType=\"dijit.form.Button\">
|
|
<button dojoType='dijit.form.Button' onclick='Users.removeSelected()'>".
|
|
__('Remove')."</button dojoType=\"dijit.form.Button\">
|
|
<button dojoType='dijit.form.Button' onclick='Users.resetSelected()'>".
|
|
__('Reset password')."</button dojoType=\"dijit.form.Button\">";
|
|
|
|
PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION,
|
|
"hook_prefs_tab_section", "prefUsersToolbar");
|
|
|
|
print "</div>"; #toolbar
|
|
print "</div>"; #pane
|
|
print "<div style='padding : 0px' dojoType='dijit.layout.ContentPane' region='center'>";
|
|
|
|
$sort = validate_field($sort,
|
|
["login", "access_level", "created", "num_feeds", "created", "last_login"], "login");
|
|
|
|
if ($sort != "login") $sort = "$sort DESC";
|
|
|
|
$sth = $this->pdo->prepare("SELECT
|
|
tu.id,
|
|
login,access_level,email,
|
|
".SUBSTRING_FOR_DATE."(last_login,1,16) as last_login,
|
|
".SUBSTRING_FOR_DATE."(created,1,16) as created,
|
|
(SELECT COUNT(id) FROM ttrss_feeds WHERE owner_uid = tu.id) AS num_feeds
|
|
FROM
|
|
ttrss_users tu
|
|
WHERE
|
|
(:search = '' OR login LIKE :search) AND tu.id > 0
|
|
ORDER BY $sort");
|
|
$sth->execute([":search" => $user_search ? "%$user_search%" : ""]);
|
|
|
|
print "<p><table width='100%' cellspacing='0' class='prefUserList' id='prefUserList'>";
|
|
|
|
print "<tr class='title'>
|
|
<td align='center' width='5%'> </td>
|
|
<td width='20%'><a href='#' onclick=\"Users.reload('login')\">".__('Login')."</a></td>
|
|
<td width='20%'><a href='#' onclick=\"Users.reload('access_level')\">".__('Access Level')."</a></td>
|
|
<td width='10%'><a href='#' onclick=\"Users.reload('num_feeds')\">".__('Subscribed feeds')."</a></td>
|
|
<td width='20%'><a href='#' onclick=\"Users.reload('created')\">".__('Registered')."</a></td>
|
|
<td width='20%'><a href='#' onclick=\"Users.reload('last_login')\">".__('Last login')."</a></td></tr>";
|
|
|
|
$lnum = 0;
|
|
|
|
while ($line = $sth->fetch()) {
|
|
|
|
$uid = $line["id"];
|
|
|
|
print "<tr data-row-id='$uid' onclick='Users.edit($uid)'>";
|
|
|
|
$line["login"] = htmlspecialchars($line["login"]);
|
|
$line["created"] = make_local_datetime($line["created"], false);
|
|
$line["last_login"] = make_local_datetime($line["last_login"], false);
|
|
|
|
print "<td align='center'><input onclick='Tables.onRowChecked(this); event.stopPropagation();'
|
|
dojoType='dijit.form.CheckBox' type='checkbox'></td>";
|
|
|
|
print "<td title='".__('Click to edit')."'><i class='material-icons'>person</i> " . $line["login"] . "</td>";
|
|
|
|
print "<td>" . $access_level_names[$line["access_level"]] . "</td>";
|
|
print "<td>" . $line["num_feeds"] . "</td>";
|
|
print "<td>" . $line["created"] . "</td>";
|
|
print "<td>" . $line["last_login"] . "</td>";
|
|
|
|
print "</tr>";
|
|
|
|
++$lnum;
|
|
}
|
|
|
|
print "</table>";
|
|
|
|
if ($lnum == 0) {
|
|
if (!$user_search) {
|
|
print_warning(__('No users defined.'));
|
|
} else {
|
|
print_warning(__('No matching users found.'));
|
|
}
|
|
}
|
|
|
|
print "</div>"; #pane
|
|
|
|
PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB,
|
|
"hook_prefs_tab", "prefUsers");
|
|
|
|
print "</div>"; #container
|
|
|
|
}
|
|
}
|