pref_item_map = [
__('General') => [
'USER_LANGUAGE',
'USER_TIMEZONE',
'BLOCK_SEPARATOR',
'USER_CSS_THEME',
'BLOCK_SEPARATOR',
'ENABLE_API_ACCESS',
],
__('Feeds') => [
'DEFAULT_UPDATE_INTERVAL',
'FRESH_ARTICLE_MAX_AGE',
'DEFAULT_SEARCH_LANGUAGE',
'BLOCK_SEPARATOR',
'ENABLE_FEED_CATS',
'BLOCK_SEPARATOR',
'CONFIRM_FEED_CATCHUP',
'ON_CATCHUP_SHOW_NEXT_FEED',
'BLOCK_SEPARATOR',
'HIDE_READ_FEEDS',
'HIDE_READ_SHOWS_SPECIAL',
],
__('Articles') => [
'PURGE_OLD_DAYS',
'PURGE_UNREAD_ARTICLES',
'BLOCK_SEPARATOR',
'COMBINED_DISPLAY_MODE',
'CDM_EXPANDED',
'BLOCK_SEPARATOR',
'CDM_AUTO_CATCHUP',
'VFEED_GROUP_BY_FEED',
'BLOCK_SEPARATOR',
'SHOW_CONTENT_PREVIEW',
'STRIP_IMAGES',
],
__('Digest') => [
'DIGEST_ENABLE',
'DIGEST_CATCHUP',
'DIGEST_PREFERRED_TIME',
],
__('Advanced') => [
'BLACKLISTED_TAGS',
'BLOCK_SEPARATOR',
'LONG_DATE_FORMAT',
'SHORT_DATE_FORMAT',
'BLOCK_SEPARATOR',
'SSL_CERT_SERIAL',
]
];
$this->pref_help_bottom = [
"BLACKLISTED_TAGS" => __("Never apply these tags automatically (comma-separated list)."),
];
$this->pref_help = [
"ALLOW_DUPLICATE_POSTS" => array(__("Allow duplicate articles"), ""),
"BLACKLISTED_TAGS" => array(__("Blacklisted tags"), ""),
"DEFAULT_SEARCH_LANGUAGE" => array(__("Default language"), __("Used for full-text search")),
"CDM_AUTO_CATCHUP" => array(__("Mark read on scroll"), __("Mark articles as read as you scroll past them")),
"CDM_EXPANDED" => array(__("Always expand articles")),
"COMBINED_DISPLAY_MODE" => array(__("Combined mode"), __("Show flat list of articles instead of separate panels")),
"CONFIRM_FEED_CATCHUP" => array(__("Confirm marking feeds as read")),
"DEFAULT_ARTICLE_LIMIT" => array(__("Amount of articles to display at once")),
"DEFAULT_UPDATE_INTERVAL" => array(__("Default update interval")),
"DIGEST_CATCHUP" => array(__("Mark sent articles as read")),
"DIGEST_ENABLE" => array(__("Enable digest"), __("Send daily digest of new (and unread) headlines to your e-mail address")),
"DIGEST_PREFERRED_TIME" => array(__("Try to send around this time"), __("Time in UTC")),
"ENABLE_API_ACCESS" => array(__("Enable API"), __("Allows accessing this account through the API")),
"ENABLE_FEED_CATS" => array(__("Enable categories")),
"FEEDS_SORT_BY_UNREAD" => array(__("Sort feeds by unread articles count"), ""),
"FRESH_ARTICLE_MAX_AGE" => array(__("Maximum age of fresh articles"), "" . __("hours") . ""),
"HIDE_READ_FEEDS" => array(__("Hide read feeds")),
"HIDE_READ_SHOWS_SPECIAL" => array(__("Always show special feeds"), __("While hiding read feeds")),
"LONG_DATE_FORMAT" => array(__("Long date format"), __("Syntax is identical to PHP date() function.")),
"ON_CATCHUP_SHOW_NEXT_FEED" => array(__("Automatically show next feed"), __("After marking one as read")),
"PURGE_OLD_DAYS" => array(__("Purge articles older than"), __("days (0 disables)")),
"PURGE_UNREAD_ARTICLES" => array(__("Purge unread articles")),
"REVERSE_HEADLINES" => array(__("Reverse headline order (oldest first)")),
"SHORT_DATE_FORMAT" => array(__("Short date format")),
"SHOW_CONTENT_PREVIEW" => array(__("Show content preview in headlines")),
"SORT_HEADLINES_BY_FEED_DATE" => array(__("Sort headlines by feed date"), __("Use feed-specified date to sort headlines instead of local import date.")),
"SSL_CERT_SERIAL" => array(__("SSL client certificate")),
"STRIP_IMAGES" => array(__("Do not embed media")),
"STRIP_UNSAFE_TAGS" => array(__("Strip unsafe tags from articles"), __("Strip all but most common HTML tags when reading articles.")),
"USER_STYLESHEET" => array(__("Customize stylesheet")),
"USER_TIMEZONE" => array(__("Time zone")),
"VFEED_GROUP_BY_FEED" => array(__("Group by feed"), __("Group multiple-feed output by originating feed")),
"USER_LANGUAGE" => array(__("Language")),
"USER_CSS_THEME" => array(__("Theme"))
];
$this->pref_blacklist = ["ALLOW_DUPLICATE_POSTS", "STRIP_UNSAFE_TAGS", "REVERSE_HEADLINES",
"SORT_HEADLINES_BY_FEED_DATE", "DEFAULT_ARTICLE_LIMIT",
"FEEDS_SORT_BY_UNREAD", "USER_STYLESHEET"];
/* "FEEDS_SORT_BY_UNREAD", "HIDE_READ_FEEDS", "REVERSE_HEADLINES" */
$this->profile_blacklist = ["ALLOW_DUPLICATE_POSTS", "PURGE_OLD_DAYS",
"PURGE_UNREAD_ARTICLES", "DIGEST_ENABLE", "DIGEST_CATCHUP",
"BLACKLISTED_TAGS", "ENABLE_API_ACCESS", "UPDATE_POST_ON_CHECKSUM_CHANGE",
"DEFAULT_UPDATE_INTERVAL", "USER_TIMEZONE", "SORT_HEADLINES_BY_FEED_DATE",
"SSL_CERT_SERIAL", "DIGEST_PREFERRED_TIME"];
}
function changepassword() {
if (Config::get(Config::FORBID_PASSWORD_CHANGES)) {
print "ERROR: ".format_error("Access forbidden.");
return;
}
$old_pw = clean($_POST["old_password"]);
$new_pw = clean($_POST["new_password"]);
$new_unclean_pw = $_POST["new_password"];
$con_pw = clean($_POST["confirm_password"]);
if ($new_unclean_pw != $new_pw) {
print "ERROR: ".format_error("New password contains disallowed characters.");
return;
}
if ($old_pw == $new_pw) {
print "ERROR: ".format_error("New password must be different from the old one.");
return;
}
if ($old_pw == "") {
print "ERROR: ".format_error("Old password cannot be blank.");
return;
}
if ($new_pw == "") {
print "ERROR: ".format_error("New password cannot be blank.");
return;
}
if ($new_pw != $con_pw) {
print "ERROR: ".format_error("Entered passwords do not match.");
return;
}
$authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]);
if (method_exists($authenticator, "change_password")) {
print format_notice($authenticator->change_password($_SESSION["uid"], $old_pw, $new_pw));
} else {
print "ERROR: ".format_error("Function not supported by authentication module.");
}
}
function saveconfig() {
$boolean_prefs = explode(",", clean($_POST["boolean_prefs"]));
foreach ($boolean_prefs as $pref) {
if (!isset($_POST[$pref])) $_POST[$pref] = 'false';
}
$need_reload = false;
foreach (array_keys($_POST) as $pref_name) {
$value = $_POST[$pref_name];
switch ($pref_name) {
case 'DIGEST_PREFERRED_TIME':
if (get_pref('DIGEST_PREFERRED_TIME') != $value) {
$sth = $this->pdo->prepare("UPDATE ttrss_users SET
last_digest_sent = NULL WHERE id = ?");
$sth->execute([$_SESSION['uid']]);
}
break;
case 'USER_LANGUAGE':
if (!$need_reload) $need_reload = $_SESSION["language"] != $value;
break;
case 'USER_CSS_THEME':
if (!$need_reload) $need_reload = get_pref($pref_name) != $value;
break;
case 'BLACKLISTED_TAGS':
$cats = FeedItem_Common::normalize_categories(explode(",", $value));
asort($cats);
$value = implode(", ", $cats);
break;
}
if (Prefs::is_valid($pref_name)) {
Prefs::set($pref_name, $value, $_SESSION["uid"], $_SESSION["profile"] ?? null);
}
}
if ($need_reload) {
print "PREFS_NEED_RELOAD";
} else {
print __("The configuration was saved.");
}
}
function changeemail() {
$email = clean($_POST["email"]);
$full_name = clean($_POST["full_name"]);
$active_uid = $_SESSION["uid"];
$sth = $this->pdo->prepare("SELECT email, login, full_name FROM ttrss_users WHERE id = ?");
$sth->execute([$active_uid]);
if ($row = $sth->fetch()) {
$old_email = $row["email"];
if ($old_email != $email) {
$mailer = new Mailer();
$tpl = new Templator();
$tpl->readTemplateFromFile("mail_change_template.txt");
$tpl->setVariable('LOGIN', $row["login"]);
$tpl->setVariable('NEWMAIL', $email);
$tpl->setVariable('TTRSS_HOST', Config::get(Config::SELF_URL_PATH));
$tpl->addBlock('message');
$tpl->generateOutputToString($message);
$mailer->mail(["to_name" => $row["login"],
"to_address" => $row["email"],
"subject" => "[tt-rss] Mail address change notification",
"message" => $message]);
}
}
$sth = $this->pdo->prepare("UPDATE ttrss_users SET email = ?,
full_name = ? WHERE id = ?");
$sth->execute([$email, $full_name, $active_uid]);
print __("Your personal data has been saved.");
return;
}
function resetconfig() {
Prefs::reset($_SESSION["uid"], $_SESSION["profile"]);
print "PREFS_NEED_RELOAD";
}
private function index_auth_personal() {
$sth = $this->pdo->prepare("SELECT email,full_name,otp_enabled,
access_level FROM ttrss_users
WHERE id = ?");
$sth->execute([$_SESSION["uid"]]);
$row = $sth->fetch();
$email = htmlspecialchars($row["email"]);
$full_name = htmlspecialchars($row["full_name"]);
$otp_enabled = sql_bool_to_bool($row["otp_enabled"]);
?>
get_plugin($_SESSION["auth_module"]);
} else {
$authenticator = false;
}
$otp_enabled = $this->is_otp_enabled();
if ($authenticator && method_exists($authenticator, "change_password")) {
?>
%s) does not provide an ability to set passwords.",
$_SESSION["auth_module"]));
}
}
private function index_auth_app_passwords() {
print_notice("You can create separate passwords for API clients. Using one is required if you enable OTP.");
?>
appPasswordList() ?>
pdo->prepare("SELECT otp_enabled FROM ttrss_users
WHERE id = ?");
$sth->execute([$_SESSION["uid"]]);
if ($row = $sth->fetch()) {
return sql_bool_to_bool($row["otp_enabled"]);
}
return false;
}
private function index_auth_2fa() {
$otp_enabled = $this->is_otp_enabled();
if ($_SESSION["auth_module"] == "auth_internal") {
if ($otp_enabled) {
print_warning("One time passwords are currently enabled. Enter your current password below to disable.");
?>
" . __("Scan the following code by the Authenticator application or copy the key manually") . "";
$csrf_token_hash = sha1($_SESSION["csrf_token"]);
print "";
} else {
print_error("PHP GD functions are required to generate QR codes.");
print "
" . __("Use the following OTP key with a compatible Authenticator application") . "