extension ".__('Plugins')."\">";
-
- print "
+
+
+
+
+ = __("Loading, please wait...") ?>
+
+
+ index_prefs() ?>
+
+
+
+ = __("Loading, please wait...") ?>
+
+ run_hooks(PluginHost::HOOK_PREFS_TAB, "prefPrefs") ?>
+
+ readTemplateFromFile("otp_disabled_template.txt");
$tpl->setVariable('LOGIN', $row["login"]);
- $tpl->setVariable('TTRSS_HOST', SELF_URL_PATH);
+ $tpl->setVariable('TTRSS_HOST', Config::get(Config::SELF_URL_PATH));
$tpl->addBlock('message');
@@ -1171,112 +1180,107 @@ class Pref_Prefs extends Handler_Protected {
print json_encode(["value" => $value]);
}
- function editPrefProfiles() {
- print "
";
+ function activateprofile() {
+ $_SESSION["profile"] = (int) clean($_REQUEST["id"]);
- print "
".
- "
" . __('Select')." ";
- print "
";
- print "
".__('All')."
";
- print "
".__('None')."
";
- print "
";
+ // default value
+ if (!$_SESSION["profile"]) $_SESSION["profile"] = null;
+ }
- print "
";
+ function remprofiles() {
+ $ids = explode(",", clean($_REQUEST["ids"]));
- print "
- ".
- __('Create profile')."
";
+ foreach ($ids as $id) {
+ if ($_SESSION["profile"] != $id) {
+ $sth = $this->pdo->prepare("DELETE FROM ttrss_settings_profiles WHERE id = ? AND
+ owner_uid = ?");
+ $sth->execute([$id, $_SESSION['uid']]);
+ }
+ }
+ }
- print "
";
+ function addprofile() {
+ $title = clean($_REQUEST["title"]);
+
+ if ($title) {
+ $this->pdo->beginTransaction();
+
+ $sth = $this->pdo->prepare("SELECT id FROM ttrss_settings_profiles
+ WHERE title = ? AND owner_uid = ?");
+ $sth->execute([$title, $_SESSION['uid']]);
+
+ if (!$sth->fetch()) {
+
+ $sth = $this->pdo->prepare("INSERT INTO ttrss_settings_profiles (title, owner_uid)
+ VALUES (?, ?)");
+
+ $sth->execute([$title, $_SESSION['uid']]);
+
+ $sth = $this->pdo->prepare("SELECT id FROM ttrss_settings_profiles WHERE
+ title = ? AND owner_uid = ?");
+ $sth->execute([$title, $_SESSION['uid']]);
+
+ if ($row = $sth->fetch()) {
+ $profile_id = $row['id'];
+
+ if ($profile_id) {
+ Pref_Prefs::_init_user_prefs($_SESSION["uid"], $profile_id);
+ }
+ }
+ }
+
+ $this->pdo->commit();
+ }
+ }
+
+ function saveprofile() {
+ $id = clean($_REQUEST["id"]);
+ $title = clean($_REQUEST["title"]);
+
+ if ($id == 0) {
+ print __("Default profile");
+ return;
+ }
+
+ if ($title) {
+ $sth = $this->pdo->prepare("UPDATE ttrss_settings_profiles
+ SET title = ? WHERE id = ? AND
+ owner_uid = ?");
+
+ $sth->execute([$title, $id, $_SESSION['uid']]);
+ print $title;
+ }
+ }
+
+ // TODO: this maybe needs to be unified with Public::getProfiles()
+ function getProfiles() {
+ $rv = [];
$sth = $this->pdo->prepare("SELECT title,id FROM ttrss_settings_profiles
WHERE owner_uid = ? ORDER BY title");
$sth->execute([$_SESSION['uid']]);
- print "
";
+ array_push($rv, ["title" => __("Default profile"),
+ "id" => 0,
+ "active" => empty($_SESSION["profile"])
+ ]);
- print "";
-
- print "
- ".
- __('Remove selected profiles')."
- ".
- __('Activate profile')."
- ".
- __('Cancel')." ";
- print " ";
-
- print " ";
+ while ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
+ $row["active"] = isset($_SESSION["profile"]) && $_SESSION["profile"] == $row["id"];
+ array_push($rv, $row);
+ };
+ print json_encode($rv);
}
- private function getShortDesc($pref_name) {
+ private function _get_short_desc($pref_name) {
if (isset($this->pref_help[$pref_name][0])) {
return $this->pref_help[$pref_name][0];
}
return "";
}
- private function getHelpText($pref_name) {
+ private function _get_help_text($pref_name) {
if (isset($this->pref_help[$pref_name][1])) {
return $this->pref_help[$pref_name][1];
}
@@ -1284,56 +1288,54 @@ class Pref_Prefs extends Handler_Protected {
}
private function appPasswordList() {
- print "
";
- print "
" .
- "
" . __('Select') . " ";
- print "
";
- print "
" . __('All') . "
";
- print "
" . __('None') . "
";
- print "
";
- print "
"; #toolbar
+ ?>
+
+
+
= __('Select') ?>
+
+
= __('All') ?>
+
= __('None') ?>
+
+
+
- print "
+ encryptAppPassword($new_password);
+ $new_password_hash = $this->_encrypt_app_password($new_password);
print_warning(T_sprintf("Generated password
%s for %s. Please remember it for future reference.", $new_password, $title));
@@ -1366,7 +1368,7 @@ class Pref_Prefs extends Handler_Protected {
$this->appPasswordList();
}
- static function initialize_user_prefs($uid, $profile = false) {
+ static function _init_user_prefs($uid, $profile = false) {
if (get_schema_version() < 63) $profile_qpart = "";
diff --git a/classes/pref/system.php b/classes/pref/system.php
index d91339698..67f7133c6 100644
--- a/classes/pref/system.php
+++ b/classes/pref/system.php
@@ -1,20 +1,9 @@
pdo->query("DELETE FROM ttrss_error_log");
}
- private function log_viewer(int $page, int $severity) {
+ function getphpinfo() {
+ ob_start();
+ phpinfo();
+ $info = ob_get_contents();
+ ob_end_clean();
+
+ print preg_replace( '%^.*(.*).*$%ms','$1', (string)$info);
+ }
+
+ private function _log_viewer(int $page, int $severity) {
$errno_values = [];
switch ($severity) {
@@ -62,125 +60,121 @@ class Pref_System extends Handler_Protected {
$total_pages = 0;
}
- print "
";
+ ?>
+
+
- print "
";
+
+ = __('Refresh') ?>
+
- print "
".__('Refresh')." ";
+
+ onclick='Helpers.EventLog.prevPage()'>
+ = __('<<') ?>
+
- $prev_page_disabled = $page <= 0 ? "disabled" : "";
+
+ = T_sprintf('Page %d of %d', $page+1, $total_pages+1) ?>
+
- print "
".__('<<')." ";
+
= $total_pages ? "disabled" : "") ?>
+ onclick='Helpers.EventLog.nextPage()'>
+ = __('>>') ?>
+
- print "
".T_sprintf('Page %d of %d', $page+1, $total_pages+1)." ";
+
+ = __('Clear') ?>
+
- $next_page_disabled = $page >= $total_pages ? "disabled" : "";
+
+ = __("Severity:") ?>
- print "".__('>>')." ";
+ = \Controls\select_hash("severity", $severity,
+ [
+ E_USER_ERROR => __("Errors"),
+ E_USER_WARNING => __("Warnings"),
+ E_USER_NOTICE => __("Everything")
+ ], ["onchange"=> "Helpers.EventLog.refresh()"], "severity") ?>
+
+
- print "
".__('Clear')." ";
+
- print "
";
+
- print __("Severity:") . " ";
- print_select_hash("severity", $severity,
- [
- E_USER_ERROR => __("Errors"),
- E_USER_WARNING => __("Warnings"),
- E_USER_NOTICE => __("Everything")
- ], 'dojoType="fox.form.Select" onchange="Helpers.EventLog.refresh()"');
+
+ = __("Error") ?>
+ = __("Filename") ?>
+ = __("Message") ?>
+ = __("User") ?>
+ = __("Date") ?>
+
- print ""; # pull-right
+ pdo->prepare("SELECT
+ errno, errstr, filename, lineno, created_at, login, context
+ FROM
+ ttrss_error_log LEFT JOIN ttrss_users ON (owner_uid = ttrss_users.id)
+ WHERE
+ $errno_filter_qpart
+ ORDER BY
+ ttrss_error_log.id DESC
+ LIMIT $limit OFFSET $offset");
- print ""; # toolbar
+ $sth->execute($errno_values);
- print '';
-
- print "
";
-
- print "
- ".__("Error")."
- ".__("Filename")."
- ".__("Message")."
- ".__("User")."
- ".__("Date")."
- ";
-
- $sth = $this->pdo->prepare("SELECT
- errno, errstr, filename, lineno, created_at, login, context
- FROM
- ttrss_error_log LEFT JOIN ttrss_users ON (owner_uid = ttrss_users.id)
- WHERE
- $errno_filter_qpart
- ORDER BY
- ttrss_error_log.id DESC
- LIMIT $limit OFFSET $offset");
-
- $sth->execute($errno_values);
-
- while ($line = $sth->fetch()) {
- print "";
-
- foreach ($line as $k => $v) {
- $line[$k] = htmlspecialchars($v);
- }
-
- print "" . Logger::$errornames[$line["errno"]] . " (" . $line["errno"] . ") ";
- print "" . $line["filename"] . ":" . $line["lineno"] . " ";
- print "" . $line["errstr"] . "\n" . $line["context"] . " ";
- print "" . $line["login"] . " ";
-
- print "" .
- TimeHelper::make_local_datetime($line["created_at"], false) . " ";
-
- print " ";
- }
-
- print "
";
+ while ($line = $sth->fetch()) {
+ foreach ($line as $k => $v) { $line[$k] = htmlspecialchars($v); }
+ ?>
+
+
+ = Logger::$errornames[$line["errno"]] . " (" . $line["errno"] . ")" ?>
+
+ = $line["filename"] . ":" . $line["lineno"] ?>
+ = $line["errstr"] . "\n" . $line["context"] ?>
+ = $line["login"] ?>
+
+ = TimeHelper::make_local_datetime($line["created_at"], false) ?>
+
+
+
+
+
+
+
+
+
'>
+ _log_viewer($page, $severity);
+ } else {
+ print_notice("Please set Config::get(Config::LOG_DESTINATION) to 'sql' in config.php to enable database logging.");
+ }
+ ?>
+
- print "
";
- print "
";
+
'>
+
+ = __("Loading, please wait...") ?>
+
- if (LOG_DESTINATION == "sql") {
-
- $this->log_viewer($page, $severity);
-
- } else {
- print_notice("Please set LOG_DESTINATION to 'sql' in config.php to enable database logging.");
- }
-
- print "
"; # content pane
- print "
"; # container
- print "
"; # accordion pane
-
- print "
";
-
- ob_start();
- phpinfo();
- $info = ob_get_contents();
- ob_end_clean();
-
- print "
";
- print preg_replace( '%^.*(.*).*$%ms','$1', $info);
- print "
";
-
- print "
"; # accordion pane
-
- PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB, "prefSystem");
-
- print "
"; #container
+ run_hooks(PluginHost::HOOK_PREFS_TAB, "prefSystem") ?>
+
+ ";
+ $id = (int)clean($_REQUEST["id"]);
- print '
-
';
-
- //print "
";
-
- $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 = $this->pdo->prepare("SELECT id, login, access_level, email 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 "";
- print "";
-
- print "";
- print "";
-
- print "";
- print "";
-
- print "";
-
+ if ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
+ print json_encode([
+ "user" => $row,
+ "access_level_names" => $access_level_names
+ ]);
}
-
- print ' '; #tab
- print "
";
-
- print '
';
- print '
';
-
- print "
- ".
- __('Save')."
- ".
- __('Cancel')."
- ";
-
- print "";
-
- return;
}
function userdetails() {
@@ -135,7 +36,6 @@ class Pref_Users extends Handler_Protected {
$sth->execute([$id]);
if ($row = $sth->fetch()) {
- print "
";
$last_login = TimeHelper::make_local_datetime(
$row["last_login"], true);
@@ -145,47 +45,62 @@ class Pref_Users extends Handler_Protected {
$stored_articles = $row["stored_articles"];
- print "".__('Registered')." $created ";
- print "".__('Last logged in')." $last_login ";
-
$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 "".__('Subscribed feeds count')." $num_feeds ";
- print "".__('Stored articles')." $stored_articles ";
+ ?>
- print "
";
+
+ = __('Registered') ?>:
+ = $created ?>
+
- print "
".__('Subscribed feeds')." ";
+
+ = __('Last logged in') ?>:
+ = $last_login ?>
+
- $sth = $this->pdo->prepare("SELECT id,title,site_url FROM ttrss_feeds
- WHERE owner_uid = ? ORDER BY title");
- $sth->execute([$id]);
+
+ = __('Subscribed feeds') ?>:
+ = $num_feeds ?>
+
- print "
";
+
+ = __('Stored articles') ?>:
+ = $stored_articles ?>
+
- while ($line = $sth->fetch()) {
+ pdo->prepare("SELECT id,title,site_url FROM ttrss_feeds
+ WHERE owner_uid = ? ORDER BY title");
+ $sth->execute([$id]);
+ ?>
- $icon_file = ICONS_URL."/".$line["id"].".ico";
+ ";
+ ">
+ = htmlspecialchars($row["title"]) ?>
+
+
+
+
+ ".__('User not found')."";
+ print_error(__('User not found'));
}
}
@@ -197,6 +112,12 @@ class Pref_Users extends Handler_Protected {
$email = clean($_REQUEST["email"]);
$password = clean($_REQUEST["password"]);
+ // no blank usernames
+ if (!$login) return;
+
+ // forbid renaming admin
+ if ($uid == 1) $login = "admin";
+
if ($password) {
$salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
$pwd_hash = encrypt_password($password, $salt, true);
@@ -246,67 +167,25 @@ class Pref_Users extends Handler_Protected {
if ($new_uid = UserHelper::find_user_by_login($login)) {
- $new_uid = $row['id'];
-
print T_sprintf("Added user %s with password %s",
$login, $tmp_user_pwd);
- $this->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", "
$login ", "
$tmp_user_pwd ");
-
- if ($format_output)
- print_notice($message);
- else
- print $message;
-
- }
- }
-
function resetPass() {
- $uid = clean($_REQUEST["id"]);
- self::resetUserPassword($uid);
+ UserHelper::reset_password(clean($_REQUEST["id"]));
}
function index() {
global $access_level_names;
- print "
";
- print "
";
- print "
";
-
$user_search = clean($_REQUEST["search"] ?? "");
if (array_key_exists("search", $_REQUEST)) {
@@ -315,146 +194,111 @@ class Pref_Users extends Handler_Protected {
$user_search = ($_SESSION["prefs_user_search"] ?? "");
}
- print "
-
- ".
- __('Search')."
-
";
-
$sort = clean($_REQUEST["sort"] ?? "");
if (!$sort || $sort == "undefined") {
$sort = "login";
}
- print "
".
- "
" . __('Select')." ";
- print "
";
- print "
".__('All')."
";
- print "
".__('None')."
";
- print "
";
-
- print "
".__('Create user')." ";
-
- print "
-
".
- __('Edit')."
-
".
- __('Remove')."
-
".
- __('Reset password')." ";
-
- PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefUsersToolbar");
-
- print "
"; #toolbar
- print "
"; #pane
- print "
";
-
- $sort = $this->validate_field($sort,
+ $sort = $this->_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 "
";
+
+
+
- print "
-
- ".__('Login')."
- ".__('Access Level')."
- ".__('Subscribed feeds')."
- ".__('Registered')."
- ".__('Last login')." ";
+
+
+
+ = __('Search') ?>
+
+
- $lnum = 0;
+
+
= __('Select') ?>
+
+
= __('All') ?>
+
= __('None') ?>
+
+
- while ($line = $sth->fetch()) {
+
+ = __('Create user') ?>
+
- $uid = $line["id"];
+
+ = __('Remove') ?>
+
- print "
";
+
+ = __('Reset password') ?>
+
- $line["login"] = htmlspecialchars($line["login"]);
- $line["created"] = TimeHelper::make_local_datetime($line["created"], false);
- $line["last_login"] = TimeHelper::make_local_datetime($line["last_login"], false);
+ run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefUsersToolbar") ?>
- print " ";
+
+
+
- print "
person " . $line["login"] . "";
+
- print "" . $access_level_names[$line["access_level"]] . " ";
- print "" . $line["num_feeds"] . " ";
- print "" . $line["created"] . " ";
- print "" . $line["last_login"] . " ";
+
+
+ = ('Login') ?>
+ = ('Access Level') ?>
+ = ('Subscribed feeds') ?>
+ = ('Registered') ?>
+ = ('Last login') ?>
+
- print "";
+ 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%" : ""]);
- ++$lnum;
- }
+ while ($row = $sth->fetch()) { ?>
- print "
";
+
+
+
+
- if ($lnum == 0) {
- if (!$user_search) {
- print_warning(__('No users defined.'));
- } else {
- print_warning(__('No matching users found.'));
- }
- }
+ person = htmlspecialchars($row["login"]) ?>
+ = $access_level_names[$row["access_level"]] ?>
+ = $row["num_feeds"] ?>
+ = TimeHelper::make_local_datetime($row["created"], false) ?>
+ = TimeHelper::make_local_datetime($row["last_login"], false) ?>
+
+
+
+
+ run_hooks(PluginHost::HOOK_PREFS_TAB, "prefUsers") ?>
+
+ "; #pane
-
- PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB, "prefUsers");
-
- print "
"; #container
-
- }
-
- function validate_field($string, $allowed, $default = "") {
+ private function _validate_field($string, $allowed, $default = "") {
if (in_array($string, $allowed))
return $string;
else
return $default;
}
- // this is called after user is created to initialize default feeds, labels
- // or whatever else
- // user preferences are checked on every login, not here
- static function initialize_user($uid) {
-
- $pdo = Db::pdo();
-
- $sth = $pdo->prepare("insert into ttrss_feeds (owner_uid,title,feed_url)
- values (?, 'Tiny Tiny RSS: Forum',
- 'https://tt-rss.org/forum/rss.php')");
- $sth->execute([$uid]);
- }
-
- static function logout_user() {
- if (session_status() === PHP_SESSION_ACTIVE)
- session_destroy();
-
- if (isset($_COOKIE[session_name()])) {
- setcookie(session_name(), '', time()-42000, '/');
-
- }
- session_commit();
- }
-
}
diff --git a/classes/rpc.php b/classes/rpc.php
index f8af1d660..8945823c6 100755
--- a/classes/rpc.php
+++ b/classes/rpc.php
@@ -1,96 +1,11 @@
pdo->prepare("DELETE FROM ttrss_settings_profiles WHERE id = ? AND
- owner_uid = ?");
- $sth->execute([$id, $_SESSION['uid']]);
- }
- }
- }
-
- // Silent
- function addprofile() {
- $title = clean($_REQUEST["title"]);
-
- if ($title) {
- $this->pdo->beginTransaction();
-
- $sth = $this->pdo->prepare("SELECT id FROM ttrss_settings_profiles
- WHERE title = ? AND owner_uid = ?");
- $sth->execute([$title, $_SESSION['uid']]);
-
- if (!$sth->fetch()) {
-
- $sth = $this->pdo->prepare("INSERT INTO ttrss_settings_profiles (title, owner_uid)
- VALUES (?, ?)");
-
- $sth->execute([$title, $_SESSION['uid']]);
-
- $sth = $this->pdo->prepare("SELECT id FROM ttrss_settings_profiles WHERE
- title = ? AND owner_uid = ?");
- $sth->execute([$title, $_SESSION['uid']]);
-
- if ($row = $sth->fetch()) {
- $profile_id = $row['id'];
-
- if ($profile_id) {
- Pref_Prefs::initialize_user_prefs($_SESSION["uid"], $profile_id);
- }
- }
- }
-
- $this->pdo->commit();
- }
- }
-
- function saveprofile() {
- $id = clean($_REQUEST["id"]);
- $title = clean($_REQUEST["value"]);
-
- if ($id == 0) {
- print __("Default profile");
- return;
- }
-
- if ($title) {
- $sth = $this->pdo->prepare("UPDATE ttrss_settings_profiles
- SET title = ? WHERE id = ? AND
- owner_uid = ?");
-
- $sth->execute([$title, $id, $_SESSION['uid']]);
- print $title;
- }
- }
-
- function addfeed() {
- $feed = clean($_REQUEST['feed']);
- $cat = clean($_REQUEST['cat']);
- $need_auth = isset($_REQUEST['need_auth']);
- $login = $need_auth ? clean($_REQUEST['login']) : '';
- $pass = $need_auth ? clean($_REQUEST['pass']) : '';
-
- $rc = Feeds::subscribe_to_feed($feed, $cat, $login, $pass);
-
- print json_encode(array("result" => $rc));
- }
+ }*/
function togglepref() {
$key = clean($_REQUEST["key"]);
@@ -131,7 +46,7 @@ class RPC extends Handler_Protected {
WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
$sth->execute(array_merge($ids, [$_SESSION['uid']]));
- Article::purge_orphans();
+ Article::_purge_orphans();
print json_encode(array("message" => "UPDATE_COUNTERS"));
}
@@ -149,67 +64,100 @@ class RPC extends Handler_Protected {
print json_encode(array("message" => "UPDATE_COUNTERS"));
}
+ function getRuntimeInfo() {
+ $reply = [
+ 'runtime-info' => $this->make_runtime_info()
+ ];
+
+ print json_encode($reply);
+ }
+
function getAllCounters() {
@$seq = (int) $_REQUEST['seq'];
+ $feed_id_count = (int)$_REQUEST["feed_id_count"];
+ $label_id_count = (int)$_REQUEST["label_id_count"];
+
+ // it seems impossible to distinguish empty array [] from a null - both become unset in $_REQUEST
+ // so, count is >= 0 means we had an array, -1 means null
+ // we need null because it means "return all counters"; [] would return nothing
+ if ($feed_id_count == -1)
+ $feed_ids = null;
+ else
+ $feed_ids = array_map("intval", clean($_REQUEST["feed_ids"] ?? []));
+
+ if ($label_id_count == -1)
+ $label_ids = null;
+ else
+ $label_ids = array_map("intval", clean($_REQUEST["label_ids"] ?? []));
+
+ // @phpstan-ignore-next-line
+ $counters = is_array($feed_ids) ? Counters::get_conditional($feed_ids, $label_ids) : Counters::get_all();
+
$reply = [
- 'counters' => Counters::getAllCounters(),
+ 'counters' => $counters,
'seq' => $seq
];
- if ($seq % 2 == 0)
- $reply['runtime-info'] = $this->make_runtime_info();
-
print json_encode($reply);
}
/* GET["cmode"] = 0 - mark as read, 1 - as unread, 2 - toggle */
function catchupSelected() {
- $ids = explode(",", clean($_REQUEST["ids"]));
+ $ids = array_map("intval", clean($_REQUEST["ids"] ?? []));
$cmode = (int)clean($_REQUEST["cmode"]);
- Article::catchupArticlesById($ids, $cmode);
+ if (count($ids) > 0)
+ Article::_catchup_by_id($ids, $cmode);
- print json_encode(array("message" => "UPDATE_COUNTERS", "ids" => $ids));
+ print json_encode(["message" => "UPDATE_COUNTERS",
+ "labels" => Article::_labels_of($ids),
+ "feeds" => Article::_feeds_of($ids)]);
}
function markSelected() {
- $ids = explode(",", clean($_REQUEST["ids"]));
+ $ids = array_map("intval", clean($_REQUEST["ids"] ?? []));
$cmode = (int)clean($_REQUEST["cmode"]);
- $this->markArticlesById($ids, $cmode);
+ if (count($ids) > 0)
+ $this->markArticlesById($ids, $cmode);
- print json_encode(array("message" => "UPDATE_COUNTERS"));
+ print json_encode(["message" => "UPDATE_COUNTERS", "feeds" => Article::_feeds_of($ids)]);
}
function publishSelected() {
- $ids = explode(",", clean($_REQUEST["ids"]));
+ $ids = array_map("intval", clean($_REQUEST["ids"] ?? []));
$cmode = (int)clean($_REQUEST["cmode"]);
- $this->publishArticlesById($ids, $cmode);
+ if (count($ids) > 0)
+ $this->publishArticlesById($ids, $cmode);
- print json_encode(array("message" => "UPDATE_COUNTERS"));
+ print json_encode(["message" => "UPDATE_COUNTERS", "feeds" => Article::_feeds_of($ids)]);
}
function sanityCheck() {
- $_SESSION["hasAudio"] = clean($_REQUEST["hasAudio"]) === "true";
$_SESSION["hasSandbox"] = clean($_REQUEST["hasSandbox"]) === "true";
- $_SESSION["hasMp3"] = clean($_REQUEST["hasMp3"]) === "true";
$_SESSION["clientTzOffset"] = clean($_REQUEST["clientTzOffset"]);
- $reply = array();
+ $error = Errors::E_SUCCESS;
- $reply['error'] = sanity_check();
-
- if ($reply['error']['code'] == 0) {
- $reply['init-params'] = $this->make_init_params();
- $reply['runtime-info'] = $this->make_runtime_info();
+ if (get_schema_version(true) != SCHEMA_VERSION) {
+ $error = Errors::E_SCHEMA_MISMATCH;
}
- print json_encode($reply);
+ if ($error == Errors::E_SUCCESS) {
+ $reply = [];
+
+ $reply['init-params'] = $this->make_init_params();
+ $reply['runtime-info'] = $this->make_runtime_info();
+
+ print json_encode($reply);
+ } else {
+ print Errors::to_json($error);
+ }
}
- function completeLabels() {
+ /*function completeLabels() {
$search = clean($_REQUEST["search"]);
$sth = $this->pdo->prepare("SELECT DISTINCT caption FROM
@@ -224,19 +172,19 @@ class RPC extends Handler_Protected {
print "
" . $line["caption"] . " ";
}
print "";
- }
+ }*/
function catchupFeed() {
$feed_id = clean($_REQUEST['feed_id']);
$is_cat = clean($_REQUEST['is_cat']) == "true";
- $mode = clean($_REQUEST['mode']);
+ $mode = clean($_REQUEST['mode'] ?? '');
$search_query = clean($_REQUEST['search_query']);
$search_lang = clean($_REQUEST['search_lang']);
- Feeds::catchup_feed($feed_id, $is_cat, false, $mode, [$search_query, $search_lang]);
+ Feeds::_catchup($feed_id, $is_cat, false, $mode, [$search_query, $search_lang]);
// return counters here synchronously so that frontend can figure out next unread feed properly
- print json_encode(['counters' => Counters::getAllCounters()]);
+ print json_encode(['counters' => Counters::get_all()]);
//print json_encode(array("message" => "UPDATE_COUNTERS"));
}
@@ -244,8 +192,9 @@ class RPC extends Handler_Protected {
function setpanelmode() {
$wide = (int) clean($_REQUEST["wide"]);
+ // FIXME should this use SESSION_COOKIE_LIFETIME and be renewed periodically?
setcookie("ttrss_widescreen", (string)$wide,
- time() + COOKIE_LIFETIME_LONG);
+ time() + 86400*365);
print json_encode(array("wide" => $wide));
}
@@ -253,7 +202,7 @@ class RPC extends Handler_Protected {
static function updaterandomfeed_real() {
// Test if the feed need a update (update interval exceded).
- if (DB_TYPE == "pgsql") {
+ if (Config::get(Config::DB_TYPE) == "pgsql") {
$update_limit_qpart = "AND ((
ttrss_feeds.update_interval = 0
AND ttrss_feeds.last_updated < NOW() - CAST((ttrss_user_prefs.value || ' minutes') AS INTERVAL)
@@ -278,7 +227,7 @@ class RPC extends Handler_Protected {
}
// Test if feed is currently being updated by another process.
- if (DB_TYPE == "pgsql") {
+ if (Config::get(Config::DB_TYPE) == "pgsql") {
$updstart_thresh_qpart = "AND (ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < NOW() - INTERVAL '5 minutes')";
} else {
$updstart_thresh_qpart = "AND (ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < DATE_SUB(NOW(), INTERVAL 5 MINUTE))";
@@ -324,7 +273,7 @@ class RPC extends Handler_Protected {
}
// Purge orphans and cleanup tags
- Article::purge_orphans();
+ Article::_purge_orphans();
//cleanup_tags(14, 50000);
if ($num_updated > 0) {
@@ -382,23 +331,6 @@ class RPC extends Handler_Protected {
$sth->execute(array_merge($ids, [$_SESSION['uid']]));
}
- function getlinktitlebyid() {
- $id = clean($_REQUEST['id']);
-
- $sth = $this->pdo->prepare("SELECT link, title FROM ttrss_entries, ttrss_user_entries
- WHERE ref_id = ? AND ref_id = id AND owner_uid = ?");
- $sth->execute([$id, $_SESSION['uid']]);
-
- if ($row = $sth->fetch()) {
- $link = $row['link'];
- $title = $row['title'];
-
- echo json_encode(array("link" => $link, "title" => $title));
- } else {
- echo json_encode(array("error" => "ARTICLE_NOT_FOUND"));
- }
- }
-
function log() {
$msg = clean($_REQUEST['msg']);
$file = basename(clean($_REQUEST['file']));
@@ -410,10 +342,7 @@ class RPC extends Handler_Protected {
$msg, 'client-js:' . $file, $line, $context);
echo json_encode(array("message" => "HOST_ERROR_LOGGED"));
- } else {
- echo json_encode(array("error" => "MESSAGE_NOT_FOUND"));
}
-
}
function checkforupdates() {
@@ -424,7 +353,7 @@ class RPC extends Handler_Protected {
get_version($git_commit, $git_timestamp);
- if (defined('CHECK_FOR_UPDATES') && CHECK_FOR_UPDATES && $_SESSION["access_level"] >= 10 && $git_timestamp) {
+ if (Config::get(Config::CHECK_FOR_UPDATES) && $_SESSION["access_level"] >= 10 && $git_timestamp) {
$content = @UrlHelper::fetch(["url" => "https://tt-rss.org/version.json"]);
if ($content) {
@@ -455,9 +384,9 @@ class RPC extends Handler_Protected {
}
$params["safe_mode"] = !empty($_SESSION["safe_mode"]);
- $params["check_for_updates"] = CHECK_FOR_UPDATES;
- $params["icons_url"] = ICONS_URL;
- $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME;
+ $params["check_for_updates"] = Config::get(Config::CHECK_FOR_UPDATES);
+ $params["icons_url"] = Config::get(Config::ICONS_URL);
+ $params["cookie_lifetime"] = Config::get(Config::SESSION_COOKIE_LIFETIME);
$params["default_view_mode"] = get_pref("_DEFAULT_VIEW_MODE");
$params["default_view_limit"] = (int) get_pref("_DEFAULT_VIEW_LIMIT");
$params["default_view_order_by"] = get_pref("_DEFAULT_VIEW_ORDER_BY");
@@ -486,16 +415,11 @@ class RPC extends Handler_Protected {
$params["self_url_prefix"] = get_self_url_prefix();
$params["max_feed_id"] = (int) $max_feed_id;
$params["num_feeds"] = (int) $num_feeds;
-
$params["hotkeys"] = $this->get_hotkeys_map();
-
$params["widescreen"] = (int) ($_COOKIE["ttrss_widescreen"] ?? 0);
-
- $params['simple_update'] = SIMPLE_UPDATE_MODE;
-
+ $params['simple_update'] = Config::get(Config::SIMPLE_UPDATE_MODE);
$params["icon_indicator_white"] = $this->image_to_base64("images/indicator_white.gif");
-
- $params["labels"] = Labels::get_all_labels($_SESSION["uid"]);
+ $params["labels"] = Labels::get_all($_SESSION["uid"]);
return $params;
}
@@ -526,10 +450,10 @@ class RPC extends Handler_Protected {
$data["max_feed_id"] = (int) $max_feed_id;
$data["num_feeds"] = (int) $num_feeds;
$data['cdm_expanded'] = get_pref('CDM_EXPANDED');
- $data["labels"] = Labels::get_all_labels($_SESSION["uid"]);
+ $data["labels"] = Labels::get_all($_SESSION["uid"]);
- if (LOG_DESTINATION == 'sql' && $_SESSION['access_level'] >= 10) {
- if (DB_TYPE == 'pgsql') {
+ if (Config::get(Config::LOG_DESTINATION) == 'sql' && $_SESSION['access_level'] >= 10) {
+ if (Config::get(Config::DB_TYPE) == 'pgsql') {
$log_interval = "created_at > NOW() - interval '1 hour'";
} else {
$log_interval = "created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)";
@@ -538,7 +462,7 @@ class RPC extends Handler_Protected {
$sth = $pdo->prepare("SELECT COUNT(id) AS cid
FROM ttrss_error_log
WHERE
- errno != 1024 AND
+ errno NOT IN (".E_USER_NOTICE.", ".E_USER_DEPRECATED.") AND
$log_interval AND
errstr NOT LIKE '%imagecreatefromstring(): Data is not in a recognized format%'");
$sth->execute();
@@ -548,13 +472,13 @@ class RPC extends Handler_Protected {
}
}
- if (file_exists(LOCK_DIRECTORY . "/update_daemon.lock")) {
+ if (file_exists(Config::get(Config::LOCK_DIRECTORY) . "/update_daemon.lock")) {
$data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
if (time() - ($_SESSION["daemon_stamp_check"] ?? 0) > 30) {
- $stamp = (int) @file_get_contents(LOCK_DIRECTORY . "/update_daemon.stamp");
+ $stamp = (int) @file_get_contents(Config::get(Config::LOCK_DIRECTORY) . "/update_daemon.stamp");
if ($stamp) {
$stamp_delta = time() - $stamp;
@@ -737,4 +661,73 @@ class RPC extends Handler_Protected {
return array($prefixes, $hotkeys);
}
+ function hotkeyHelp() {
+ $info = self::get_hotkeys_info();
+ $imap = self::get_hotkeys_map();
+ $omap = array();
+
+ foreach ($imap[1] as $sequence => $action) {
+ if (!isset($omap[$action])) $omap[$action] = array();
+
+ array_push($omap[$action], $sequence);
+ }
+
+ ?>
+
+
+ = \Controls\submit_tag(__('Close this window')) ?>
+
+ prepare("SELECT id FROM ttrss_feeds WHERE id = ?");
- // check icon files once every CACHE_MAX_DAYS days
- $icon_files = array_filter(glob(ICONS_DIR . "/*.ico"),
- function($f) { return filemtime($f) < time() - 86400*CACHE_MAX_DAYS; });
+ // check icon files once every Config::get(Config::CACHE_MAX_DAYS) days
+ $icon_files = array_filter(glob(Config::get(Config::ICONS_DIR) . "/*.ico"),
+ function($f) { return filemtime($f) < time() - 86400 * Config::get(Config::CACHE_MAX_DAYS); });
foreach ($icon_files as $icon) {
$feed_id = basename($icon, ".ico");
@@ -52,26 +52,28 @@ class RSSUtils {
}
}
- static function update_daemon_common($limit = DAEMON_FEED_LIMIT, $options = []) {
+ static function update_daemon_common($limit = null, $options = []) {
$schema_version = get_schema_version();
+ if (!$limit) $limit = Config::get(Config::DAEMON_FEED_LIMIT);
+
if ($schema_version != SCHEMA_VERSION) {
die("Schema version is wrong, please upgrade the database.\n");
}
$pdo = Db::pdo();
- if (!SINGLE_USER_MODE && DAEMON_UPDATE_LOGIN_LIMIT > 0) {
- if (DB_TYPE == "pgsql") {
- $login_thresh_qpart = "AND ttrss_users.last_login >= NOW() - INTERVAL '".DAEMON_UPDATE_LOGIN_LIMIT." days'";
+ if (!Config::get(Config::SINGLE_USER_MODE) && Config::get(Config::DAEMON_UPDATE_LOGIN_LIMIT) > 0) {
+ if (Config::get(Config::DB_TYPE) == "pgsql") {
+ $login_thresh_qpart = "AND ttrss_users.last_login >= NOW() - INTERVAL '".Config::get(Config::DAEMON_UPDATE_LOGIN_LIMIT)." days'";
} else {
- $login_thresh_qpart = "AND ttrss_users.last_login >= DATE_SUB(NOW(), INTERVAL ".DAEMON_UPDATE_LOGIN_LIMIT." DAY)";
+ $login_thresh_qpart = "AND ttrss_users.last_login >= DATE_SUB(NOW(), INTERVAL ".Config::get(Config::DAEMON_UPDATE_LOGIN_LIMIT)." DAY)";
}
} else {
$login_thresh_qpart = "";
}
- if (DB_TYPE == "pgsql") {
+ if (Config::get(Config::DB_TYPE) == "pgsql") {
$update_limit_qpart = "AND ((
ttrss_feeds.update_interval = 0
AND ttrss_user_prefs.value != '-1'
@@ -96,7 +98,7 @@ class RSSUtils {
}
// Test if feed is currently being updated by another process.
- if (DB_TYPE == "pgsql") {
+ if (Config::get(Config::DB_TYPE) == "pgsql") {
$updstart_thresh_qpart = "AND (last_update_started IS NULL OR last_update_started < NOW() - INTERVAL '10 minutes')";
} else {
$updstart_thresh_qpart = "AND (last_update_started IS NULL OR last_update_started < DATE_SUB(NOW(), INTERVAL 10 MINUTE))";
@@ -106,7 +108,7 @@ class RSSUtils {
// Update the least recently updated feeds first
$query_order = "ORDER BY last_updated";
- if (DB_TYPE == "pgsql") $query_order .= " NULLS FIRST";
+ if (Config::get(Config::DB_TYPE) == "pgsql") $query_order .= " NULLS FIRST";
$query = "SELECT DISTINCT ttrss_feeds.feed_url, ttrss_feeds.last_updated
FROM
@@ -182,7 +184,7 @@ class RSSUtils {
if (self::function_enabled('passthru')) {
$exit_code = 0;
- passthru(PHP_EXECUTABLE . " update.php --update-feed " . $tline["id"] . " --pidlock feed-" . $tline["id"] . " $quiet $log $log_level", $exit_code);
+ passthru(Config::get(Config::PHP_EXECUTABLE) . " update.php --update-feed " . $tline["id"] . " --pidlock feed-" . $tline["id"] . " $quiet $log $log_level", $exit_code);
Debug::log(sprintf("<= %.4f (sec) exit code: %d", microtime(true) - $fstarted, $exit_code));
@@ -275,7 +277,7 @@ class RSSUtils {
$pluginhost = new PluginHost();
$user_plugins = get_pref("_ENABLED_PLUGINS", $owner_uid);
- $pluginhost->load(PLUGINS, PluginHost::KIND_ALL);
+ $pluginhost->load(Config::get(Config::PLUGINS), PluginHost::KIND_ALL);
$pluginhost->load((string)$user_plugins, PluginHost::KIND_USER, $owner_uid);
//$pluginhost->load_data();
@@ -288,7 +290,7 @@ class RSSUtils {
if (!$basic_info) {
$feed_data = UrlHelper::fetch($fetch_url, false,
$auth_login, $auth_pass, false,
- FEED_FETCH_TIMEOUT,
+ Config::get(Config::FEED_FETCH_TIMEOUT),
0);
$feed_data = trim($feed_data);
@@ -395,12 +397,12 @@ class RSSUtils {
$date_feed_processed = date('Y-m-d H:i');
- $cache_filename = CACHE_DIR . "/feeds/" . sha1($fetch_url) . ".xml";
+ $cache_filename = Config::get(Config::CACHE_DIR) . "/feeds/" . sha1($fetch_url) . ".xml";
$pluginhost = new PluginHost();
$user_plugins = get_pref("_ENABLED_PLUGINS", $owner_uid);
- $pluginhost->load(PLUGINS, PluginHost::KIND_ALL);
+ $pluginhost->load(Config::get(Config::PLUGINS), PluginHost::KIND_ALL);
$pluginhost->load((string)$user_plugins, PluginHost::KIND_USER, $owner_uid);
//$pluginhost->load_data();
@@ -455,7 +457,7 @@ class RSSUtils {
Debug::log("not using CURL due to open_basedir restrictions", Debug::$LOG_VERBOSE);
}
- if (time() - strtotime($last_unconditional) > MAX_CONDITIONAL_INTERVAL) {
+ if (time() - strtotime($last_unconditional) > Config::get(Config::MAX_CONDITIONAL_INTERVAL)) {
Debug::log("maximum allowed interval for conditional requests exceeded, forcing refetch", Debug::$LOG_VERBOSE);
$force_refetch = true;
@@ -469,7 +471,7 @@ class RSSUtils {
"url" => $fetch_url,
"login" => $auth_login,
"pass" => $auth_pass,
- "timeout" => $no_cache ? FEED_FETCH_NO_CACHE_TIMEOUT : FEED_FETCH_TIMEOUT,
+ "timeout" => $no_cache ? Config::get(Config::FEED_FETCH_NO_CACHE_TIMEOUT) : Config::get(Config::FEED_FETCH_TIMEOUT),
"last_modified" => $force_refetch ? "" : $stored_last_modified
]);
@@ -488,7 +490,7 @@ class RSSUtils {
}
// cache vanilla feed data for re-use
- if ($feed_data && !$auth_pass && !$auth_login && is_writable(CACHE_DIR . "/feeds")) {
+ if ($feed_data && !$auth_pass && !$auth_login && is_writable(Config::get(Config::CACHE_DIR) . "/feeds")) {
$new_rss_hash = sha1($feed_data);
if ($new_rss_hash != $rss_hash) {
@@ -561,7 +563,7 @@ class RSSUtils {
Debug::log("language: $feed_language", Debug::$LOG_VERBOSE);
Debug::log("processing feed data...", Debug::$LOG_VERBOSE);
- if (DB_TYPE == "pgsql") {
+ if (Config::get(Config::DB_TYPE) == "pgsql") {
$favicon_interval_qpart = "favicon_last_checked < NOW() - INTERVAL '12 hour'";
} else {
$favicon_interval_qpart = "favicon_last_checked < DATE_SUB(NOW(), INTERVAL 12 HOUR)";
@@ -591,10 +593,10 @@ class RSSUtils {
/* terrible hack: if we crash on floicon shit here, we won't check
* the icon avgcolor again (unless the icon got updated) */
- $favicon_file = ICONS_DIR . "/$feed.ico";
+ $favicon_file = Config::get(Config::ICONS_DIR) . "/$feed.ico";
$favicon_modified = file_exists($favicon_file) ? filemtime($favicon_file) : -1;
- Debug::log("checking favicon...", Debug::$LOG_VERBOSE);
+ Debug::log("checking favicon for feed $feed...", Debug::$LOG_VERBOSE);
self::check_feed_favicon($site_url, $feed);
$favicon_modified_new = file_exists($favicon_file) ? filemtime($favicon_file) : -1;
@@ -610,7 +612,7 @@ class RSSUtils {
id = ?");
$sth->execute([$feed]);
- $favicon_color = calculate_avg_color($favicon_file);
+ $favicon_color = \Colors\calculate_avg_color($favicon_file);
$favicon_colorstring = ",favicon_avg_color = " . $pdo->quote($favicon_color);
@@ -723,9 +725,9 @@ class RSSUtils {
if ($row = $sth->fetch()) {
$base_entry_id = $row["id"];
$entry_stored_hash = $row["content_hash"];
- $article_labels = Article::get_article_labels($base_entry_id, $owner_uid);
+ $article_labels = Article::_get_labels($base_entry_id, $owner_uid);
- $existing_tags = Article::get_article_tags($base_entry_id, $owner_uid);
+ $existing_tags = Article::_get_tags($base_entry_id, $owner_uid);
$entry_tags = array_unique(array_merge($entry_tags, $existing_tags));
} else {
$base_entry_id = false;
@@ -739,7 +741,7 @@ class RSSUtils {
$enclosures = array();
- $encs = $item->get_enclosures();
+ $encs = $item->_get_enclosures();
if (is_array($encs)) {
foreach ($encs as $e) {
@@ -755,7 +757,7 @@ class RSSUtils {
$e->type, $e->length, $e->title, $e->width, $e->height);
// Yet another episode of "mysql utf8_general_ci is gimped"
- if (DB_TYPE == "mysql" && MYSQL_CHARSET != "UTF8MB4") {
+ if (Config::get(Config::DB_TYPE) == "mysql" && Config::get(Config::MYSQL_CHARSET) != "UTF8MB4") {
for ($i = 0; $i < count($e_item); $i++) {
if (is_string($e_item[$i])) {
$e_item[$i] = self::strip_utf8mb4($e_item[$i]);
@@ -833,7 +835,7 @@ class RSSUtils {
Debug::log("plugin data: $entry_plugin_data", Debug::$LOG_VERBOSE);
// Workaround: 4-byte unicode requires utf8mb4 in MySQL. See https://tt-rss.org/forum/viewtopic.php?f=1&t=3377&p=20077#p20077
- if (DB_TYPE == "mysql" && MYSQL_CHARSET != "UTF8MB4") {
+ if (Config::get(Config::DB_TYPE) == "mysql" && Config::get(Config::MYSQL_CHARSET) != "UTF8MB4") {
foreach ($article as $k => $v) {
// i guess we'll have to take the risk of 4byte unicode labels & tags here
if (is_string($article[$k])) {
@@ -1079,7 +1081,7 @@ class RSSUtils {
Debug::log("resulting RID: $entry_ref_id, IID: $entry_int_id", Debug::$LOG_VERBOSE);
- if (DB_TYPE == "pgsql")
+ if (Config::get(Config::DB_TYPE) == "pgsql")
$tsvector_qpart = "tsvector_combined = to_tsvector(:ts_lang, :ts_content),";
else
$tsvector_qpart = "";
@@ -1107,7 +1109,7 @@ class RSSUtils {
":lang" => $entry_language,
":id" => $ref_id];
- if (DB_TYPE == "pgsql") {
+ if (Config::get(Config::DB_TYPE) == "pgsql") {
$params[":ts_lang"] = $feed_language;
$params[":ts_content"] = mb_substr(strip_tags($entry_title . " " . $entry_content), 0, 900000);
}
@@ -1239,7 +1241,7 @@ class RSSUtils {
Debug::log("purging feed...", Debug::$LOG_VERBOSE);
- Feeds::purge_feed($feed, 0);
+ Feeds::_purge($feed, 0);
$sth = $pdo->prepare("UPDATE ttrss_feeds SET
last_updated = NOW(),
@@ -1281,7 +1283,7 @@ class RSSUtils {
static function cache_enclosures($enclosures, $site_url) {
$cache = new DiskCache("images");
- if ($cache->isWritable()) {
+ if ($cache->is_writable()) {
foreach ($enclosures as $enc) {
if (preg_match("/(image|audio|video)/", $enc[1])) {
@@ -1298,7 +1300,7 @@ class RSSUtils {
$file_content = UrlHelper::fetch(array("url" => $src,
"http_referrer" => $src,
- "max_size" => MAX_CACHE_FILE_SIZE));
+ "max_size" => Config::get(Config::MAX_CACHE_FILE_SIZE)));
if ($file_content) {
$cache->put($local_filename, $file_content);
@@ -1328,14 +1330,14 @@ class RSSUtils {
$file_content = UrlHelper::fetch(array("url" => $url,
"http_referrer" => $url,
- "max_size" => MAX_CACHE_FILE_SIZE));
+ "max_size" => Config::get(Config::MAX_CACHE_FILE_SIZE)));
if ($file_content) {
$cache->put($local_filename, $file_content);
} else {
Debug::log("cache_media: failed with $fetch_last_error_code: $fetch_last_error");
}
- } else if ($cache->isWritable($local_filename)) {
+ } else if ($cache->is_writable($local_filename)) {
$cache->touch($local_filename);
}
}
@@ -1344,7 +1346,7 @@ class RSSUtils {
static function cache_media($html, $site_url) {
$cache = new DiskCache("images");
- if ($html && $cache->isWritable()) {
+ if ($html && $cache->is_writable()) {
$doc = new DOMDocument();
if (@$doc->loadHTML($html)) {
$xpath = new DOMXPath($doc);
@@ -1375,7 +1377,7 @@ class RSSUtils {
$pdo = Db::pdo();
- if (DB_TYPE == "pgsql") {
+ if (Config::get(Config::DB_TYPE) == "pgsql") {
$pdo->query("DELETE FROM ttrss_error_log
WHERE created_at < NOW() - INTERVAL '7 days'");
} else {
@@ -1396,8 +1398,8 @@ class RSSUtils {
$num_deleted = 0;
- if (is_writable(LOCK_DIRECTORY)) {
- $files = glob(LOCK_DIRECTORY . "/*.lock");
+ if (is_writable(Config::get(Config::LOCK_DIRECTORY))) {
+ $files = glob(Config::get(Config::LOCK_DIRECTORY) . "/*.lock");
if ($files) {
foreach ($files as $file) {
@@ -1581,17 +1583,17 @@ class RSSUtils {
}
static function disable_failed_feeds() {
- if (defined('DAEMON_UNSUCCESSFUL_DAYS_LIMIT') && DAEMON_UNSUCCESSFUL_DAYS_LIMIT > 0) {
+ if (Config::get(Config::DAEMON_UNSUCCESSFUL_DAYS_LIMIT) > 0) {
$pdo = Db::pdo();
$pdo->beginTransaction();
- $days = DAEMON_UNSUCCESSFUL_DAYS_LIMIT;
+ $days = Config::get(Config::DAEMON_UNSUCCESSFUL_DAYS_LIMIT);
- if (DB_TYPE == "pgsql") {
+ if (Config::get(Config::DB_TYPE) == "pgsql") {
$interval_query = "last_successful_update < NOW() - INTERVAL '$days days' AND last_updated > NOW() - INTERVAL '1 days'";
- } else /* if (DB_TYPE == "mysql") */ {
+ } else /* if (Config::get(Config::DB_TYPE) == "mysql") */ {
$interval_query = "last_successful_update < DATE_SUB(NOW(), INTERVAL $days DAY) AND last_updated > DATE_SUB(NOW(), INTERVAL 1 DAY)";
}
@@ -1604,10 +1606,10 @@ class RSSUtils {
while ($row = $sth->fetch()) {
Logger::get()->log(E_USER_NOTICE,
sprintf("Auto disabling feed %d (%s, UID: %d) because it failed to update for %d days.",
- $row["id"], clean($row["title"]), $row["owner_uid"], DAEMON_UNSUCCESSFUL_DAYS_LIMIT));
+ $row["id"], clean($row["title"]), $row["owner_uid"], Config::get(Config::DAEMON_UNSUCCESSFUL_DAYS_LIMIT)));
Debug::log(sprintf("Auto-disabling feed %d (%s) (failed to update for %d days).", $row["id"],
- clean($row["title"]), DAEMON_UNSUCCESSFUL_DAYS_LIMIT));
+ clean($row["title"]), Config::get(Config::DAEMON_UNSUCCESSFUL_DAYS_LIMIT)));
}
$sth = $pdo->prepare("UPDATE ttrss_feeds SET update_interval = -1 WHERE
@@ -1636,65 +1638,74 @@ class RSSUtils {
self::cleanup_feed_icons();
self::disable_failed_feeds();
- Article::purge_orphans();
+ Article::_purge_orphans();
self::cleanup_counters_cache();
PluginHost::getInstance()->run_hooks(PluginHost::HOOK_HOUSE_KEEPING);
}
static function check_feed_favicon($site_url, $feed) {
- # print "FAVICON [$site_url]: $favicon_url\n";
+ $icon_file = Config::get(Config::ICONS_DIR) . "/$feed.ico";
- $icon_file = ICONS_DIR . "/$feed.ico";
-
- if (!file_exists($icon_file)) {
- $favicon_url = self::get_favicon_url($site_url);
-
- if ($favicon_url) {
- // Limiting to "image" type misses those served with text/plain
- $contents = UrlHelper::fetch($favicon_url); // , "image");
-
- if ($contents) {
- // Crude image type matching.
- // Patterns gleaned from the file(1) source code.
- if (preg_match('/^\x00\x00\x01\x00/', $contents)) {
- // 0 string \000\000\001\000 MS Windows icon resource
- //error_log("check_feed_favicon: favicon_url=$favicon_url isa MS Windows icon resource");
- }
- elseif (preg_match('/^GIF8/', $contents)) {
- // 0 string GIF8 GIF image data
- //error_log("check_feed_favicon: favicon_url=$favicon_url isa GIF image");
- }
- elseif (preg_match('/^\x89PNG\x0d\x0a\x1a\x0a/', $contents)) {
- // 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data
- //error_log("check_feed_favicon: favicon_url=$favicon_url isa PNG image");
- }
- elseif (preg_match('/^\xff\xd8/', $contents)) {
- // 0 beshort 0xffd8 JPEG image data
- //error_log("check_feed_favicon: favicon_url=$favicon_url isa JPG image");
- }
- elseif (preg_match('/^BM/', $contents)) {
- // 0 string BM PC bitmap (OS2, Windows BMP files)
- //error_log("check_feed_favicon, favicon_url=$favicon_url isa BMP image");
- }
- else {
- //error_log("check_feed_favicon: favicon_url=$favicon_url isa UNKNOWN type");
- $contents = "";
- }
- }
-
- if ($contents) {
- $fp = @fopen($icon_file, "w");
-
- if ($fp) {
- fwrite($fp, $contents);
- fclose($fp);
- chmod($icon_file, 0644);
- }
- }
- }
- return $icon_file;
+ $favicon_url = self::get_favicon_url($site_url);
+ if (!$favicon_url) {
+ Debug::log("couldn't find favicon URL in $site_url", Debug::$LOG_VERBOSE);
+ return false;
}
+
+ // Limiting to "image" type misses those served with text/plain
+ $contents = UrlHelper::fetch([
+ 'url' => $favicon_url,
+ 'max_size' => Config::get(Config::MAX_FAVICON_FILE_SIZE),
+ //'type' => 'image',
+ ]);
+ if (!$contents) {
+ Debug::log("fetching favicon $favicon_url failed", Debug::$LOG_VERBOSE);
+ return false;
+ }
+
+ // Crude image type matching.
+ // Patterns gleaned from the file(1) source code.
+ if (preg_match('/^\x00\x00\x01\x00/', $contents)) {
+ // 0 string \000\000\001\000 MS Windows icon resource
+ //error_log("check_feed_favicon: favicon_url=$favicon_url isa MS Windows icon resource");
+ }
+ elseif (preg_match('/^GIF8/', $contents)) {
+ // 0 string GIF8 GIF image data
+ //error_log("check_feed_favicon: favicon_url=$favicon_url isa GIF image");
+ }
+ elseif (preg_match('/^\x89PNG\x0d\x0a\x1a\x0a/', $contents)) {
+ // 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data
+ //error_log("check_feed_favicon: favicon_url=$favicon_url isa PNG image");
+ }
+ elseif (preg_match('/^\xff\xd8/', $contents)) {
+ // 0 beshort 0xffd8 JPEG image data
+ //error_log("check_feed_favicon: favicon_url=$favicon_url isa JPG image");
+ }
+ elseif (preg_match('/^BM/', $contents)) {
+ // 0 string BM PC bitmap (OS2, Windows BMP files)
+ //error_log("check_feed_favicon, favicon_url=$favicon_url isa BMP image");
+ }
+ else {
+ //error_log("check_feed_favicon: favicon_url=$favicon_url isa UNKNOWN type");
+ Debug::log("favicon $favicon_url type is unknown (not updating)", Debug::$LOG_VERBOSE);
+ return false;
+ }
+
+ Debug::log("setting contents of $icon_file", Debug::$LOG_VERBOSE);
+
+ $fp = @fopen($icon_file, "w");
+ if (!$fp) {
+ Debug::log("failed to open $icon_file for writing", Debug::$LOG_VERBOSE);
+ return false;
+ }
+
+ fwrite($fp, $contents);
+ fclose($fp);
+ chmod($icon_file, 0644);
+ clearstatcache();
+
+ return $icon_file;
}
static function is_gzipped($feed_data) {
@@ -1706,7 +1717,7 @@ class RSSUtils {
$filters = array();
$feed_id = (int) $feed_id;
- $cat_id = (int)Feeds::getFeedCategory($feed_id);
+ $cat_id = (int)Feeds::_cat_of_feed($feed_id);
if ($cat_id == 0)
$null_cat_qpart = "cat_id IS NULL OR";
@@ -1720,7 +1731,7 @@ class RSSUtils {
$sth->execute([$owner_uid]);
$check_cats = array_merge(
- Feeds::getParentCategories($cat_id, $owner_uid),
+ Feeds::_get_parent_cats($cat_id, $owner_uid),
[$cat_id]);
$check_cats_str = join(",", $check_cats);
diff --git a/classes/urlhelper.php b/classes/urlhelper.php
index 8717d02c3..bf2e22a76 100644
--- a/classes/urlhelper.php
+++ b/classes/urlhelper.php
@@ -123,9 +123,9 @@ class UrlHelper {
'protocol_version'=> 1.1)
);
- if (defined('_HTTP_PROXY')) {
+ if (Config::get(Config::HTTP_PROXY)) {
$context_options['http']['request_fulluri'] = true;
- $context_options['http']['proxy'] = _HTTP_PROXY;
+ $context_options['http']['proxy'] = Config::get(Config::HTTP_PROXY);
}
$context = stream_context_create($context_options);
@@ -209,7 +209,7 @@ class UrlHelper {
$last_modified = isset($options["last_modified"]) ? $options["last_modified"] : "";
$useragent = isset($options["useragent"]) ? $options["useragent"] : false;
$followlocation = isset($options["followlocation"]) ? $options["followlocation"] : true;
- $max_size = isset($options["max_size"]) ? $options["max_size"] : MAX_DOWNLOAD_FILE_SIZE; // in bytes
+ $max_size = isset($options["max_size"]) ? $options["max_size"] : Config::get(Config::MAX_DOWNLOAD_FILE_SIZE); // in bytes
$http_accept = isset($options["http_accept"]) ? $options["http_accept"] : false;
$http_referrer = isset($options["http_referrer"]) ? $options["http_referrer"] : false;
@@ -231,7 +231,7 @@ class UrlHelper {
return false;
}
- if (!defined('NO_CURL') && function_exists('curl_init') && !ini_get("open_basedir")) {
+ if (function_exists('curl_init') && !ini_get("open_basedir")) {
$fetch_curl_used = true;
@@ -250,8 +250,8 @@ class UrlHelper {
if (count($curl_http_headers) > 0)
curl_setopt($ch, CURLOPT_HTTPHEADER, $curl_http_headers);
- curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout ? $timeout : FILE_FETCH_CONNECT_TIMEOUT);
- curl_setopt($ch, CURLOPT_TIMEOUT, $timeout ? $timeout : FILE_FETCH_TIMEOUT);
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout ? $timeout : Config::get(Config::FILE_FETCH_CONNECT_TIMEOUT));
+ curl_setopt($ch, CURLOPT_TIMEOUT, $timeout ? $timeout : Config::get(Config::FILE_FETCH_TIMEOUT));
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, !ini_get("open_basedir") && $followlocation);
curl_setopt($ch, CURLOPT_MAXREDIRS, 20);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
@@ -283,8 +283,8 @@ class UrlHelper {
curl_setopt($ch, CURLOPT_COOKIEJAR, "/dev/null");
}
- if (defined('_HTTP_PROXY')) {
- curl_setopt($ch, CURLOPT_PROXY, _HTTP_PROXY);
+ if (Config::get(Config::HTTP_PROXY)) {
+ curl_setopt($ch, CURLOPT_PROXY, Config::get(Config::HTTP_PROXY));
}
if ($post_query) {
@@ -395,7 +395,7 @@ class UrlHelper {
),
'method' => 'GET',
'ignore_errors' => true,
- 'timeout' => $timeout ? $timeout : FILE_FETCH_TIMEOUT,
+ 'timeout' => $timeout ? $timeout : Config::get(Config::FILE_FETCH_TIMEOUT),
'protocol_version'=> 1.1)
);
@@ -408,16 +408,16 @@ class UrlHelper {
if ($http_referrer)
array_push($context_options['http']['header'], "Referer: $http_referrer");
- if (defined('_HTTP_PROXY')) {
+ if (Config::get(Config::HTTP_PROXY)) {
$context_options['http']['request_fulluri'] = true;
- $context_options['http']['proxy'] = _HTTP_PROXY;
+ $context_options['http']['proxy'] = Config::get(Config::HTTP_PROXY);
}
$context = stream_context_create($context_options);
$old_error = error_get_last();
- $fetch_effective_url = self::resolve_redirects($url, $timeout ? $timeout : FILE_FETCH_CONNECT_TIMEOUT);
+ $fetch_effective_url = self::resolve_redirects($url, $timeout ? $timeout : Config::get(Config::FILE_FETCH_CONNECT_TIMEOUT));
if (!self::validate($fetch_effective_url, true)) {
$fetch_last_error = "URL received after redirection failed extended validation.";
diff --git a/classes/userhelper.php b/classes/userhelper.php
index c9c4dd102..82a2fe05f 100644
--- a/classes/userhelper.php
+++ b/classes/userhelper.php
@@ -2,7 +2,7 @@
class UserHelper {
static function authenticate(string $login = null, string $password = null, bool $check_only = false, string $service = null) {
- if (!SINGLE_USER_MODE) {
+ if (!Config::get(Config::SINGLE_USER_MODE)) {
$user_id = false;
$auth_module = false;
@@ -41,7 +41,7 @@ class UserHelper {
$_SESSION["user_agent"] = sha1($_SERVER['HTTP_USER_AGENT']);
$_SESSION["pwd_hash"] = $row["pwd_hash"];
- Pref_Prefs::initialize_user_prefs($_SESSION["uid"]);
+ Pref_Prefs::_init_user_prefs($_SESSION["uid"]);
return true;
}
@@ -64,7 +64,7 @@ class UserHelper {
$_SESSION["ip_address"] = UserHelper::get_user_ip();
- Pref_Prefs::initialize_user_prefs($_SESSION["uid"]);
+ Pref_Prefs::_init_user_prefs($_SESSION["uid"]);
return true;
}
@@ -88,26 +88,26 @@ class UserHelper {
static function login_sequence() {
$pdo = Db::pdo();
- if (SINGLE_USER_MODE) {
+ if (Config::get(Config::SINGLE_USER_MODE)) {
@session_start();
self::authenticate("admin", null);
startup_gettext();
self::load_user_plugins($_SESSION["uid"]);
} else {
- if (!validate_session()) $_SESSION["uid"] = false;
+ if (!\Sessions\validate_session()) $_SESSION["uid"] = false;
if (empty($_SESSION["uid"])) {
- if (AUTH_AUTO_LOGIN && self::authenticate(null, null)) {
+ if (Config::get(Config::AUTH_AUTO_LOGIN) && self::authenticate(null, null)) {
$_SESSION["ref_schema_version"] = get_schema_version(true);
} else {
self::authenticate(null, null, true);
}
if (empty($_SESSION["uid"])) {
- Pref_Users::logout_user();
+ UserHelper::logout();
- Handler_Public::render_login_form();
+ Handler_Public::_render_login_form();
exit;
}
@@ -157,4 +157,46 @@ class UserHelper {
return false;
}
+
+ static function logout() {
+ if (session_status() === PHP_SESSION_ACTIVE)
+ session_destroy();
+
+ if (isset($_COOKIE[session_name()])) {
+ setcookie(session_name(), '', time()-42000, '/');
+
+ }
+ session_commit();
+ }
+
+ static function reset_password($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", "
$login ", "
$tmp_user_pwd ");
+
+ if ($format_output)
+ print_notice($message);
+ else
+ print $message;
+
+ }
+ }
}
diff --git a/config.php-dist b/config.php-dist
index cd0ee0078..27006377d 100644
--- a/config.php-dist
+++ b/config.php-dist
@@ -1,153 +1,18 @@
System), syslog - logs to system log.
- // Setting this to blank uses PHP logging (usually to http server
- // error.log).
- // Note that feed updating daemons don't use this logging facility
- // for normal output.
-
- define('CONFIG_VERSION', 26);
- // Expected config version. Please update this option in config.php
- // if necessary (after migrating all new options from this file).
-
- // vim:ft=php
diff --git a/errors.php b/errors.php
deleted file mode 100644
index 597c8429c..000000000
--- a/errors.php
+++ /dev/null
@@ -1,60 +0,0 @@
- $error) {
-
- $error = preg_replace("/\n/", "", $error);
- $error = preg_replace("/\"/", "\\\"", $error);
-
- print "ERRORS[$id] = \"$error\";\n";
- }
- }
-?>
diff --git a/include/autoload.php b/include/autoload.php
index c02923dba..19e00b9ea 100644
--- a/include/autoload.php
+++ b/include/autoload.php
@@ -1,6 +1,4 @@
readICO($imageFile);
if(count($ico->images)==0)
diff --git a/include/controls.php b/include/controls.php
index 8f49e99c5..b65a166c2 100755
--- a/include/controls.php
+++ b/include/controls.php
@@ -1,347 +1,183 @@
";
- foreach ($values as $v) {
- if ($v == $default)
- $sel = "selected=\"1\"";
- else
- $sel = "";
+ foreach ($attributes as $k => $v) {
- $v = trim($v);
+ // special handling for "disabled"
+ if ($k === "disabled" && !sql_bool_to_bool($v))
+ continue;
- print "
$v ";
- }
- print "";
-}
+ $rv .= "$k=\"" . htmlspecialchars($v) . "\"";
+ }
-function print_select_hash($id, $default, $values, $attributes = "", $name = "") {
- if (!$name) $name = $id;
+ return $rv;
+ }
- print "
";
- foreach (array_keys($values) as $v) {
- if ($v == $default)
- $sel = 'selected="selected"';
- else
- $sel = "";
+ // shortcut syntax (disabled)
+ /* function pluginhandler_tags(\Plugin $plugin, string $method) {
+ return hidden_tag("op", strtolower(get_class($plugin) . \PluginHost::PUBLIC_METHOD_DELIMITER . $method));
+ } */
- $v = trim($v);
+ function public_method_tags(\Plugin $plugin, string $method) {
+ return hidden_tag("op", strtolower(get_class($plugin) . \PluginHost::PUBLIC_METHOD_DELIMITER . $method));
+ }
- print "".$values[$v]." ";
- }
+ function pluginhandler_tags(\Plugin $plugin, string $method) {
+ return hidden_tag("op", "pluginhandler") .
+ hidden_tag("plugin", strtolower(get_class($plugin))) .
+ hidden_tag("method", $method);
+ }
- print " ";
-}
+ function button_tag(string $value, string $type, array $attributes = []) {
+ return "
$value ";
+ }
-function format_hidden($name, $value) {
- return "
";
-}
+ function input_tag(string $name, string $value, string $type = "text", array $attributes = [], string $id = "") {
+ $attributes_str = attributes_to_string($attributes);
+ $dojo_type = strpos($attributes_str, "dojoType") === false ? "dojoType='dijit.form.TextBox'" : "";
-function print_hidden($name, $value) {
- print format_hidden($name, $value);
-}
+ return "
";
+ }
-function format_checkbox($id, $checked, $value = "", $attributes = "") {
- $checked_str = $checked ? "checked" : "";
- $value_str = $value ? "value=\"$value\"" : "";
+ function number_spinner_tag(string $name, string $value, array $attributes = [], string $id = "") {
+ return input_tag($name, $value, "text", array_merge(["dojoType" => "dijit.form.NumberSpinner"], $attributes), $id);
+ }
- return "
";
-}
+ function submit_tag(string $value, array $attributes = []) {
+ return button_tag($value, "submit", array_merge(["class" => "alt-primary"], $attributes));
+ }
-function print_checkbox($id, $checked, $value = "", $attributes = "") {
- print format_checkbox($id, $checked, $value, $attributes);
-}
+ function cancel_dialog_tag(string $value, array $attributes = []) {
+ return button_tag($value, "", array_merge(["onclick" => "App.dialogOf(this).hide()"], $attributes));
+ }
-function print_button($type, $value, $attributes = "") {
- print "
$value ";
-}
+ function icon(string $icon, array $attributes = []) {
+ return "$icon ";
+ }
-function print_radio($id, $default, $true_is, $values, $attributes = "") {
- foreach ($values as $v) {
+ function select_tag(string $name, $value, array $values, array $attributes = [], string $id = "") {
+ $attributes_str = attributes_to_string($attributes);
+ $dojo_type = strpos($attributes_str, "dojoType") === false ? "dojoType='fox.form.Select'" : "";
- if ($v == $default)
- $sel = "checked";
- else
- $sel = "";
+ $rv = "";
- if ($v == $true_is) {
- $sel .= " value=\"1\"";
- } else {
- $sel .= " value=\"0\"";
- }
+ foreach ($values as $v) {
+ $is_sel = ($v == $value) ? "selected=\"selected\"" : "";
- print " $v ";
+ $rv .= "".htmlspecialchars($v)." ";
+ }
- }
-}
+ $rv .= " ";
-function print_feed_multi_select($id, $default_ids = [],
- $attributes = "", $include_all_feeds = true,
- $root_id = null, $nest_level = 0) {
+ return $rv;
+ }
- $pdo = Db::pdo();
+ /*function select_labels(string $name, string $value, array $attributes = [], string $id = "") {
+ $values = \Labels::get_as_hash($_SESSION["uid"]);
- print_r(in_array("CAT:6",$default_ids));
+ return select_tag($name, $value, $values, $attributes, $id);
+ }*/
- if (!$root_id) {
- print "";
- if ($include_all_feeds) {
- $is_selected = (in_array("0", $default_ids)) ? "selected=\"1\"" : "";
- print "".__('All feeds')." ";
- }
- }
+ function select_hash(string $name, $value, array $values, array $attributes = [], string $id = "") {
+ $attributes_str = attributes_to_string($attributes);
+ $dojo_type = strpos($attributes_str, "dojoType") === false ? "dojoType='fox.form.Select'" : "";
- if (get_pref('ENABLE_FEED_CATS')) {
+ $rv = "";
- if (!$root_id) $root_id = null;
+ foreach ($values as $k => $v) {
+ $is_sel = ($k == $value) ? "selected=\"selected\"" : "";
- $sth = $pdo->prepare("SELECT id,title,
- (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
- c2.parent_cat = ttrss_feed_categories.id) AS num_children
- FROM ttrss_feed_categories
- WHERE owner_uid = :uid AND
- (parent_cat = :root_id OR (:root_id IS NULL AND parent_cat IS NULL)) ORDER BY title");
+ $rv .= "".htmlspecialchars($v)." ";
+ }
- $sth->execute([":uid" => $_SESSION['uid'], ":root_id" => $root_id]);
+ $rv .= " ";
- while ($line = $sth->fetch()) {
+ return $rv;
+ }
- for ($i = 0; $i < $nest_level; $i++)
- $line["title"] = " " . $line["title"];
+ function hidden_tag(string $name, string $value, array $attributes = []) {
+ return " ";
+ }
- $is_selected = in_array("CAT:".$line["id"], $default_ids) ? "selected=\"1\"" : "";
+ function checkbox_tag(string $name, bool $checked = false, string $value = "", array $attributes = [], string $id = "") {
+ $is_checked = $checked ? "checked" : "";
+ $value_str = $value ? "value=\"".htmlspecialchars($value)."\"" : "";
- printf("%s ",
- $line["id"], htmlspecialchars($line["title"]));
+ return " ";
+ }
- if ($line["num_children"] > 0)
- print_feed_multi_select($id, $default_ids, $attributes,
- $include_all_feeds, $line["id"], $nest_level+1);
+ function select_feeds_cats(string $name, int $default_id = null, array $attributes = [],
+ bool $include_all_cats = true, string $root_id = null, int $nest_level = 0, string $id = "") {
- $f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds
- WHERE cat_id = ? AND owner_uid = ? ORDER BY title");
+ $ret = "";
- $f_sth->execute([$line['id'], $_SESSION['uid']]);
+ if (!$root_id) {
+ $ret .= "";
+ }
- while ($fline = $f_sth->fetch()) {
- $is_selected = (in_array($fline["id"], $default_ids)) ? "selected=\"1\"" : "";
+ $pdo = \Db::pdo();
- $fline["title"] = " " . $fline["title"];
+ if (!$root_id) $root_id = null;
- for ($i = 0; $i < $nest_level; $i++)
- $fline["title"] = " " . $fline["title"];
+ $sth = $pdo->prepare("SELECT id,title,
+ (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
+ c2.parent_cat = ttrss_feed_categories.id) AS num_children
+ FROM ttrss_feed_categories
+ WHERE owner_uid = :uid AND
+ (parent_cat = :root_id OR (:root_id IS NULL AND parent_cat IS NULL)) ORDER BY title");
+ $sth->execute([":uid" => $_SESSION['uid'], ":root_id" => $root_id]);
- printf("%s ",
- $fline["id"], htmlspecialchars($fline["title"]));
- }
- }
+ $found = 0;
- if (!$root_id) {
- $is_selected = in_array("CAT:0", $default_ids) ? "selected=\"1\"" : "";
+ while ($line = $sth->fetch()) {
+ ++$found;
- printf("%s ",
- __("Uncategorized"));
+ if ($line["id"] == $default_id) {
+ $is_selected = "selected=\"1\"";
+ } else {
+ $is_selected = "";
+ }
- $f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds
- WHERE cat_id IS NULL AND owner_uid = ? ORDER BY title");
- $f_sth->execute([$_SESSION['uid']]);
+ for ($i = 0; $i < $nest_level; $i++)
+ $line["title"] = " " . $line["title"];
- while ($fline = $f_sth->fetch()) {
- $is_selected = in_array($fline["id"], $default_ids) ? "selected=\"1\"" : "";
+ if ($line["title"])
+ $ret .= sprintf("%s ",
+ $line["id"], htmlspecialchars($line["title"]));
- $fline["title"] = " " . $fline["title"];
+ if ($line["num_children"] > 0)
+ $ret .= select_feeds_cats($id, $default_id, $attributes,
+ $include_all_cats, $line["id"], $nest_level+1, $id);
+ }
- for ($i = 0; $i < $nest_level; $i++)
- $fline["title"] = " " . $fline["title"];
+ if (!$root_id) {
+ if ($include_all_cats) {
+ if ($found > 0) {
+ $ret .= "――――――――――――――― ";
+ }
- printf("%s ",
- $fline["id"], htmlspecialchars($fline["title"]));
- }
- }
+ if ($default_id == 0) {
+ $is_selected = "selected=\"1\"";
+ } else {
+ $is_selected = "";
+ }
- } else {
- $sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds
- WHERE owner_uid = ? ORDER BY title");
- $sth->execute([$_SESSION['uid']]);
+ $ret .= "".__('Uncategorized')." ";
+ }
+ $ret .= " ";
+ }
- while ($line = $sth->fetch()) {
-
- $is_selected = (in_array($line["id"], $default_ids)) ? "selected=\"1\"" : "";
-
- printf("%s ",
- $line["id"], htmlspecialchars($line["title"]));
- }
- }
-
- if (!$root_id) {
- print " ";
- }
-}
-
-function print_feed_cat_select($id, $default_id,
- $attributes, $include_all_cats = true, $root_id = null, $nest_level = 0) {
-
- if (!$root_id) {
- print "";
- }
-
- $pdo = Db::pdo();
-
- if (!$root_id) $root_id = null;
-
- $sth = $pdo->prepare("SELECT id,title,
- (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
- c2.parent_cat = ttrss_feed_categories.id) AS num_children
- FROM ttrss_feed_categories
- WHERE owner_uid = :uid AND
- (parent_cat = :root_id OR (:root_id IS NULL AND parent_cat IS NULL)) ORDER BY title");
- $sth->execute([":uid" => $_SESSION['uid'], ":root_id" => $root_id]);
-
- $found = 0;
-
- while ($line = $sth->fetch()) {
- ++$found;
-
- if ($line["id"] == $default_id) {
- $is_selected = "selected=\"1\"";
- } else {
- $is_selected = "";
- }
-
- for ($i = 0; $i < $nest_level; $i++)
- $line["title"] = " " . $line["title"];
-
- if ($line["title"])
- printf("%s ",
- $line["id"], htmlspecialchars($line["title"]));
-
- if ($line["num_children"] > 0)
- print_feed_cat_select($id, $default_id, $attributes,
- $include_all_cats, $line["id"], $nest_level+1);
- }
-
- if (!$root_id) {
- if ($include_all_cats) {
- if ($found > 0) {
- print "――――――――――――――― ";
- }
-
- if ($default_id == 0) {
- $is_selected = "selected=\"1\"";
- } else {
- $is_selected = "";
- }
-
- print "".__('Uncategorized')." ";
- }
- print " ";
- }
-}
-
-function stylesheet_tag($filename, $id = false) {
- $timestamp = filemtime($filename);
-
- $id_part = $id ? "id=\"$id\"" : "";
-
- return " \n";
-}
-
-function javascript_tag($filename) {
- $query = "";
-
- if (!(strpos($filename, "?") === false)) {
- $query = substr($filename, strpos($filename, "?")+1);
- $filename = substr($filename, 0, strpos($filename, "?"));
- }
-
- $timestamp = filemtime($filename);
-
- if ($query) $timestamp .= "&$query";
-
- return "\n";
-}
-
-function format_warning($msg, $id = "") {
- return "
$msg
";
-}
-
-function format_notice($msg, $id = "") {
- return "
$msg
";
-}
-
-function format_error($msg, $id = "") {
- return "
$msg
";
-}
-
-function print_notice($msg) {
- return print format_notice($msg);
-}
-
-function print_warning($msg) {
- return print format_warning($msg);
-}
-
-function print_error($msg) {
- return print format_error($msg);
-}
-
-function format_inline_player($url, $ctype) {
-
- $entry = "";
-
- $url = htmlspecialchars($url);
-
- if (strpos($ctype, "audio/") === 0) {
-
- $entry .= "
";
-
- if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
- $_SESSION["hasMp3"])) {
-
- $entry .= "
-
- ";
-
- }
-
- if ($entry) $entry .= "
" . basename($url) . " ";
-
- $entry .= "
";
-
- return $entry;
-
- }
-
- return "";
-}
-
-function print_label_select($name, $value, $attributes = "") {
-
- $pdo = Db::pdo();
-
- $sth = $pdo->prepare("SELECT caption FROM ttrss_labels2
- WHERE owner_uid = ? ORDER BY caption");
- $sth->execute([$_SESSION['uid']]);
-
- print "
";
-
- while ($line = $sth->fetch()) {
-
- $issel = ($line["caption"] == $value) ? "selected=\"1\"" : "";
-
- print "" . htmlspecialchars($line["caption"]) . " ";
-
- }
-
-# print "" .__("Add label...") . " ";
-
- print " ";
-
-
-}
+ return $ret;
+ }
diff --git a/include/controls_compat.php b/include/controls_compat.php
new file mode 100644
index 000000000..d62265471
--- /dev/null
+++ b/include/controls_compat.php
@@ -0,0 +1,314 @@
+\n";
+}
+
+function javascript_tag($filename) {
+ $query = "";
+
+ if (!(strpos($filename, "?") === false)) {
+ $query = substr($filename, strpos($filename, "?")+1);
+ $filename = substr($filename, 0, strpos($filename, "?"));
+ }
+
+ $timestamp = filemtime($filename);
+
+ if ($query) $timestamp .= "&$query";
+
+ return "\n";
+}
+
+function format_warning($msg, $id = "") {
+ return "
$msg
";
+}
+
+function format_notice($msg, $id = "") {
+ return "
$msg
";
+}
+
+function format_error($msg, $id = "") {
+ return "
$msg
";
+}
+
+function print_notice($msg) {
+ return print format_notice($msg);
+}
+
+function print_warning($msg) {
+ return print format_warning($msg);
+}
+
+function print_error($msg) {
+ return print format_error($msg);
+}
+
+// the following is deprecated and will be eventually removed
+
+/*function print_select($id, $default, $values, $attributes = "", $name = "") {
+ if (!$name) $name = $id;
+
+ print "
";
+ foreach ($values as $v) {
+ if ($v == $default)
+ $sel = "selected=\"1\"";
+ else
+ $sel = "";
+
+ $v = trim($v);
+
+ print "$v ";
+ }
+ print " ";
+}
+
+function print_select_hash($id, $default, $values, $attributes = "", $name = "") {
+ if (!$name) $name = $id;
+
+ print "
";
+ foreach (array_keys($values) as $v) {
+ if ($v == $default)
+ $sel = 'selected="selected"';
+ else
+ $sel = "";
+
+ $v = trim($v);
+
+ print "".$values[$v]." ";
+ }
+
+ print " ";
+}
+
+function format_hidden($name, $value) {
+ return "
";
+}
+
+function print_hidden($name, $value) {
+ print format_hidden($name, $value);
+}
+
+function format_checkbox($id, $checked, $value = "", $attributes = "") {
+ $checked_str = $checked ? "checked" : "";
+ $value_str = $value ? "value=\"$value\"" : "";
+
+ return "
";
+}
+
+function print_checkbox($id, $checked, $value = "", $attributes = "") {
+ print format_checkbox($id, $checked, $value, $attributes);
+}
+
+function format_button($type, $value, $attributes = "") {
+ return "
$value ";
+}
+
+function print_button($type, $value, $attributes = "") {
+ print format_button($type, $value, $attributes);
+}
+
+function print_feed_multi_select($id, $default_ids = [],
+ $attributes = "", $include_all_feeds = true,
+ $root_id = null, $nest_level = 0) {
+
+ $pdo = Db::pdo();
+
+ print_r(in_array("CAT:6",$default_ids));
+
+ if (!$root_id) {
+ print "
";
+ if ($include_all_feeds) {
+ $is_selected = (in_array("0", $default_ids)) ? "selected=\"1\"" : "";
+ print "".__('All feeds')." ";
+ }
+ }
+
+ if (get_pref('ENABLE_FEED_CATS')) {
+
+ if (!$root_id) $root_id = null;
+
+ $sth = $pdo->prepare("SELECT id,title,
+ (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
+ c2.parent_cat = ttrss_feed_categories.id) AS num_children
+ FROM ttrss_feed_categories
+ WHERE owner_uid = :uid AND
+ (parent_cat = :root_id OR (:root_id IS NULL AND parent_cat IS NULL)) ORDER BY title");
+
+ $sth->execute([":uid" => $_SESSION['uid'], ":root_id" => $root_id]);
+
+ while ($line = $sth->fetch()) {
+
+ for ($i = 0; $i < $nest_level; $i++)
+ $line["title"] = " " . $line["title"];
+
+ $is_selected = in_array("CAT:".$line["id"], $default_ids) ? "selected=\"1\"" : "";
+
+ printf("%s ",
+ $line["id"], htmlspecialchars($line["title"]));
+
+ if ($line["num_children"] > 0)
+ print_feed_multi_select($id, $default_ids, $attributes,
+ $include_all_feeds, $line["id"], $nest_level+1);
+
+ $f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds
+ WHERE cat_id = ? AND owner_uid = ? ORDER BY title");
+
+ $f_sth->execute([$line['id'], $_SESSION['uid']]);
+
+ while ($fline = $f_sth->fetch()) {
+ $is_selected = (in_array($fline["id"], $default_ids)) ? "selected=\"1\"" : "";
+
+ $fline["title"] = " " . $fline["title"];
+
+ for ($i = 0; $i < $nest_level; $i++)
+ $fline["title"] = " " . $fline["title"];
+
+ printf("%s ",
+ $fline["id"], htmlspecialchars($fline["title"]));
+ }
+ }
+
+ if (!$root_id) {
+ $is_selected = in_array("CAT:0", $default_ids) ? "selected=\"1\"" : "";
+
+ printf("%s ",
+ __("Uncategorized"));
+
+ $f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds
+ WHERE cat_id IS NULL AND owner_uid = ? ORDER BY title");
+ $f_sth->execute([$_SESSION['uid']]);
+
+ while ($fline = $f_sth->fetch()) {
+ $is_selected = in_array($fline["id"], $default_ids) ? "selected=\"1\"" : "";
+
+ $fline["title"] = " " . $fline["title"];
+
+ for ($i = 0; $i < $nest_level; $i++)
+ $fline["title"] = " " . $fline["title"];
+
+ printf("%s ",
+ $fline["id"], htmlspecialchars($fline["title"]));
+ }
+ }
+
+ } else {
+ $sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds
+ WHERE owner_uid = ? ORDER BY title");
+ $sth->execute([$_SESSION['uid']]);
+
+ while ($line = $sth->fetch()) {
+
+ $is_selected = (in_array($line["id"], $default_ids)) ? "selected=\"1\"" : "";
+
+ printf("%s ",
+ $line["id"], htmlspecialchars($line["title"]));
+ }
+ }
+
+ if (!$root_id) {
+ print " ";
+ }
+}
+
+function print_feed_cat_select($id, $default_id, $attributes, $include_all_cats = true,
+ $root_id = null, $nest_level = 0) {
+
+ print format_feed_cat_select($id, $default_id, $attributes, $include_all_cats, $root_id, $nest_level);
+}
+
+function format_feed_cat_select($id, $default_id, $attributes, $include_all_cats = true,
+ $root_id = null, $nest_level = 0) {
+
+ $ret = "";
+
+ if (!$root_id) {
+ $ret .= "
";
+ }
+
+ $pdo = Db::pdo();
+
+ if (!$root_id) $root_id = null;
+
+ $sth = $pdo->prepare("SELECT id,title,
+ (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
+ c2.parent_cat = ttrss_feed_categories.id) AS num_children
+ FROM ttrss_feed_categories
+ WHERE owner_uid = :uid AND
+ (parent_cat = :root_id OR (:root_id IS NULL AND parent_cat IS NULL)) ORDER BY title");
+ $sth->execute([":uid" => $_SESSION['uid'], ":root_id" => $root_id]);
+
+ $found = 0;
+
+ while ($line = $sth->fetch()) {
+ ++$found;
+
+ if ($line["id"] == $default_id) {
+ $is_selected = "selected=\"1\"";
+ } else {
+ $is_selected = "";
+ }
+
+ for ($i = 0; $i < $nest_level; $i++)
+ $line["title"] = " " . $line["title"];
+
+ if ($line["title"])
+ $ret .= sprintf("%s ",
+ $line["id"], htmlspecialchars($line["title"]));
+
+ if ($line["num_children"] > 0)
+ $ret .= format_feed_cat_select($id, $default_id, $attributes,
+ $include_all_cats, $line["id"], $nest_level+1);
+ }
+
+ if (!$root_id) {
+ if ($include_all_cats) {
+ if ($found > 0) {
+ $ret .= "――――――――――――――― ";
+ }
+
+ if ($default_id == 0) {
+ $is_selected = "selected=\"1\"";
+ } else {
+ $is_selected = "";
+ }
+
+ $ret .= "".__('Uncategorized')." ";
+ }
+ $ret .= " ";
+ }
+
+ return $ret;
+}
+
+function print_label_select($name, $value, $attributes = "") {
+
+ $pdo = Db::pdo();
+
+ $sth = $pdo->prepare("SELECT caption FROM ttrss_labels2
+ WHERE owner_uid = ? ORDER BY caption");
+ $sth->execute([$_SESSION['uid']]);
+
+ print "
";
+
+ while ($line = $sth->fetch()) {
+
+ $issel = ($line["caption"] == $value) ? "selected=\"1\"" : "";
+
+ print "" . htmlspecialchars($line["caption"]) . " ";
+
+ }
+
+# print "" .__("Add label...") . " ";
+
+ print " ";
+
+
+}
+*/
diff --git a/include/db-prefs.php b/include/db-prefs.php
deleted file mode 100644
index 91235b479..000000000
--- a/include/db-prefs.php
+++ /dev/null
@@ -1,10 +0,0 @@
-read($pref_name, $user_id, $die_on_error);
- }
-
- function set_pref($pref_name, $value, $user_id = false, $strip_tags = true) {
- return Db_Prefs::get()->write($pref_name, $value, $user_id, $strip_tags);
- }
\ No newline at end of file
diff --git a/include/db.php b/include/db.php
deleted file mode 100644
index c02e90ef2..000000000
--- a/include/db.php
+++ /dev/null
@@ -1,38 +0,0 @@
-escape_string($s, $strip_tags);
-}
-
-function db_query($query, $die_on_error = true) {
- return Db::get()->query($query, $die_on_error);
-}
-
-function db_fetch_assoc($result) {
- return Db::get()->fetch_assoc($result);
-}
-
-
-function db_num_rows($result) {
- return Db::get()->num_rows($result);
-}
-
-function db_fetch_result($result, $row, $param) {
- return Db::get()->fetch_result($result, $row, $param);
-}
-
-function db_affected_rows($result) {
- return Db::get()->affected_rows($result);
-}
-
-function db_last_error() {
- return Db::get()->last_error();
-}
-
-function db_last_query_error() {
- return Db::get()->last_query_error();
-}
-
-function db_quote($str){
- return Db::get()->quote($str);
-}
diff --git a/include/errorhandler.php b/include/errorhandler.php
index fab559b76..68e2285c1 100644
--- a/include/errorhandler.php
+++ b/include/errorhandler.php
@@ -48,7 +48,7 @@ function ttrss_error_handler($errno, $errstr, $file, $line) {
if (error_reporting() == 0 || !$errno) return false;
- $file = substr(str_replace(dirname(dirname(__FILE__)), "", $file), 1);
+ $file = substr(str_replace(dirname(__DIR__), "", $file), 1);
$context = format_backtrace(debug_backtrace());
$errstr = truncate_middle($errstr, 16384, " (...) ");
@@ -72,7 +72,7 @@ function ttrss_fatal_handler() {
$context = format_backtrace(debug_backtrace());
- $file = substr(str_replace(dirname(dirname(__FILE__)), "", $file), 1);
+ $file = substr(str_replace(dirname(__DIR__), "", $file), 1);
if ($last_query) $errstr .= " [Last query: $last_query]";
diff --git a/include/functions.php b/include/functions.php
index f870f3382..d916301fb 100644
--- a/include/functions.php
+++ b/include/functions.php
@@ -1,16 +1,9 @@
read($pref_name, $user_id, $die_on_error);
+ }
+
+ function set_pref($pref_name, $value, $user_id = false, $strip_tags = true) {
+ return Db_Prefs::get()->write($pref_name, $value, $user_id, $strip_tags);
+ }
+
function get_translations() {
- $tr = array(
+ $t = array(
"auto" => __("Detect automatically"),
"ar_SA" => "العربيّة (Arabic)",
"bg_BG" => "Bulgarian",
@@ -129,38 +82,76 @@
"fi_FI" => "Suomi",
"tr_TR" => "Türkçe");
- return $tr;
+ return $t;
}
- require_once "lib/accept-to-gettext.php";
require_once "lib/gettext/gettext.inc.php";
function startup_gettext() {
- # Get locale from Accept-Language header
- if (version_compare(PHP_VERSION, '8.0.0', '<')) {
- $lang = al2gt(array_keys(get_translations()), "text/html");
- } else {
- $lang = ""; // FIXME: do something with accept-to-gettext.php
- }
+ $selected_locale = "";
- if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) {
- $lang = _TRANSLATION_OVERRIDE_DEFAULT;
- }
+ // https://www.codingwithjesse.com/blog/use-accept-language-header/
+ if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
+ $valid_langs = [];
+ $translations = array_keys(get_translations());
- if (!empty($_SESSION["uid"]) && get_schema_version() >= 120) {
- $pref_lang = get_pref("USER_LANGUAGE", $_SESSION["uid"]);
+ array_shift($translations); // remove "auto"
- if ($pref_lang && $pref_lang != 'auto') {
- $lang = $pref_lang;
+ // full locale first
+ foreach ($translations as $t) {
+ $lang = strtolower(str_replace("_", "-", (string)$t));
+ $valid_langs[$lang] = $t;
+
+ $lang = substr($lang, 0, 2);
+ if (!isset($valid_langs[$lang]))
+ $valid_langs[$lang] = $t;
+ }
+
+ // break up string into pieces (languages and q factors)
+ preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i',
+ $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse);
+
+ if (count($lang_parse[1])) {
+ // create a list like "en" => 0.8
+ $langs = array_combine($lang_parse[1], $lang_parse[4]);
+
+ if (is_array($langs)) {
+ // set default to 1 for any without q factor
+ foreach ($langs as $lang => $val) {
+ if ($val === '') $langs[$lang] = 1;
+ }
+
+ // sort list based on value
+ arsort($langs, SORT_NUMERIC);
+
+ foreach (array_keys($langs) as $lang) {
+ $lang = strtolower($lang);
+
+ foreach ($valid_langs as $vlang => $vlocale) {
+ if ($vlang == $lang) {
+ $selected_locale = $vlocale;
+ break 2;
+ }
+ }
+ }
+ }
}
}
- if ($lang) {
+ if (!empty($_SESSION["uid"]) && get_schema_version() >= 120) {
+ $pref_locale = get_pref("USER_LANGUAGE", $_SESSION["uid"]);
+
+ if (!empty($pref_locale) && $pref_locale != 'auto') {
+ $selected_locale = $pref_locale;
+ }
+ }
+
+ if ($selected_locale) {
if (defined('LC_MESSAGES')) {
- _setlocale(LC_MESSAGES, $lang);
+ _setlocale(LC_MESSAGES, $selected_locale);
} else if (defined('LC_ALL')) {
- _setlocale(LC_ALL, $lang);
+ _setlocale(LC_ALL, $selected_locale);
}
_bindtextdomain("messages", "locale");
@@ -169,8 +160,8 @@
}
}
- require_once 'db-prefs.php';
require_once 'controls.php';
+ require_once 'controls_compat.php';
define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . get_version() . ' (http://tt-rss.org/)');
ini_set('user_agent', SELF_USER_AGENT);
@@ -185,7 +176,7 @@
// @deprecated
function getFeedUnread($feed, $is_cat = false) {
- return Feeds::getFeedArticles($feed, $is_cat, true, $_SESSION["uid"]);
+ return Feeds::_get_counters($feed, $is_cat, true, $_SESSION["uid"]);
}
// @deprecated
@@ -248,7 +239,7 @@
} else if (is_string($param)) {
return trim(strip_tags($param));
} else {
- return trim($param);
+ return $param;
}
}
@@ -278,7 +269,7 @@
}
function validate_csrf($csrf_token) {
- return isset($csrf_token) && hash_equals($_SESSION['csrf_token'], $csrf_token);
+ return isset($csrf_token) && hash_equals($_SESSION['csrf_token'] ?? "", $csrf_token);
}
function truncate_string($str, $max_len, $suffix = '…') {
@@ -332,24 +323,10 @@
}
}
- function sanity_check() {
- require_once 'errors.php';
- $ERRORS = get_error_types();
-
- $error_code = 0;
- $schema_version = get_schema_version(true);
-
- if ($schema_version != SCHEMA_VERSION) {
- $error_code = 5;
- }
-
- return array("code" => $error_code, "message" => $ERRORS[$error_code]);
- }
-
function file_is_locked($filename) {
- if (file_exists(LOCK_DIRECTORY . "/$filename")) {
+ if (file_exists(Config::get(Config::LOCK_DIRECTORY) . "/$filename")) {
if (function_exists('flock')) {
- $fp = @fopen(LOCK_DIRECTORY . "/$filename", "r");
+ $fp = @fopen(Config::get(Config::LOCK_DIRECTORY) . "/$filename", "r");
if ($fp) {
if (flock($fp, LOCK_EX | LOCK_NB)) {
flock($fp, LOCK_UN);
@@ -369,11 +346,11 @@
}
function make_lockfile($filename) {
- $fp = fopen(LOCK_DIRECTORY . "/$filename", "w");
+ $fp = fopen(Config::get(Config::LOCK_DIRECTORY) . "/$filename", "w");
if ($fp && flock($fp, LOCK_EX | LOCK_NB)) {
$stat_h = fstat($fp);
- $stat_f = stat(LOCK_DIRECTORY . "/$filename");
+ $stat_f = stat(Config::get(Config::LOCK_DIRECTORY) . "/$filename");
if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
if ($stat_h["ino"] != $stat_f["ino"] ||
@@ -397,7 +374,7 @@
}
function uniqid_short() {
- return uniqid(base_convert(rand(), 10, 36));
+ return uniqid(base_convert((string)rand(), 10, 36));
}
function T_sprintf() {
@@ -416,15 +393,15 @@
}
function is_prefix_https() {
- return parse_url(SELF_URL_PATH, PHP_URL_SCHEME) == 'https';
+ return parse_url(Config::get(Config::SELF_URL_PATH), PHP_URL_SCHEME) == 'https';
}
- // this returns SELF_URL_PATH sans ending slash
+ // this returns Config::get(Config::SELF_URL_PATH) sans ending slash
function get_self_url_prefix() {
- if (strrpos(SELF_URL_PATH, "/") === strlen(SELF_URL_PATH)-1) {
- return substr(SELF_URL_PATH, 0, strlen(SELF_URL_PATH)-1);
+ if (strrpos(Config::get(Config::SELF_URL_PATH), "/") === strlen(Config::get(Config::SELF_URL_PATH))-1) {
+ return substr(Config::get(Config::SELF_URL_PATH), 0, strlen(Config::get(Config::SELF_URL_PATH))-1);
} else {
- return SELF_URL_PATH;
+ return Config::get(Config::SELF_URL_PATH);
}
}
@@ -439,7 +416,7 @@
} // function encrypt_password
function init_plugins() {
- PluginHost::getInstance()->load(PLUGINS, PluginHost::KIND_ALL);
+ PluginHost::getInstance()->load(Config::get(Config::PLUGINS), PluginHost::KIND_ALL);
return true;
}
@@ -542,20 +519,6 @@
return file_exists("themes/$theme") || file_exists("themes.local/$theme");
}
- /**
- * @SuppressWarnings(unused)
- */
- function error_json($code) {
- require_once "errors.php";
- $ERRORS = get_error_types();
-
- @$message = $ERRORS[$code];
-
- return json_encode(array("error" =>
- array("code" => $code, "message" => $message)));
-
- }
-
function arr_qmarks($arr) {
return str_repeat('?,', count($arr) - 1) . '?';
}
@@ -592,7 +555,7 @@
$ttrss_version['version'] = "UNKNOWN (Unsupported)";
date_default_timezone_set('UTC');
- $root_dir = dirname(dirname(__FILE__));
+ $root_dir = dirname(__DIR__);
if (PHP_OS === "Darwin") {
$ttrss_version['version'] = "UNKNOWN (Unsupported, Darwin)";
diff --git a/include/login_form.php b/include/login_form.php
index aec305b13..06bf57470 100755
--- a/include/login_form.php
+++ b/include/login_form.php
@@ -6,20 +6,17 @@
-
-
+
+
+
+
URL:
-
+
Article URL:
-
+
Test
@@ -694,7 +701,7 @@ class Af_RedditImgur extends Plugin {
private function get_header($url, $header, $useragent = SELF_USER_AGENT) {
$ret = false;
- if (function_exists("curl_init") && !defined("NO_CURL")) {
+ if (function_exists("curl_init")) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
@@ -720,7 +727,7 @@ class Af_RedditImgur extends Plugin {
private function readability($article, $url, $doc, $xpath, $debug = false) {
- if (!defined('NO_CURL') && function_exists("curl_init") && $this->host->get($this, "enable_readability") &&
+ if (function_exists("curl_init") && $this->host->get($this, "enable_readability") &&
mb_strlen(strip_tags($article["content"])) <= 150) {
// do not try to embed posts linking back to other reddit posts
diff --git a/plugins/af_tumblr_1280/init.php b/plugins/af_tumblr_1280/init.php
deleted file mode 100755
index 5d7f366a4..000000000
--- a/plugins/af_tumblr_1280/init.php
+++ /dev/null
@@ -1,91 +0,0 @@
- true);
- }
-
- function init($host) {
- $this->host = $host;
-
- if (function_exists("curl_init")) {
- $host->add_hook($host::HOOK_ARTICLE_FILTER, $this);
- }
- }
-
- function hook_article_filter($article) {
-
- if (!function_exists("curl_init") || ini_get("open_basedir"))
- return $article;
-
- $doc = new DOMDocument();
- $doc->loadHTML('' . $article["content"]);
-
- $found = false;
-
- if ($doc) {
- $xpath = new DOMXpath($doc);
-
- $images = $xpath->query('(//img[contains(@src, \'media.tumblr.com\')])');
-
- foreach ($images as $img) {
- $src = $img->getAttribute("src");
-
- $test_src = preg_replace("/_\d{3}.(jpg|gif|png)/", "_1280.$1", $src);
-
- if ($src != $test_src) {
-
- $ch = curl_init($test_src);
- curl_setopt($ch, CURLOPT_TIMEOUT, 5);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($ch, CURLOPT_HEADER, true);
- curl_setopt($ch, CURLOPT_NOBODY, true);
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
- curl_setopt($ch, CURLOPT_USERAGENT, SELF_USER_AGENT);
-
- @$result = curl_exec($ch);
- $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
-
- if ($result && $http_code == 200) {
- $img->setAttribute("src", $test_src);
- $found = true;
- }
- }
- }
-
- $video_sources = $xpath->query('//video/source[contains(@src, \'.tumblr.com/video_file\')]');
-
- foreach ($video_sources as $source) {
- $src = $source->getAttribute("src");
-
- $new_src = preg_replace("/\/\d{3}$/", "", $src);
-
- if ($src != $new_src) {
- $source->setAttribute("src", $new_src);
- $found = true;
- }
- }
-
- if ($found) {
- $doc->removeChild($doc->firstChild); //remove doctype
- $article["content"] = $doc->saveHTML();
- }
- }
-
- return $article;
-
- }
-
-
- function api_version() {
- return 2;
- }
-
-}
diff --git a/plugins/af_unburn/init.php b/plugins/af_unburn/init.php
index 4d0c56740..386b6387f 100755
--- a/plugins/af_unburn/init.php
+++ b/plugins/af_unburn/init.php
@@ -21,7 +21,7 @@ class Af_Unburn extends Plugin {
function hook_article_filter($article) {
$owner_uid = $article["owner_uid"];
- if (defined('NO_CURL') || !function_exists("curl_init") || ini_get("open_basedir"))
+ if (!function_exists("curl_init") || ini_get("open_basedir"))
return $article;
if ((strpos($article["link"], "feedproxy.google.com") !== false ||
@@ -37,8 +37,8 @@ class Af_Unburn extends Plugin {
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_USERAGENT, SELF_USER_AGENT);
- if (defined('_CURL_HTTP_PROXY')) {
- curl_setopt($ch, CURLOPT_PROXY, _CURL_HTTP_PROXY);
+ if (Config::get(Config::HTTP_PROXY)) {
+ curl_setopt($ch, CURLOPT_PROXY, Config::get(Config::HTTP_PROXY));
}
@curl_exec($ch);
@@ -80,4 +80,4 @@ class Af_Unburn extends Plugin {
return 2;
}
-}
\ No newline at end of file
+}
diff --git a/plugins/af_youtube_embed/init.php b/plugins/af_youtube_embed/init.php
index db82dc9f5..6309aac02 100644
--- a/plugins/af_youtube_embed/init.php
+++ b/plugins/af_youtube_embed/init.php
@@ -23,9 +23,9 @@ class Af_Youtube_Embed extends Plugin {
$matches = array();
- if (preg_match("/\/\/www\.youtube\.com\/v\/([\w-]+)/", $entry["url"], $matches) ||
- preg_match("/\/\/www\.youtube\.com\/watch?v=([\w-]+)/", $entry["url"], $matches) ||
- preg_match("/\/\/youtu.be\/([\w-]+)/", $entry["url"], $matches)) {
+ if (preg_match("/\/\/www\.youtube\.com\/v\/([\w-]+)/", $entry["content_url"], $matches) ||
+ preg_match("/\/\/www\.youtube\.com\/watch?v=([\w-]+)/", $entry["content_url"], $matches) ||
+ preg_match("/\/\/youtu.be\/([\w-]+)/", $entry["content_url"], $matches)) {
$vid_id = $matches[1];
diff --git a/plugins/af_zz_vidmute/init.js b/plugins/af_zz_vidmute/init.js
index fab9b99e6..b8be8cecd 100644
--- a/plugins/af_zz_vidmute/init.js
+++ b/plugins/af_zz_vidmute/init.js
@@ -3,7 +3,7 @@ require(['dojo/_base/kernel', 'dojo/ready'], function (dojo, ready) {
PluginHost.register(PluginHost.HOOK_ARTICLE_RENDERED_CDM, function (row) {
if (row) {
- row.select("video").each(function (v) {
+ row.querySelectorAll("video").forEach(function (v) {
v.muted = true;
});
}
@@ -14,7 +14,7 @@ require(['dojo/_base/kernel', 'dojo/ready'], function (dojo, ready) {
PluginHost.register(PluginHost.HOOK_ARTICLE_RENDERED, function (row) {
if (row) {
- row.select("video").each(function (v) {
+ row.querySelectorAll("video").forEach(function (v) {
v.muted = true;
});
}
diff --git a/plugins/auth_internal/init.php b/plugins/auth_internal/init.php
index a69ea444c..13a7bc969 100644
--- a/plugins/auth_internal/init.php
+++ b/plugins/auth_internal/init.php
@@ -63,21 +63,21 @@ class Auth_Internal extends Auth_Base {
Tiny Tiny RSS
-
+ = stylesheet_tag("themes/light.css") ?>
-
+ = __("Authentication") ?>
-
-
-
- ">
- ">
- ">
+
+
+ ">
+ ">
+ ">
-
+ = __("Please enter your one time password:") ?>
@@ -244,7 +244,7 @@ class Auth_Internal extends Auth_Base {
$tpl->readTemplateFromFile("password_change_template.txt");
$tpl->setVariable('LOGIN', $row["login"]);
- $tpl->setVariable('TTRSS_HOST', SELF_URL_PATH);
+ $tpl->setVariable('TTRSS_HOST', Config::get(Config::SELF_URL_PATH));
$tpl->addBlock('message');
diff --git a/plugins/auth_remote/init.php b/plugins/auth_remote/init.php
index 85be67d05..f2dcfb318 100644
--- a/plugins/auth_remote/init.php
+++ b/plugins/auth_remote/init.php
@@ -56,7 +56,7 @@ class Auth_Remote extends Auth_Base {
$_SESSION["hide_logout"] = true;
// LemonLDAP can send user informations via HTTP HEADER
- if (defined('AUTH_AUTO_CREATE') && AUTH_AUTO_CREATE){
+ if (Config::get(Config::AUTH_AUTO_CREATE)) {
// update user name
$fullname = isset($_SERVER['HTTP_USER_NAME']) ? $_SERVER['HTTP_USER_NAME'] : ($_SERVER['AUTHENTICATE_CN'] ?? "");
if ($fullname){
diff --git a/plugins/auto_assign_labels/init.php b/plugins/auto_assign_labels/init.php
index 3fa4ad8c0..341895cef 100755
--- a/plugins/auto_assign_labels/init.php
+++ b/plugins/auto_assign_labels/init.php
@@ -19,6 +19,7 @@ class Auto_Assign_Labels extends Plugin {
function get_all_labels_filter_format($owner_uid) {
$rv = array();
+ // TODO: use Labels::get_all()
$sth = $this->pdo->prepare("SELECT id, fg_color, bg_color, caption FROM ttrss_labels2 WHERE owner_uid = ?");
$sth->execute([$owner_uid]);
diff --git a/plugins/bookmarklets/init.php b/plugins/bookmarklets/init.php
index fa1bb8cf6..967918823 100644
--- a/plugins/bookmarklets/init.php
+++ b/plugins/bookmarklets/init.php
@@ -1,53 +1,371 @@
host = $host;
+ function init($host) {
+ $this->host = $host;
- $host->add_hook($host::HOOK_PREFS_TAB, $this);
- }
+ $host->add_hook($host::HOOK_PREFS_TAB, $this);
+ }
- function hook_prefs_tab($args) {
- if ($args == "prefFeeds") {
+ function is_public_method($method) {
+ return in_array($method, ["subscribe", "sharepopup"]);
+ }
- print "bookmark ".__('Bookmarklets')."\">";
+ function subscribe() {
+ if (Config::get(Config::SINGLE_USER_MODE)) {
+ UserHelper::login_sequence();
+ }
- print "
" . __("Drag the link below to your browser toolbar, open the feed you're interested in in your browser and click on the link to subscribe to it.") . " ";
+ if (!empty($_SESSION["uid"])) {
- $bm_subscribe_url = str_replace('%s', '', Pref_Feeds::subscribe_to_feed_url());
+ $feed_url = clean($_REQUEST["feed_url"] ?? "");
+ $csrf_token = clean($_POST["csrf_token"] ?? "");
- $confirm_str = str_replace("'", "\'", __('Subscribe to %s in Tiny Tiny RSS?'));
+ header('Content-Type: text/html; charset=utf-8');
+ ?>
+
+
+
+
= __("Subscribe to feed...") ?>
+ = javascript_tag("lib/dojo/dojo.js") ?>
+ = javascript_tag("js/utility.js") ?>
+ = javascript_tag("js/common.js") ?>
+ = javascript_tag("lib/dojo/tt-rss-layer.js") ?>
+
+
+
+
+
+
+
+
+
= __("Subscribe to feed...") ?>
+
+
";
- print "" . __('Subscribe in Tiny Tiny RSS'). " ";
- print " ";
+ if (!$feed_url || !validate_csrf($csrf_token)) {
+ ?>
+
+ = \Controls\public_method_tags($this, "subscribe") ?>
+ = \Controls\hidden_tag("csrf_token", $_SESSION["csrf_token"]) ?>
- print "" . __("Use this bookmarklet to publish arbitrary pages using Tiny Tiny RSS") . " ";
+
+ Feed or site URL:
+
+
- print "";
- $bm_url = htmlspecialchars("javascript:(function(){var d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),f='".get_self_url_prefix()."/public.php?op=sharepopup',l=d.location,e=encodeURIComponent,g=f+'&title='+((e(s))?e(s):e(document.title))+'&url='+e(l.href);function a(){if(!w.open(g,'t','toolbar=0,resizable=0,scrollbars=1,status=1,width=500,height=250')){l.href=g;}}a();})()");
- print "" . __('Share with Tiny Tiny RSS'). " ";
- print " ";
+
+ = __("Subscribe") ?>
+
- print "
- help ".__("More info...")." ";
+ = __("Return to Tiny Tiny RSS") ?>
+
+ "; #pane
+ $rc = Feeds::_subscribe($feed_url);
+ $feed_urls = false;
- }
- }
+ switch ($rc['code']) {
+ case 0:
+ print_warning(T_sprintf("Already subscribed to
%s .", $feed_url));
+ break;
+ case 1:
+ print_notice(T_sprintf("Subscribed to
%s .", $feed_url));
+ break;
+ case 2:
+ print_error(T_sprintf("Could not subscribe to
%s .", $feed_url));
+ break;
+ case 3:
+ print_error(T_sprintf("No feeds found in
%s .", $feed_url));
+ break;
+ case 4:
+ $feed_urls = $rc["feeds"];
+ break;
+ case 5:
+ print_error(T_sprintf("Could not subscribe to
%s .
Can't download the Feed URL.", $feed_url));
+ break;
+ }
+
+ if ($feed_urls) {
+ ?>
+
+ = \Controls\public_method_tags($this, "subscribe") ?>
+ = \Controls\hidden_tag("csrf_token", $_SESSION["csrf_token"]) ?>
+
+
+ = __("Multiple feed URLs found:") ?>
+
+ $name) { ?>
+ = htmlspecialchars($name) ?>
+
+
+
+
+ = __("Subscribe to selected feed") ?>
+ = __("Return to Tiny Tiny RSS") ?>
+
+
+
">
+
+
+
+ = __("Edit subscription options") ?>
+ = __("Return to Tiny Tiny RSS") ?>
+
+
+
= __("Return to Tiny Tiny RSS") ?>
+
+
+
+
+
+
+
+
+
+
= __("Share with Tiny Tiny RSS") ?>
+ = javascript_tag("lib/dojo/dojo.js") ?>
+ = javascript_tag("js/utility.js") ?>
+ = javascript_tag("js/common.js") ?>
+ = javascript_tag("lib/dojo/tt-rss-layer.js") ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ = \Controls\public_method_tags($this, "sharepopup") ?>
+ = \Controls\hidden_tag("csrf_token", $_SESSION["csrf_token"]) ?>
+ = \Controls\hidden_tag("action", "share") ?>
+
+
+ = __("Title:") ?>
+
+
+
+
+ = __("URL:") ?>
+
+
+
+
+ = __("Content:") ?>
+
+
+
+
+ = __("Labels:") ?>
+
+
+
+
+
+
+
+ = \Controls\submit_tag(__("Share")) ?>
+ = \Controls\button_tag(__("Cancel"), "", ["onclick" => "window.close()"]) ?>
+ = __("Shared article will appear in the Published feed.") ?>
+
+
+
+
+
+
+
+
+
+
+ = __("Login:") ?>
+ " />
+
+
+
+ = __("Password:") ?>
+
+ "/>
+
+
+
+
+
+
+
+ = __('Log in') ?>
+
+
+
+
+
+
+
+ host->get_public_method_url($this, "subscribe");
+ $bm_share_url = $this->host->get_public_method_url($this, "sharepopup");
+
+ $confirm_str = str_replace("'", "\'", __('Subscribe to %s in Tiny Tiny RSS?'));
+
+ $bm_subscribe_url = htmlspecialchars("javascript:{if(confirm('$confirm_str'.replace('%s',window.location.href)))window.location.href='$bm_subscribe_url&feed_url='+encodeURIComponent(window.location.href)}");
+ $bm_share_url = htmlspecialchars("javascript:(function(){var d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),f='$bm_share_url',l=d.location,e=encodeURIComponent,g=f+'&title='+((e(s))?e(s):e(document.title))+'&url='+e(l.href);function a(){if(!w.open(g,'t','toolbar=0,resizable=0,scrollbars=1,status=1,width=500,height=250')){l.href=g;}}a();})()");
+ ?>
+
+
+
+
= __("Drag the link below to your browser toolbar, open the feed you're interested in in your browser and click on the link to subscribe to it.") ?>
+
+
+ = __('Subscribe in Tiny Tiny RSS') ?>
+
+
+
= __("Use this bookmarklet to publish arbitrary pages using Tiny Tiny RSS") ?>
+
+
+ = __('Share with Tiny Tiny RSS') ?>
+
+
+ = \Controls\button_tag(\Controls\icon("help") . " " . __("More info..."), "",
+ ["class" => 'alt-info', "onclick" => "window.open('https://tt-rss.org/wiki/ShareAnything')"]) ?>
+
+
+
+ host = $host;
$this->cache = new DiskCache("starred-images");
- if ($this->cache->makeDir())
- chmod($this->cache->getDir(), 0777);
+ if ($this->cache->make_dir())
+ chmod($this->cache->get_dir(), 0777);
if (!$this->cache->exists(".no-auto-expiry"))
$this->cache->touch(".no-auto-expiry");
- if ($this->cache->isWritable()) {
+ if ($this->cache->is_writable()) {
$host->add_hook($host::HOOK_HOUSE_KEEPING, $this);
$host->add_hook($host::HOOK_ENCLOSURE_ENTRY, $this);
$host->add_hook($host::HOOK_SANITIZE, $this);
} else {
- user_error("Starred cache directory ".$this->cache->getDir()." is not writable.", E_USER_WARNING);
+ user_error("Starred cache directory ".$this->cache->get_dir()." is not writable.", E_USER_WARNING);
}
}
@@ -38,13 +38,13 @@ class Cache_Starred_Images extends Plugin {
Debug::log("caching media of starred articles for user " . $this->host->get_owner_uid() . "...");
$sth = $this->pdo->prepare("SELECT content, ttrss_entries.title,
- ttrss_user_entries.owner_uid, link, site_url, ttrss_entries.id, plugin_data
+ ttrss_user_entries.owner_uid, link, site_url, ttrss_entries.id, plugin_data
FROM ttrss_entries, ttrss_user_entries LEFT JOIN ttrss_feeds ON
(ttrss_user_entries.feed_id = ttrss_feeds.id)
WHERE ref_id = ttrss_entries.id AND
marked = true AND
site_url != '' AND
- ttrss_user_entries.owner_uid = ? AND
+ ttrss_user_entries.owner_uid = ? AND
plugin_data NOT LIKE '%starred_cache_images%'
ORDER BY ".Db::sql_random_function()." LIMIT 100");
@@ -59,7 +59,7 @@ class Cache_Starred_Images extends Plugin {
$success = $this->cache_article_images($line["content"], $line["site_url"], $line["owner_uid"], $line["id"]);
if ($success) {
- $plugin_data = "starred_cache_images,${line['owner_uid']}:" . $line["plugin_data"];
+ $plugin_data = "starred_cache_images," . $line["owner_uid"] . ":" . $line["plugin_data"];
$usth->execute([$plugin_data, $line['id']]);
}
@@ -69,9 +69,12 @@ class Cache_Starred_Images extends Plugin {
/* actual housekeeping */
- Debug::log("expiring " . $this->cache->getDir() . "...");
+ Debug::log("expiring " . $this->cache->get_dir() . "...");
- $files = glob($this->cache->getDir() . "/*.{png,mp4,status}", GLOB_BRACE);
+ $files = array_merge(
+ glob($this->cache->get_dir() . "/*.png"),
+ glob($this->cache->get_dir() . "/*.mp4"),
+ glob($this->cache->get_dir() . "/*.status"));
$last_article_id = 0;
$article_exists = 1;
@@ -98,14 +101,14 @@ class Cache_Starred_Images extends Plugin {
$local_filename = $article_id . "-" . sha1($enc["content_url"]);
if ($this->cache->exists($local_filename)) {
- $enc["content_url"] = $this->cache->getUrl($local_filename);
+ $enc["content_url"] = $this->cache->get_url($local_filename);
}
return $enc;
}
function hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes, $article_id) {
- $xpath = new DOMXpath($doc);
+ $xpath = new DOMXPath($doc);
if ($article_id) {
$entries = $xpath->query('(//img[@src])|(//video/source[@src])');
@@ -117,7 +120,7 @@ class Cache_Starred_Images extends Plugin {
$local_filename = $article_id . "-" . sha1($src);
if ($this->cache->exists($local_filename)) {
- $entry->setAttribute("src", $this->cache->getUrl($local_filename));
+ $entry->setAttribute("src", $this->cache->get_url($local_filename));
$entry->removeAttribute("srcset");
}
}
@@ -133,7 +136,7 @@ class Cache_Starred_Images extends Plugin {
if (!$this->cache->exists($local_filename)) {
Debug::log("cache_images: downloading: $url to $local_filename", Debug::$LOG_VERBOSE);
- $data = UrlHelper::fetch(["url" => $url, "max_size" => MAX_CACHE_FILE_SIZE]);
+ $data = UrlHelper::fetch(["url" => $url, "max_size" => Config::get(Config::MAX_CACHE_FILE_SIZE)]);
if ($data)
return $this->cache->put($local_filename, $data);;
@@ -151,37 +154,37 @@ class Cache_Starred_Images extends Plugin {
$status_filename = $article_id . "-" . sha1($site_url) . ".status";
/* housekeeping might run as a separate user, in this case status/media might not be writable */
- if (!$this->cache->isWritable($status_filename)) {
+ if (!$this->cache->is_writable($status_filename)) {
Debug::log("status not writable: $status_filename", Debug::$LOG_VERBOSE);
return false;
}
Debug::log("status: $status_filename", Debug::$LOG_VERBOSE);
- if ($this->cache->exists($status_filename))
- $status = json_decode($this->cache->get($status_filename), true);
- else
- $status = [];
+ if ($this->cache->exists($status_filename))
+ $status = json_decode($this->cache->get($status_filename), true);
+ else
+ $status = ["attempt" => 0];
- $status["attempt"] += 1;
+ $status["attempt"] += 1;
- // only allow several download attempts for article
- if ($status["attempt"] > $this->max_cache_attempts) {
- Debug::log("too many attempts for $site_url", Debug::$LOG_VERBOSE);
- return false;
- }
+ // only allow several download attempts for article
+ if ($status["attempt"] > $this->max_cache_attempts) {
+ Debug::log("too many attempts for $site_url", Debug::$LOG_VERBOSE);
+ return false;
+ }
- if (!$this->cache->put($status_filename, json_encode($status))) {
- user_error("unable to write status file: $status_filename", E_USER_WARNING);
- return false;
- }
+ if (!$this->cache->put($status_filename, json_encode($status))) {
+ user_error("unable to write status file: $status_filename", E_USER_WARNING);
+ return false;
+ }
$doc = new DOMDocument();
$has_images = false;
$success = false;
- if (@$doc->loadHTML('' . $content)) {
+ if (@$doc->loadHTML('' . $content)) {
$xpath = new DOMXPath($doc);
$entries = $xpath->query('(//img[@src])|(//video/source[@src])');
@@ -203,11 +206,11 @@ class Cache_Starred_Images extends Plugin {
$esth = $this->pdo->prepare("SELECT content_url FROM ttrss_enclosures WHERE post_id = ? AND
(content_type LIKE '%image%' OR content_type LIKE '%video%')");
- if ($esth->execute([$article_id])) {
- while ($enc = $esth->fetch()) {
+ if ($esth->execute([$article_id])) {
+ while ($enc = $esth->fetch()) {
- $has_images = true;
- $url = rewrite_relative_url($site_url, $enc["content_url"]);
+ $has_images = true;
+ $url = rewrite_relative_url($site_url, $enc["content_url"]);
if ($this->cache_url($article_id, $url)) {
$success = true;
diff --git a/plugins/close_button/init.php b/plugins/close_button/init.php
index a2ba89478..4f33d1af0 100644
--- a/plugins/close_button/init.php
+++ b/plugins/close_button/init.php
@@ -15,7 +15,7 @@ class Close_Button extends Plugin {
}
function get_css() {
- return "i.icon-close-article { color : red; }";
+ return ".post .header .buttons i.material-icons.icon-close-article { color : red; }";
}
function hook_article_button($line) {
diff --git a/plugins/mail/init.php b/plugins/mail/init.php
index 40d147fc9..467f8294a 100644
--- a/plugins/mail/init.php
+++ b/plugins/mail/init.php
@@ -15,10 +15,15 @@ class Mail extends Plugin {
$host->add_hook($host::HOOK_ARTICLE_BUTTON, $this);
$host->add_hook($host::HOOK_PREFS_TAB, $this);
+ $host->add_hook($host::HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM, $this);
}
function get_js() {
- return file_get_contents(dirname(__FILE__) . "/mail.js");
+ return file_get_contents(__DIR__ . "/mail.js");
+ }
+
+ function hook_headline_toolbar_select_menu_item($feed_id, $is_cat) {
+ return "
".__('Forward by email')."
";
}
function save() {
@@ -32,42 +37,38 @@ class Mail extends Plugin {
function hook_prefs_tab($args) {
if ($args != "prefPrefs") return;
- print "
mail ".__('Mail plugin')."\">";
+ $addresslist = $this->host->get($this, "addresslist");
- print "
" . __("You can set predefined email addressed here (comma-separated list):") . "
";
+ ?>
- print "
";
+
- print "";
+
- print_hidden("op", "pluginhandler");
- print_hidden("method", "save");
- print_hidden("plugin", "mail");
+
= __("You can set predefined email addressed here (comma-separated list):") ?>
- $addresslist = $this->host->get($this, "addresslist");
+
= $addresslist ?>
- print "
$addresslist ";
+
- print "
".
- __("Save")." ";
+ = \Controls\submit_tag(__("Save")) ?>
- print "";
-
- print "
";
+
+
+ pdo->prepare("SELECT email, full_name FROM ttrss_users WHERE
id = ?");
@@ -100,9 +98,6 @@ class Mail extends Plugin {
if (!$user_name)
$user_name = $_SESSION['name'];
- print_hidden("from_email", "$user_email");
- print_hidden("from_name", "$user_name");
-
$tpl = new Templator();
$tpl->readTemplateFromFile("email_article_template.txt");
@@ -143,46 +138,56 @@ class Mail extends Plugin {
$content = "";
$tpl->generateOutputToString($content);
- print "
";
-
$addresslist = explode(",", $this->host->get($this, "addresslist"));
- print __('To:');
+ ?>
- print " ";
+
-/* print " "; */
+ = \Controls\pluginhandler_tags($this, "sendemail") ?>
- print_select("destination", "", $addresslist, 'style="width: 30em" dojoType="dijit.form.ComboBox"');
+ = \Controls\hidden_tag("from_email", $user_email) ?>
+ = \Controls\hidden_tag("from_name", $user_name) ?>
-/* print "
"; */
+
- print " ";
+
+
+ = __('To:') ?>
+ = \Controls\select_tag("destination", "", $addresslist,
+ ["style" => "width: 380px", "required" => 1, "dojoType" => "dijit.form.ComboBox"]) ?>
+
+
- print __('Subject:');
+
- print " ";
+ = $content ?>
- print " ";
+
+ = \Controls\submit_tag(__('Send email')) ?>
+ = \Controls\cancel_dialog_tag(__('Cancel')) ?>
+
- print " ";
-
- print "$content ";
-
- print "
";
-
- print "
";
- print "".__('Send e-mail')." ";
- print "".__('Cancel')." ";
- print " ";
-
- //return;
+
+ ";
-
- foreach ($_SESSION['stored_emails'] as $email) {
- if (strpos($email, $search) !== false) {
- print "
$email ";
- }
- }
-
- print "";
- } */
-
function api_version() {
return 2;
}
diff --git a/plugins/mail/mail.js b/plugins/mail/mail.js
index 5ddc0dc41..d2bafe0e9 100644
--- a/plugins/mail/mail.js
+++ b/plugins/mail/mail.js
@@ -1,4 +1,4 @@
-/* global Plugins, Headlines, xhrJson, Notify, fox, __ */
+/* global Plugins, Headlines, dojo, App, xhr, Notify, fox, __ */
Plugins.Mail = {
send: function(id) {
@@ -13,14 +13,11 @@ Plugins.Mail = {
id = ids.toString();
}
- const query = "backend.php?op=pluginhandler&plugin=mail&method=emailArticle¶m=" + encodeURIComponent(id);
-
const dialog = new fox.SingleUseDialog({
- id: "emailArticleDlg",
title: __("Forward article by email"),
execute: function () {
if (this.validate()) {
- xhrJson("backend.php", this.attr('value'), (reply) => {
+ xhr.json("backend.php", this.attr('value'), (reply) => {
if (reply) {
const error = reply['error'];
@@ -35,16 +32,16 @@ Plugins.Mail = {
});
}
},
- href: query
+ content: __("Loading, please wait...")
});
- /* var tmph = dojo.connect(dialog, 'onLoad', function() {
- dojo.disconnect(tmph);
+ const tmph = dojo.connect(dialog, 'onShow', function () {
+ dojo.disconnect(tmph);
- new Ajax.Autocompleter('emailArticleDlg_destination', 'emailArticleDlg_dst_choices',
- "backend.php?op=pluginhandler&plugin=mail&method=completeEmails",
- { tokens: '', paramName: "search" });
- }); */
+ xhr.post("backend.php", App.getPhArgs("mail", "emailArticle", {ids: id}), (reply) => {
+ dialog.attr('content', reply);
+ });
+ });
dialog.show();
},
diff --git a/plugins/mail/mail.png b/plugins/mail/mail.png
deleted file mode 100644
index 7348aed77..000000000
Binary files a/plugins/mail/mail.png and /dev/null differ
diff --git a/plugins/mailto/init.js b/plugins/mailto/init.js
index ae68bf49b..4a9557249 100644
--- a/plugins/mailto/init.js
+++ b/plugins/mailto/init.js
@@ -1,4 +1,4 @@
-/* global Plugins, Headlines, fox, __ */
+/* global Plugins, Headlines, xhr, dojo, fox, __ */
Plugins.Mailto = {
send: function (id) {
@@ -13,12 +13,19 @@ Plugins.Mailto = {
id = ids.toString();
}
- const query = "backend.php?op=pluginhandler&plugin=mailto&method=emailArticle¶m=" + encodeURIComponent(id);
-
const dialog = new fox.SingleUseDialog({
- id: "emailArticleDlg",
- title: __("Forward article by email"),
- href: query});
+ title: __("Forward article by email (mailto:)"),
+ content: __("Loading, please wait...")
+ });
+
+ const tmph = dojo.connect(dialog, 'onShow', function () {
+ dojo.disconnect(tmph);
+
+ xhr.post("backend.php", App.getPhArgs("mailto", "emailArticle", {ids: id}), (reply) => {
+ dialog.attr('content', reply);
+ });
+ });
+
dialog.show();
}
diff --git a/plugins/mailto/init.php b/plugins/mailto/init.php
index 390984b71..c34b400ce 100644
--- a/plugins/mailto/init.php
+++ b/plugins/mailto/init.php
@@ -12,21 +12,26 @@ class MailTo extends Plugin {
$this->host = $host;
$host->add_hook($host::HOOK_ARTICLE_BUTTON, $this);
+ $host->add_hook($host::HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM, $this);
+ }
+
+ function hook_headline_toolbar_select_menu_item($feed_id, $is_cat) {
+ return "
".__('Forward by email (mailto:)')."
";
}
function get_js() {
- return file_get_contents(dirname(__FILE__) . "/init.js");
+ return file_get_contents(__DIR__ . "/init.js");
}
function hook_article_button($line) {
return "
mail_outline ";
+ title='".__('Forward by email (mailto:)')."'>mail_outline";
}
function emailArticle() {
- $ids = explode(",", $_REQUEST['param']);
+ $ids = explode(",", clean($_REQUEST['ids']));
$ids_qmarks = arr_qmarks($ids);
$tpl = new Templator();
@@ -37,7 +42,6 @@ class MailTo extends Plugin {
//$tpl->setVariable('USER_EMAIL', $user_email, true);
$tpl->setVariable('TTRSS_HOST', $_SERVER["HTTP_HOST"], true);
-
$sth = $this->pdo->prepare("SELECT DISTINCT link, content, title
FROM ttrss_user_entries, ttrss_entries WHERE id = ref_id AND
id IN ($ids_qmarks) AND owner_uid = ?");
@@ -65,25 +69,23 @@ class MailTo extends Plugin {
$content = "";
$tpl->generateOutputToString($content);
- $mailto_link = htmlspecialchars("mailto:?subject=".rawurlencode($subject).
- "&body=".rawurlencode($content));
+ $mailto_link = "mailto:?subject=".rawurlencode($subject)."&body=".rawurlencode($content);
- print __("Clicking the following link to invoke your mail client:");
+ ?>
- print "
";
+
- print __("You should be able to edit the message before sending in your mail client.");
+
+ = \Controls\submit_tag(__('Close this dialog')) ?>
+
- print "
";
-
- print "";
- print "".__('Close this dialog')." ";
- print " ";
-
- //return;
+ pdo->prepare("SELECT note FROM ttrss_user_entries WHERE
ref_id = ? AND owner_uid = ?");
- $sth->execute([$param, $_SESSION['uid']]);
+ $sth->execute([$id, $_SESSION['uid']]);
if ($row = $sth->fetch()) {
$note = $row['note'];
- print_hidden("id", "$param");
- print_hidden("op", "pluginhandler");
- print_hidden("method", "setNote");
- print_hidden("plugin", "note");
+ print \Controls\hidden_tag("id", $id);
+ print \Controls\pluginhandler_tags($this, "setnote");
- print "
+ $note ";
-
+ name='note'>= $note ?>
+ ";
- print "".__('Save')." ";
- print "".__('Cancel')." ";
- print "";
-
+ ?>
+
+ = \Controls\submit_tag(__('Save')) ?>
+ = \Controls\cancel_dialog_tag(__('Cancel')) ?>
+
+ pdo->prepare("UPDATE ttrss_user_entries SET note = ?
WHERE ref_id = ? AND owner_uid = ?");
$sth->execute([$note, $id, $_SESSION['uid']]);
- $formatted_note = Article::format_article_note($id, $note);
-
- print json_encode(array("note" => $formatted_note,
- "raw_length" => mb_strlen($note)));
+ print json_encode(["id" => $id, "note" => $note]);
}
function api_version() {
diff --git a/plugins/note/note.js b/plugins/note/note.js
index ab2ed9208..a46acb355 100644
--- a/plugins/note/note.js
+++ b/plugins/note/note.js
@@ -1,36 +1,39 @@
-/* global Plugins, xhrJson, Notify, fox, __ */
+/* global dojo, Plugins, xhr, App, Notify, fox, __ */
Plugins.Note = {
edit: function(id) {
- const query = "backend.php?op=pluginhandler&plugin=note&method=edit¶m=" + encodeURIComponent(id);
-
const dialog = new fox.SingleUseDialog({
- id: "editNoteDlg",
title: __("Edit article note"),
execute: function () {
if (this.validate()) {
Notify.progress("Saving article note...", true);
- xhrJson("backend.php", this.attr('value'), (reply) => {
+ xhr.json("backend.php", this.attr('value'), (reply) => {
Notify.close();
dialog.hide();
if (reply) {
- const elem = $("POSTNOTE-" + id);
+ App.findAll(`div[data-note-for="${reply.id}"]`).forEach((elem) => {
+ elem.querySelector(".body").innerHTML = reply.note;
- if (elem) {
- elem.innerHTML = reply.note;
-
- if (reply.raw_length != 0)
- Element.show(elem);
+ if (reply.note)
+ elem.show();
else
- Element.hide(elem);
- }
+ elem.hide();
+ });
}
});
}
},
- href: query,
+ content: __("Loading, please wait...")
+ });
+
+ const tmph = dojo.connect(dialog, 'onShow', function () {
+ dojo.disconnect(tmph);
+
+ xhr.post("backend.php", App.getPhArgs("note", "edit", {id: id}), (reply) => {
+ dialog.attr('content', reply);
+ });
});
dialog.show();
diff --git a/plugins/nsfw/init.js b/plugins/nsfw/init.js
index adb6d43c0..4bc2443e8 100644
--- a/plugins/nsfw/init.js
+++ b/plugins/nsfw/init.js
@@ -1,7 +1,12 @@
-function nsfwShow(elem) {
- let content = elem.parentNode.getElementsBySelector("div.nswf.content")[0];
+/* global Plugins */
- if (content) {
- Element.toggle(content);
+Plugins.NSFW = {
+ toggle: function(elem) {
+ const content = elem.domNode.parentNode.querySelector(".nswf.content");
+
+ if (content) {
+ Element.toggle(content);
+ }
}
}
+
diff --git a/plugins/nsfw/init.php b/plugins/nsfw/init.php
index 02344eb14..7c5b8d00f 100644
--- a/plugins/nsfw/init.php
+++ b/plugins/nsfw/init.php
@@ -19,7 +19,7 @@ class NSFW extends Plugin {
}
function get_js() {
- return file_get_contents(dirname(__FILE__) . "/init.js");
+ return file_get_contents(__DIR__ . "/init.js");
}
function hook_render_article($article) {
@@ -27,74 +27,60 @@ class NSFW extends Plugin {
$a_tags = array_map("trim", explode(",", $article["tag_cache"]));
if (count(array_intersect($tags, $a_tags)) > 0) {
- $article["content"] = "
".__("Not work safe (click to toggle)")."
-
".$article["content"]."
";
+ $article["content"] = "
".
+ \Controls\button_tag(__("Not work safe (click to toggle)"), '', ['onclick' => 'Plugins.NSFW.toggle(this)']).
+ "
".$article["content"]."
+
";
}
return $article;
}
function hook_render_article_cdm($article) {
- $tags = array_map("trim", explode(",", $this->host->get($this, "tags")));
- $a_tags = array_map("trim", explode(",", $article["tag_cache"]));
-
- if (count(array_intersect($tags, $a_tags)) > 0) {
- $article["content"] = "
".__("Not work safe (click to toggle)")."
-
".$article["content"]."
";
- }
-
- return $article;
+ return $this->hook_render_article($article);
}
function hook_prefs_tab($args) {
if ($args != "prefPrefs") return;
- print "
extension ".__("NSFW Plugin")."\">";
-
- print "
";
-
$tags = $this->host->get($this, "tags");
- print "
";
+ ?>
+ ">
+
- print "";
+
- print_hidden("op", "pluginhandler");
- print_hidden("method", "save");
- print_hidden("plugin", "nsfw");
+ = __("Tags to consider NSFW (comma-separated):") ?>
- print "";
-
- print "".
- __("Save")." ";
-
- print "
";
-
- print "
"; #pane
+ = \Controls\submit_tag(__("Save")) ?>
+
+
+ host->set($this, "tags", $tags);
diff --git a/plugins/share/init.php b/plugins/share/init.php
index 0794f5125..37799fba6 100644
--- a/plugins/share/init.php
+++ b/plugins/share/init.php
@@ -16,19 +16,22 @@ class Share extends Plugin {
$host->add_hook($host::HOOK_PREFS_TAB_SECTION, $this);
}
+ function is_public_method($method) {
+ return $method == "get";
+ }
+
function get_js() {
- return file_get_contents(dirname(__FILE__) . "/share.js");
+ return file_get_contents(__DIR__ . "/share.js");
}
function get_css() {
- return file_get_contents(dirname(__FILE__) . "/share.css");
+ return file_get_contents(__DIR__ . "/share.css");
}
function get_prefs_js() {
- return file_get_contents(dirname(__FILE__) . "/share_prefs.js");
+ return file_get_contents(__DIR__ . "/share_prefs.js");
}
-
function unshare() {
$id = $_REQUEST['id'];
@@ -36,32 +39,30 @@ class Share extends Plugin {
AND owner_uid = ?");
$sth->execute([$id, $_SESSION['uid']]);
- print "OK";
+ print __("Article unshared");
}
function hook_prefs_tab_section($id) {
if ($id == "prefFeedsPublishedGenerated") {
+ ?>
+
- print "
" . __("You can disable all articles shared by unique URLs here.") . " ";
-
- print "
".
- __('Unshare all articles')." ";
-
- print "";
+
= __("You can disable all articles shared by unique URLs here.") ?>
+
+ = __('Unshare all articles') ?>
+ pdo->prepare("UPDATE ttrss_user_entries SET uuid = '' WHERE
owner_uid = ?");
$sth->execute([$_SESSION['uid']]);
- return;
+ print __("Shared URLs cleared.");
}
-
function newkey() {
$id = $_REQUEST['id'];
$uuid = uniqid_short();
@@ -70,26 +71,169 @@ class Share extends Plugin {
AND owner_uid = ?");
$sth->execute([$uuid, $id, $_SESSION['uid']]);
- print json_encode(array("link" => $uuid));
+ print json_encode(["link" => $uuid]);
}
function hook_article_button($line) {
- $img_class = $line['uuid'] ? "shared" : "";
+ $icon_class = !empty($line['uuid']) ? "is-shared" : "";
- return "
link ";
}
- function shareArticle() {
- $param = $_REQUEST['param'];
+ function get() {
+ $uuid = clean($_REQUEST["key"] ?? "");
+
+ if ($uuid) {
+ $sth = $this->pdo->prepare("SELECT ref_id, owner_uid
+ FROM ttrss_user_entries WHERE uuid = ?");
+ $sth->execute([$uuid]);
+
+ if ($row = $sth->fetch()) {
+ header("Content-Type: text/html");
+
+ $id = $row["ref_id"];
+ $owner_uid = $row["owner_uid"];
+
+ $this->format_article($id, $owner_uid);
+
+ return;
+ }
+ }
+
+ header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
+ print "Article not found.";
+ }
+
+ private function format_article($id, $owner_uid) {
+
+ $pdo = Db::pdo();
+
+ $sth = $pdo->prepare("SELECT id,title,link,content,feed_id,comments,int_id,lang,
+ ".SUBSTRING_FOR_DATE."(updated,1,16) as updated,
+ (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
+ (SELECT title FROM ttrss_feeds WHERE id = feed_id) as feed_title,
+ (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
+ (SELECT always_display_enclosures FROM ttrss_feeds WHERE id = feed_id) as always_display_enclosures,
+ num_comments,
+ tag_cache,
+ author,
+ guid,
+ note
+ FROM ttrss_entries,ttrss_user_entries
+ WHERE id = ? AND ref_id = id AND owner_uid = ?");
+ $sth->execute([$id, $owner_uid]);
+
+ if ($line = $sth->fetch()) {
+
+ $line["tags"] = Article::_get_tags($id, $owner_uid, $line["tag_cache"]);
+ unset($line["tag_cache"]);
+
+ $line["content"] = Sanitizer::sanitize($line["content"],
+ $line['hide_images'],
+ $owner_uid, $line["site_url"], false, $line["id"]);
+
+ PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_RENDER_ARTICLE,
+ function ($result) use (&$line) {
+ $line = $result;
+ },
+ $line);
+
+ $enclosures = Article::_get_enclosures($line["id"]);
+ list ($og_image, $og_stream) = Article::_get_image($enclosures, $line['content'], $line["site_url"]);
+
+ $content_decoded = html_entity_decode($line["title"], ENT_NOQUOTES | ENT_HTML401);
+ $parsed_updated = TimeHelper::make_local_datetime($line["updated"], true, $owner_uid, true);
+
+ $line['content'] = DiskCache::rewrite_urls($line['content']);
+
+ ob_start();
+
+ ?>
+
+
+
+
+
= $line["title"] ?>
+ = javascript_tag("js/common.js") ?>
+ = javascript_tag("js/utility.js") ?>
+
+
+
+
+
+
">
+
+
+
+
+
+
+
+
+
+
+
+
+
= $line["title"] ?>
+
+
+
+
+
+
">
+ = $line["content"] ?>
+
+
+
+
+ chain_hooks_callback(PluginHost::HOOK_FORMAT_ARTICLE,
+ function ($result) use (&$rv) {
+ $rv = $result;
+ },
+ $rv, $line);
+
+ print $rv;
+ }
+ }
+
+ function shareDialog() {
+ $id = (int)clean($_REQUEST['id'] ?? 0);
$sth = $this->pdo->prepare("SELECT uuid FROM ttrss_user_entries WHERE int_id = ?
AND owner_uid = ?");
- $sth->execute([$param, $_SESSION['uid']]);
+ $sth->execute([$id, $_SESSION['uid']]);
if ($row = $sth->fetch()) {
-
$uuid = $row['uuid'];
if (!$uuid) {
@@ -97,42 +241,34 @@ class Share extends Plugin {
$sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET uuid = ? WHERE int_id = ?
AND owner_uid = ?");
- $sth->execute([$uuid, $param, $_SESSION['uid']]);
+ $sth->execute([$uuid, $id, $_SESSION['uid']]);
}
- print "
" . __("You can share this article by the following unique URL:") . " ";
+ $url_path = $this->host->get_public_method_url($this, "get", ["key" => $uuid]);
+ ?>
- $url_path = get_self_url_prefix();
- $url_path .= "/public.php?op=share&key=$uuid";
+
= __("You can share this article by the following unique URL:") ?>
- print "
+ ";
-
- /* if (!label_find_id(__('Shared'), $_SESSION["uid"]))
- label_create(__('Shared'), $_SESSION["uid"]);
-
- label_add_article($ref_id, __('Shared'), $_SESSION['uid']); */
+
+ ";
-
- print "
".
- __('Unshare article')." ";
-
- print "
".
- __('Generate new URL')." ";
-
- print "
".
- __('Close this window')." ";
-
- print "";
+ ?>
+
+ = \Controls\button_tag(__('Unshare article'), '', ['class' => 'alt-danger', 'onclick' => "App.dialogOf(this).unshare()"]) ?>
+ = \Controls\button_tag(__('Generate new URL'), '', ['onclick' => "App.dialogOf(this).newurl()"]) ?>
+ = \Controls\submit_tag(__("Close this window")) ?>
+
+ {
+ xhr.json("backend.php", App.getPhArgs("share", "newkey", {id: id}), (reply) => {
if (reply) {
const new_link = reply.link;
- const e = $('gen_article_url');
+ const target = dialog.domNode.querySelector(".target-url");
- if (new_link) {
+ if (new_link && target) {
- e.innerHTML = e.innerHTML.replace(/\&key=.*$/,
+ target.innerHTML = target.innerHTML.replace(/&key=.*$/,
"&key=" + new_link);
- e.href = e.href.replace(/\&key=.*$/,
+ target.href = target.href.replace(/&key=.*$/,
"&key=" + new_link);
- new Effect.Highlight(e);
+ const icon = document.querySelector(".share-icon-" + id);
- const img = $("SHARE-IMG-" + id);
- img.addClassName("shared");
+ if (icon)
+ icon.addClassName("is-shared");
Notify.close();
@@ -44,32 +40,35 @@ Plugins.Share = {
},
unshare: function () {
if (confirm(__("Remove sharing for this article?"))) {
+ xhr.post("backend.php", App.getPhArgs("share", "unshare", {id: id}), (reply) => {
+ Notify.info(reply);
- const query = {op: "pluginhandler", plugin: "share", method: "unshare", id: id};
+ const icon = document.querySelector(".share-icon-" + id);
- xhrPost("backend.php", query, () => {
- try {
- const img = $("SHARE-IMG-" + id);
+ if (icon)
+ icon.removeClassName("is-shared");
- if (img) {
- img.removeClassName("shared");
- img.up("div[id*=RROW]").removeClassName("shared");
- }
-
- dialog.hide();
- } catch (e) {
- console.error(e);
- }
+ dialog.hide();
});
}
},
- href: query
+ content: __("Loading, please wait...")
+ });
+
+ const tmph = dojo.connect(dialog, 'onShow', function () {
+ dojo.disconnect(tmph);
+
+ xhr.post("backend.php", App.getPhArgs("share", "shareDialog", {id: id}), (reply) => {
+ dialog.attr('content', reply)
+
+ const icon = document.querySelector(".share-icon-" + id);
+
+ if (icon)
+ icon.addClassName("is-shared");
+ });
});
dialog.show();
-
- const img = $("SHARE-IMG-" + id);
- img.addClassName("shared");
}
}
diff --git a/plugins/share/share_prefs.js b/plugins/share/share_prefs.js
index 071a6667c..d974af618 100644
--- a/plugins/share/share_prefs.js
+++ b/plugins/share/share_prefs.js
@@ -1,12 +1,12 @@
+/* global Plugins, Notify, xhr, App */
+
Plugins.Share = {
clearKeys: function() {
if (confirm(__("This will invalidate all previously shared article URLs. Continue?"))) {
Notify.progress("Clearing URLs...");
- const query = {op: "pluginhandler", plugin: "share", method: "clearArticleKeys"};
-
- xhrPost("backend.php", query, () => {
- Notify.info("Shared URLs cleared.");
+ xhr.post("backend.php", App.getPhArgs("share", "clearArticleKeys"), (reply) => {
+ Notify.info(reply);
});
}
diff --git a/plugins/shorten_expanded/init.js b/plugins/shorten_expanded/init.js
index 30bfac6ba..0abc8c129 100644
--- a/plugins/shorten_expanded/init.js
+++ b/plugins/shorten_expanded/init.js
@@ -1,3 +1,5 @@
+/* global Plugins, __, require, PluginHost */
+
const _shorten_expanded_threshold = 1.5; //window heights
Plugins.Shorten_Expanded = {
@@ -5,8 +7,8 @@ Plugins.Shorten_Expanded = {
const row = $(id);
if (row) {
- const content = row.select(".content-shrink-wrap")[0];
- const link = row.select(".expand-prompt")[0];
+ const content = row.querySelector(".content-shrink-wrap");
+ const link = row.querySelector(".expand-prompt");
if (content) content.removeClassName("content-shrink-wrap");
if (link) Element.hide(link);
@@ -22,26 +24,26 @@ require(['dojo/_base/kernel', 'dojo/ready'], function (dojo, ready) {
window.setTimeout(function() {
if (row) {
- const c_inner = row.select(".content-inner")[0];
- const c_inter = row.select(".intermediate")[0];
+ const content = row.querySelector(".content-inner");
- if (c_inner && c_inter &&
- row.offsetHeight >= _shorten_expanded_threshold * window.innerHeight) {
+ //console.log('shorten', row.offsetHeight, 'vs', _shorten_expanded_threshold * window.innerHeight);
- let tmp = document.createElement("div");
+ if (content && row.offsetHeight >= _shorten_expanded_threshold * window.innerHeight) {
- c_inter.select("> *:not([class*='attachments'])").each(function(p) {
- p.parentNode.removeChild(p);
- tmp.appendChild(p);
- });
+ const attachments = row.querySelector(".attachments-inline"); // optional
- c_inner.innerHTML = `
- ${c_inner.innerHTML}
- ${tmp.innerHTML}
+ content.innerHTML = `
+
+ ${content.innerHTML}
+ ${attachments ? attachments.innerHTML : ''}
+
${__("Click to expand article")} `;
- dojo.parser.parse(c_inner);
+ if (attachments)
+ attachments.innerHTML = "";
+
+ dojo.parser.parse(content);
}
}
}, 150);
diff --git a/plugins/toggle_sidebar/init.php b/plugins/toggle_sidebar/init.php
index f8ec35a91..19ca960e2 100644
--- a/plugins/toggle_sidebar/init.php
+++ b/plugins/toggle_sidebar/init.php
@@ -24,7 +24,7 @@ class Toggle_Sidebar extends Plugin {
+ title="= __('Toggle sidebar') ?>">chevron_left
"ttrss_feeds.title AS feed_title,"
);
- $qfh_ret = Feeds::queryFeedHeadlines($params);
+ $qfh_ret = Feeds::_get_headlines($params);
$qfh_ret[1] = __("Shared articles");
return $qfh_ret;
diff --git a/prefs.php b/prefs.php
index 61e1145b7..57ddbba7c 100644
--- a/prefs.php
+++ b/prefs.php
@@ -1,19 +1,11 @@
Fatal Error: You forgot to copy
-
config.php-dist to
config.php and edit it.\n";
- exit;
- }
-
require_once "autoload.php";
require_once "sessions.php";
require_once "functions.php";
require_once "sanity_check.php";
- require_once "config.php";
- require_once "db-prefs.php";
if (!init_plugins()) return;
@@ -24,7 +16,7 @@
-
Tiny Tiny RSS :
+
Tiny Tiny RSS : = __("Preferences") ?>
-
@@ -50,7 +42,7 @@
-
+ = __("Loading, please wait...") ?>
@@ -126,34 +115,37 @@
+ title="
settings = __('Preferences') ?>">
+ title="
rss_feed = __('Feeds') ?>">
+ title="
filter_list1 = __('Filters') ?>">
+ title="
label_outline1 = __('Labels') ?>">
= 10) { ?>
+ title="person = __('Users') ?>">
+ title="info_outline = __('System') ?>">
run_hooks(PluginHost::HOOK_PREFS_TABS);
@@ -162,8 +154,8 @@
diff --git a/public.php b/public.php
index 36308e25e..28f95d0a9 100644
--- a/public.php
+++ b/public.php
@@ -1,14 +1,11 @@
lookup_handler("public", $method);
@@ -30,6 +34,13 @@
$handler = new Handler_Public($_REQUEST);
}
+ if (strpos($method, "_") === 0) {
+ user_error("Refusing to invoke method $method which starts with underscore.", E_USER_WARNING);
+ header("Content-Type: text/json");
+ print Errors::to_json(Errors::E_UNAUTHORIZED);
+ return;
+ }
+
if (implements_interface($handler, "IHandler") && $handler->before($method)) {
if ($method && method_exists($handler, $method)) {
$reflection = new ReflectionMethod($handler, $method);
@@ -37,8 +48,9 @@
if ($reflection->getNumberOfRequiredParameters() == 0) {
$handler->$method();
} else {
+ user_error("Refusing to invoke method $method which has required parameters.", E_USER_WARNING);
header("Content-Type: text/json");
- print error_json(6);
+ print Errors::to_json(Errors::E_UNAUTHORIZED);
}
} else if (method_exists($handler, 'index')) {
$handler->index();
@@ -48,5 +60,5 @@
}
header("Content-Type: text/plain");
- print error_json(13);
+ print Errors::to_json(Errors::E_UNKNOWN_METHOD);
?>
diff --git a/themes/compact.css b/themes/compact.css
index 080f82961..16058309f 100644
--- a/themes/compact.css
+++ b/themes/compact.css
@@ -456,6 +456,7 @@ body.ttrss_main .dijitDialog .dlgSecCont {
margin: 10px 20px;
}
body.ttrss_main .dijitDialog header.horizontal + section,
+body.ttrss_main .dijitDialog section.horizontal,
body.ttrss_main .dijitDialog .dlgSecHoriz + .dlgSecCont {
margin: 10px 0;
}
@@ -506,6 +507,11 @@ body.ttrss_main .dijitDialog .dlgButtons {
body.ttrss_main .dijitDialog footer.text-center {
text-align: center;
}
+body.ttrss_main .dijitDialog textarea#tags_str {
+ height: 100px;
+ font-size: 12px;
+ width: 98%;
+}
body.ttrss_main i.icon-label {
color: #fff7d5;
}
@@ -526,22 +532,13 @@ body.ttrss_main #feed_browser_spinner {
height: 18px;
width: 18px;
}
-body.ttrss_main #exceptionDlg .dijitDialogTitleBar {
- background: red;
- color: white;
-}
-body.ttrss_main #exceptionDlg .dijitDialogPaneContent {
- background: #fcc;
-}
-body.ttrss_main #exceptionDlg .error-contents .message {
+body.ttrss_main .exception-contents h3 {
color: red;
}
-body.ttrss_main #exceptionDlg .error-contents textarea {
+body.ttrss_main .exception-contents textarea {
width: 99%;
height: 200px;
-}
-body.ttrss_main #exceptionDlg .error-contents .dlgButtons {
- text-align: center;
+ font-size: 11px;
}
body.ttrss_main #content-wrap {
padding: 0px;
@@ -713,6 +710,8 @@ body.ttrss_main #toolbar-frame #toolbar i {
margin: 0 4px;
}
body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines {
+ font-size: 12px;
+ background: transparent;
padding-right: 4px;
flex-grow: 2;
display: flex;
@@ -722,7 +721,8 @@ body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left {
display: flex;
align-items: center;
}
-body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left #feed_title {
+body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title,
+body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .cancel_search {
margin-left: 4px;
}
body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .right {
@@ -755,6 +755,17 @@ body.ttrss_main #header {
top: 0px;
z-index: 5;
}
+body.ttrss_main #header i.net-alert,
+body.ttrss_main #header .left i.icon-error {
+ color: red;
+}
+body.ttrss_main #header i.log-alert {
+ color: #ddba1c;
+ cursor: pointer;
+}
+body.ttrss_main #header i {
+ margin: 0 4px;
+}
body.ttrss_main #content-insert {
padding: 0px;
border-color: #ddd;
@@ -812,16 +823,18 @@ body.ttrss_main #headlines-spacer a:hover {
}
body.ttrss_main ul#filterDlg_Matches,
body.ttrss_main ul#filterDlg_Actions {
- max-height: 100px;
- overflow: auto;
list-style-type: none;
- border-style: solid;
- border-color: #ddd;
- border-width: 1px 1px 1px 1px;
- background-color: white;
- margin: 0px 0px 5px 0px;
- padding: 4px;
- min-height: 16px;
+ margin: 0;
+ padding: 0;
+ /*max-height : 100px;
+ overflow : auto;
+ border-style : solid;
+ border-color : @border-default;
+ border-width : 1px 1px 1px 1px;
+ background-color : @default-bg;
+ margin : 0px 0px 5px 0px;
+ padding : 4px;
+ min-height : 16px;*/
}
body.ttrss_main ul#filterDlg_Matches li,
body.ttrss_main ul#filterDlg_Actions li {
@@ -860,9 +873,6 @@ body.ttrss_main span.highlight {
body.ttrss_main #headlines-frame .dijitCheckBox {
margin-right: 4px;
}
-body.ttrss_main #editTagsDlg {
- overflow: visible;
-}
body.ttrss_main #feedEditDlg img.feedIcon {
border: 1px solid #ccc;
padding: 5px;
@@ -888,12 +898,14 @@ body.ttrss_main .dijitTooltipBelow .dijitTooltipConnector {
body.ttrss_main .dijitTooltipAbove .dijitTooltipConnector {
border-top-color: #1c5c7d;
}
-body.ttrss_main .dijitDialog h1:first-of-type,
-body.ttrss_main .dijitDialog h2:first-of-type,
-body.ttrss_main .dijitDialog h3:first-of-type,
-body.ttrss_main .dijitDialog h4:first-of-type {
- margin-top: 0px;
-}
+/*body.ttrss_main .dijitDialog {
+ h1:first-of-type,
+ h2:first-of-type,
+ h3:first-of-type,
+ h4:first-of-type {
+ margin-top: 0px;
+ }
+}*/
body.ttrss_main[view-mode="marked"] #feeds-holder #feedTree .dijitTreeRow.Has_Marked .dijitTreeLabel {
color: #257aa7;
}
@@ -1403,6 +1415,13 @@ body.ttrss_prefs {
background-color: #f5f5f5;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
+ /*.dijitContentPane {
+ h1:first-of-type,
+ h2:first-of-type,
+ h3:first-of-type {
+ margin-top: 0px;
+ }
+ }*/
}
body.ttrss_prefs h1,
body.ttrss_prefs h2,
@@ -1412,11 +1431,6 @@ body.ttrss_prefs h4 {
font-weight: 600;
color: #555;
}
-body.ttrss_prefs .dijitContentPane h1:first-of-type,
-body.ttrss_prefs .dijitContentPane h2:first-of-type,
-body.ttrss_prefs .dijitContentPane h3:first-of-type {
- margin-top: 0px;
-}
body.ttrss_prefs #footer,
body.ttrss_prefs #header {
padding: 8px;
@@ -1450,22 +1464,13 @@ body.ttrss_prefs .dijitAccordionTitle i.material-icons {
body.ttrss_prefs .dijitAccordionTitleSelected i.material-icons {
color: white;
}
+body.ttrss_prefs #feedsTab {
+ background: #f5f5f5;
+}
body.ttrss_prefs .dijitDialog #pref-profiles-list .dijitInlineEditBoxDisplayMode {
padding: 0px;
}
-body.ttrss_prefs div#feedlistLoading,
-body.ttrss_prefs div#filterlistLoading,
-body.ttrss_prefs div#labellistLoading {
- text-align: center;
- padding: 5px;
- color: #555;
-}
-body.ttrss_prefs div#feedlistLoading img,
-body.ttrss_prefs div#filterlistLoading img,
-body.ttrss_prefs div#labellistLoading {
- margin-right: 5px;
-}
-body.ttrss_prefs #errorButton {
+body.ttrss_prefs #pref_feeds_errors_btn {
color: red;
}
body.ttrss_prefs .user-css-editor {
@@ -1702,6 +1707,13 @@ body.ttrss_utility.share_popup .content {
font-size: 13px;
padding: 0px;
}
+.flat .dijitToolbar .dijitTextBox .dijitInputInner {
+ line-height: 10px;
+}
+.flat .dijitToolbar label {
+ position: relative;
+ top: 2px;
+}
.flat .dijitAccordionContainer {
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.1);
}
diff --git a/themes/compact_night.css b/themes/compact_night.css
index be6a25a2e..f1894f4de 100644
--- a/themes/compact_night.css
+++ b/themes/compact_night.css
@@ -456,6 +456,7 @@ body.ttrss_main .dijitDialog .dlgSecCont {
margin: 10px 20px;
}
body.ttrss_main .dijitDialog header.horizontal + section,
+body.ttrss_main .dijitDialog section.horizontal,
body.ttrss_main .dijitDialog .dlgSecHoriz + .dlgSecCont {
margin: 10px 0;
}
@@ -506,6 +507,11 @@ body.ttrss_main .dijitDialog .dlgButtons {
body.ttrss_main .dijitDialog footer.text-center {
text-align: center;
}
+body.ttrss_main .dijitDialog textarea#tags_str {
+ height: 100px;
+ font-size: 12px;
+ width: 98%;
+}
body.ttrss_main i.icon-label {
color: #fff7d5;
}
@@ -526,22 +532,13 @@ body.ttrss_main #feed_browser_spinner {
height: 18px;
width: 18px;
}
-body.ttrss_main #exceptionDlg .dijitDialogTitleBar {
- background: red;
- color: white;
-}
-body.ttrss_main #exceptionDlg .dijitDialogPaneContent {
- background: #fcc;
-}
-body.ttrss_main #exceptionDlg .error-contents .message {
+body.ttrss_main .exception-contents h3 {
color: red;
}
-body.ttrss_main #exceptionDlg .error-contents textarea {
+body.ttrss_main .exception-contents textarea {
width: 99%;
height: 200px;
-}
-body.ttrss_main #exceptionDlg .error-contents .dlgButtons {
- text-align: center;
+ font-size: 11px;
}
body.ttrss_main #content-wrap {
padding: 0px;
@@ -713,6 +710,8 @@ body.ttrss_main #toolbar-frame #toolbar i {
margin: 0 4px;
}
body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines {
+ font-size: 12px;
+ background: transparent;
padding-right: 4px;
flex-grow: 2;
display: flex;
@@ -722,7 +721,8 @@ body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left {
display: flex;
align-items: center;
}
-body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left #feed_title {
+body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title,
+body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .cancel_search {
margin-left: 4px;
}
body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .right {
@@ -755,6 +755,17 @@ body.ttrss_main #header {
top: 0px;
z-index: 5;
}
+body.ttrss_main #header i.net-alert,
+body.ttrss_main #header .left i.icon-error {
+ color: red;
+}
+body.ttrss_main #header i.log-alert {
+ color: #ddba1c;
+ cursor: pointer;
+}
+body.ttrss_main #header i {
+ margin: 0 4px;
+}
body.ttrss_main #content-insert {
padding: 0px;
border-color: #222;
@@ -812,16 +823,18 @@ body.ttrss_main #headlines-spacer a:hover {
}
body.ttrss_main ul#filterDlg_Matches,
body.ttrss_main ul#filterDlg_Actions {
- max-height: 100px;
- overflow: auto;
list-style-type: none;
- border-style: solid;
- border-color: #222;
- border-width: 1px 1px 1px 1px;
- background-color: #333;
- margin: 0px 0px 5px 0px;
- padding: 4px;
- min-height: 16px;
+ margin: 0;
+ padding: 0;
+ /*max-height : 100px;
+ overflow : auto;
+ border-style : solid;
+ border-color : @border-default;
+ border-width : 1px 1px 1px 1px;
+ background-color : @default-bg;
+ margin : 0px 0px 5px 0px;
+ padding : 4px;
+ min-height : 16px;*/
}
body.ttrss_main ul#filterDlg_Matches li,
body.ttrss_main ul#filterDlg_Actions li {
@@ -860,9 +873,6 @@ body.ttrss_main span.highlight {
body.ttrss_main #headlines-frame .dijitCheckBox {
margin-right: 4px;
}
-body.ttrss_main #editTagsDlg {
- overflow: visible;
-}
body.ttrss_main #feedEditDlg img.feedIcon {
border: 1px solid #ccc;
padding: 5px;
@@ -888,12 +898,14 @@ body.ttrss_main .dijitTooltipBelow .dijitTooltipConnector {
body.ttrss_main .dijitTooltipAbove .dijitTooltipConnector {
border-top-color: #d29745;
}
-body.ttrss_main .dijitDialog h1:first-of-type,
-body.ttrss_main .dijitDialog h2:first-of-type,
-body.ttrss_main .dijitDialog h3:first-of-type,
-body.ttrss_main .dijitDialog h4:first-of-type {
- margin-top: 0px;
-}
+/*body.ttrss_main .dijitDialog {
+ h1:first-of-type,
+ h2:first-of-type,
+ h3:first-of-type,
+ h4:first-of-type {
+ margin-top: 0px;
+ }
+}*/
body.ttrss_main[view-mode="marked"] #feeds-holder #feedTree .dijitTreeRow.Has_Marked .dijitTreeLabel {
color: #b87d2c;
}
@@ -1403,6 +1415,13 @@ body.ttrss_prefs {
background-color: #222;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
+ /*.dijitContentPane {
+ h1:first-of-type,
+ h2:first-of-type,
+ h3:first-of-type {
+ margin-top: 0px;
+ }
+ }*/
}
body.ttrss_prefs h1,
body.ttrss_prefs h2,
@@ -1412,11 +1431,6 @@ body.ttrss_prefs h4 {
font-weight: 600;
color: #ccc;
}
-body.ttrss_prefs .dijitContentPane h1:first-of-type,
-body.ttrss_prefs .dijitContentPane h2:first-of-type,
-body.ttrss_prefs .dijitContentPane h3:first-of-type {
- margin-top: 0px;
-}
body.ttrss_prefs #footer,
body.ttrss_prefs #header {
padding: 8px;
@@ -1450,22 +1464,13 @@ body.ttrss_prefs .dijitAccordionTitle i.material-icons {
body.ttrss_prefs .dijitAccordionTitleSelected i.material-icons {
color: white;
}
+body.ttrss_prefs #feedsTab {
+ background: #222;
+}
body.ttrss_prefs .dijitDialog #pref-profiles-list .dijitInlineEditBoxDisplayMode {
padding: 0px;
}
-body.ttrss_prefs div#feedlistLoading,
-body.ttrss_prefs div#filterlistLoading,
-body.ttrss_prefs div#labellistLoading {
- text-align: center;
- padding: 5px;
- color: #ccc;
-}
-body.ttrss_prefs div#feedlistLoading img,
-body.ttrss_prefs div#filterlistLoading img,
-body.ttrss_prefs div#labellistLoading {
- margin-right: 5px;
-}
-body.ttrss_prefs #errorButton {
+body.ttrss_prefs #pref_feeds_errors_btn {
color: red;
}
body.ttrss_prefs .user-css-editor {
@@ -1604,6 +1609,13 @@ body.ttrss_utility fieldset > label.checkbox {
font-size: 13px;
padding: 0px;
}
+.flat .dijitToolbar .dijitTextBox .dijitInputInner {
+ line-height: 10px;
+}
+.flat .dijitToolbar label {
+ position: relative;
+ top: 2px;
+}
.flat .dijitAccordionContainer {
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.1);
}
diff --git a/themes/light.css b/themes/light.css
index e16ff83dd..c014858ef 100644
--- a/themes/light.css
+++ b/themes/light.css
@@ -456,6 +456,7 @@ body.ttrss_main .dijitDialog .dlgSecCont {
margin: 10px 20px;
}
body.ttrss_main .dijitDialog header.horizontal + section,
+body.ttrss_main .dijitDialog section.horizontal,
body.ttrss_main .dijitDialog .dlgSecHoriz + .dlgSecCont {
margin: 10px 0;
}
@@ -506,6 +507,11 @@ body.ttrss_main .dijitDialog .dlgButtons {
body.ttrss_main .dijitDialog footer.text-center {
text-align: center;
}
+body.ttrss_main .dijitDialog textarea#tags_str {
+ height: 100px;
+ font-size: 12px;
+ width: 98%;
+}
body.ttrss_main i.icon-label {
color: #fff7d5;
}
@@ -526,22 +532,13 @@ body.ttrss_main #feed_browser_spinner {
height: 18px;
width: 18px;
}
-body.ttrss_main #exceptionDlg .dijitDialogTitleBar {
- background: red;
- color: white;
-}
-body.ttrss_main #exceptionDlg .dijitDialogPaneContent {
- background: #fcc;
-}
-body.ttrss_main #exceptionDlg .error-contents .message {
+body.ttrss_main .exception-contents h3 {
color: red;
}
-body.ttrss_main #exceptionDlg .error-contents textarea {
+body.ttrss_main .exception-contents textarea {
width: 99%;
height: 200px;
-}
-body.ttrss_main #exceptionDlg .error-contents .dlgButtons {
- text-align: center;
+ font-size: 11px;
}
body.ttrss_main #content-wrap {
padding: 0px;
@@ -713,6 +710,8 @@ body.ttrss_main #toolbar-frame #toolbar i {
margin: 0 4px;
}
body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines {
+ font-size: 12px;
+ background: transparent;
padding-right: 4px;
flex-grow: 2;
display: flex;
@@ -722,7 +721,8 @@ body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left {
display: flex;
align-items: center;
}
-body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left #feed_title {
+body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title,
+body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .cancel_search {
margin-left: 4px;
}
body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .right {
@@ -755,6 +755,17 @@ body.ttrss_main #header {
top: 0px;
z-index: 5;
}
+body.ttrss_main #header i.net-alert,
+body.ttrss_main #header .left i.icon-error {
+ color: red;
+}
+body.ttrss_main #header i.log-alert {
+ color: #ddba1c;
+ cursor: pointer;
+}
+body.ttrss_main #header i {
+ margin: 0 4px;
+}
body.ttrss_main #content-insert {
padding: 0px;
border-color: #ddd;
@@ -812,16 +823,18 @@ body.ttrss_main #headlines-spacer a:hover {
}
body.ttrss_main ul#filterDlg_Matches,
body.ttrss_main ul#filterDlg_Actions {
- max-height: 100px;
- overflow: auto;
list-style-type: none;
- border-style: solid;
- border-color: #ddd;
- border-width: 1px 1px 1px 1px;
- background-color: white;
- margin: 0px 0px 5px 0px;
- padding: 4px;
- min-height: 16px;
+ margin: 0;
+ padding: 0;
+ /*max-height : 100px;
+ overflow : auto;
+ border-style : solid;
+ border-color : @border-default;
+ border-width : 1px 1px 1px 1px;
+ background-color : @default-bg;
+ margin : 0px 0px 5px 0px;
+ padding : 4px;
+ min-height : 16px;*/
}
body.ttrss_main ul#filterDlg_Matches li,
body.ttrss_main ul#filterDlg_Actions li {
@@ -860,9 +873,6 @@ body.ttrss_main span.highlight {
body.ttrss_main #headlines-frame .dijitCheckBox {
margin-right: 4px;
}
-body.ttrss_main #editTagsDlg {
- overflow: visible;
-}
body.ttrss_main #feedEditDlg img.feedIcon {
border: 1px solid #ccc;
padding: 5px;
@@ -888,12 +898,14 @@ body.ttrss_main .dijitTooltipBelow .dijitTooltipConnector {
body.ttrss_main .dijitTooltipAbove .dijitTooltipConnector {
border-top-color: #1c5c7d;
}
-body.ttrss_main .dijitDialog h1:first-of-type,
-body.ttrss_main .dijitDialog h2:first-of-type,
-body.ttrss_main .dijitDialog h3:first-of-type,
-body.ttrss_main .dijitDialog h4:first-of-type {
- margin-top: 0px;
-}
+/*body.ttrss_main .dijitDialog {
+ h1:first-of-type,
+ h2:first-of-type,
+ h3:first-of-type,
+ h4:first-of-type {
+ margin-top: 0px;
+ }
+}*/
body.ttrss_main[view-mode="marked"] #feeds-holder #feedTree .dijitTreeRow.Has_Marked .dijitTreeLabel {
color: #257aa7;
}
@@ -1403,6 +1415,13 @@ body.ttrss_prefs {
background-color: #f5f5f5;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
+ /*.dijitContentPane {
+ h1:first-of-type,
+ h2:first-of-type,
+ h3:first-of-type {
+ margin-top: 0px;
+ }
+ }*/
}
body.ttrss_prefs h1,
body.ttrss_prefs h2,
@@ -1412,11 +1431,6 @@ body.ttrss_prefs h4 {
font-weight: 600;
color: #555;
}
-body.ttrss_prefs .dijitContentPane h1:first-of-type,
-body.ttrss_prefs .dijitContentPane h2:first-of-type,
-body.ttrss_prefs .dijitContentPane h3:first-of-type {
- margin-top: 0px;
-}
body.ttrss_prefs #footer,
body.ttrss_prefs #header {
padding: 8px;
@@ -1450,22 +1464,13 @@ body.ttrss_prefs .dijitAccordionTitle i.material-icons {
body.ttrss_prefs .dijitAccordionTitleSelected i.material-icons {
color: white;
}
+body.ttrss_prefs #feedsTab {
+ background: #f5f5f5;
+}
body.ttrss_prefs .dijitDialog #pref-profiles-list .dijitInlineEditBoxDisplayMode {
padding: 0px;
}
-body.ttrss_prefs div#feedlistLoading,
-body.ttrss_prefs div#filterlistLoading,
-body.ttrss_prefs div#labellistLoading {
- text-align: center;
- padding: 5px;
- color: #555;
-}
-body.ttrss_prefs div#feedlistLoading img,
-body.ttrss_prefs div#filterlistLoading img,
-body.ttrss_prefs div#labellistLoading {
- margin-right: 5px;
-}
-body.ttrss_prefs #errorButton {
+body.ttrss_prefs #pref_feeds_errors_btn {
color: red;
}
body.ttrss_prefs .user-css-editor {
@@ -1702,6 +1707,13 @@ body.ttrss_utility.share_popup .content {
font-size: 13px;
padding: 0px;
}
+.flat .dijitToolbar .dijitTextBox .dijitInputInner {
+ line-height: 10px;
+}
+.flat .dijitToolbar label {
+ position: relative;
+ top: 2px;
+}
.flat .dijitAccordionContainer {
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.1);
}
diff --git a/themes/light/dijit_basic.less b/themes/light/dijit_basic.less
index 2854c77d0..0582ddc5c 100644
--- a/themes/light/dijit_basic.less
+++ b/themes/light/dijit_basic.less
@@ -40,6 +40,15 @@
.dijitToolbar {
font-size: 13px;
padding: 0px;
+
+ .dijitTextBox .dijitInputInner {
+ line-height : 10px;
+ }
+
+ label {
+ position : relative;
+ top : 2px;
+ }
}
.dijitAccordionContainer {
diff --git a/themes/light/prefs.less b/themes/light/prefs.less
index 95ddefc34..510388391 100644
--- a/themes/light/prefs.less
+++ b/themes/light/prefs.less
@@ -9,13 +9,13 @@ body.ttrss_prefs {
color : @default-text;
}
- .dijitContentPane {
+ /*.dijitContentPane {
h1:first-of-type,
h2:first-of-type,
h3:first-of-type {
margin-top: 0px;
}
- }
+ }*/
#footer, #header {
padding : 8px;
@@ -57,21 +57,15 @@ body.ttrss_prefs {
color : white;
}
+ #feedsTab {
+ background : @color-panel-bg;
+ }
+
.dijitDialog #pref-profiles-list .dijitInlineEditBoxDisplayMode {
padding : 0px;
}
- div#feedlistLoading, div#filterlistLoading, div#labellistLoading {
- text-align : center;
- padding : 5px;
- color : @default-text;
- }
-
- div#feedlistLoading img, div#filterlistLoading img, div#labellistLoading {
- margin-right : 5px;
- }
-
- #errorButton {
+ #pref_feeds_errors_btn {
color : red;
}
diff --git a/themes/light/tt-rss.less b/themes/light/tt-rss.less
index 65ec33bc3..35eec3e48 100644
--- a/themes/light/tt-rss.less
+++ b/themes/light/tt-rss.less
@@ -516,7 +516,6 @@ body.ttrss_main {
}
.dijitDialog {
-
header,
.dlgSec, .dlgSecHoriz {
font-size : 16px;
@@ -531,6 +530,7 @@ body.ttrss_main {
}
header.horizontal + section,
+ section.horizontal,
.dlgSecHoriz + .dlgSecCont {
margin : 10px 0;
}
@@ -584,6 +584,12 @@ body.ttrss_main {
footer.text-center {
text-align: center;
}
+
+ textarea#tags_str {
+ height : 100px;
+ font-size : 12px;
+ width : 98%;
+ }
}
i.icon-label {
@@ -609,28 +615,14 @@ body.ttrss_main {
width : 18px;
}
- #exceptionDlg {
- .dijitDialogTitleBar {
- background : red;
- color : white;
+ .exception-contents {
+ h3 {
+ color : red;
}
-
- .dijitDialogPaneContent {
- background : #fcc;
- }
-
- .error-contents {
- .message {
- color : red;
- }
-
- textarea {
- width : 99%;
- height : 200px;
- }
- .dlgButtons {
- text-align : center;
- }
+ textarea {
+ width : 99%;
+ height : 200px;
+ font-size : 11px;
}
}
@@ -834,6 +826,8 @@ body.ttrss_main {
}
#toolbar-headlines {
+ font-size : 12px;
+ background: transparent;
padding-right : 4px;
flex-grow : 2;
display : flex;
@@ -843,7 +837,7 @@ body.ttrss_main {
display : flex;
align-items : center;
- #feed_title {
+ .feed_title, .cancel_search {
margin-left : 4px;
}
}
@@ -884,6 +878,19 @@ body.ttrss_main {
right : 0px;
top : 0px;
z-index : 5;
+
+ i.net-alert, .left i.icon-error {
+ color : red;
+ }
+
+ i.log-alert {
+ color : #ddba1c;
+ cursor : pointer;
+ }
+
+ i {
+ margin : 0 4px;
+ }
}
#content-insert {
@@ -949,16 +956,18 @@ body.ttrss_main {
}
ul#filterDlg_Matches, ul#filterDlg_Actions {
- max-height : 100px;
- overflow : auto;
list-style-type : none;
+ margin : 0;
+ padding: 0;
+ /*max-height : 100px;
+ overflow : auto;
border-style : solid;
border-color : @border-default;
border-width : 1px 1px 1px 1px;
background-color : @default-bg;
margin : 0px 0px 5px 0px;
padding : 4px;
- min-height : 16px;
+ min-height : 16px;*/
}
ul#filterDlg_Matches li, ul#filterDlg_Actions li {
@@ -1004,18 +1013,10 @@ body.ttrss_main {
color : #cc90cc;
}
- div.enclosure_title {
-
- }
-
#headlines-frame .dijitCheckBox {
margin-right : 4px;
}
- #editTagsDlg{
- overflow: visible;
- }
-
#feedEditDlg img.feedIcon {
border : 1px solid #ccc;
padding : 5px;
@@ -1048,14 +1049,14 @@ body.ttrss_main {
}
}
-body.ttrss_main .dijitDialog {
+/*body.ttrss_main .dijitDialog {
h1:first-of-type,
h2:first-of-type,
h3:first-of-type,
h4:first-of-type {
margin-top: 0px;
}
-}
+}*/
body.ttrss_main[view-mode="marked"] #feeds-holder #feedTree {
.dijitTreeRow.Has_Marked .dijitTreeLabel {
diff --git a/themes/night.css b/themes/night.css
index 6090890e2..1e397ee74 100644
--- a/themes/night.css
+++ b/themes/night.css
@@ -457,6 +457,7 @@ body.ttrss_main .dijitDialog .dlgSecCont {
margin: 10px 20px;
}
body.ttrss_main .dijitDialog header.horizontal + section,
+body.ttrss_main .dijitDialog section.horizontal,
body.ttrss_main .dijitDialog .dlgSecHoriz + .dlgSecCont {
margin: 10px 0;
}
@@ -507,6 +508,11 @@ body.ttrss_main .dijitDialog .dlgButtons {
body.ttrss_main .dijitDialog footer.text-center {
text-align: center;
}
+body.ttrss_main .dijitDialog textarea#tags_str {
+ height: 100px;
+ font-size: 12px;
+ width: 98%;
+}
body.ttrss_main i.icon-label {
color: #fff7d5;
}
@@ -527,22 +533,13 @@ body.ttrss_main #feed_browser_spinner {
height: 18px;
width: 18px;
}
-body.ttrss_main #exceptionDlg .dijitDialogTitleBar {
- background: red;
- color: white;
-}
-body.ttrss_main #exceptionDlg .dijitDialogPaneContent {
- background: #fcc;
-}
-body.ttrss_main #exceptionDlg .error-contents .message {
+body.ttrss_main .exception-contents h3 {
color: red;
}
-body.ttrss_main #exceptionDlg .error-contents textarea {
+body.ttrss_main .exception-contents textarea {
width: 99%;
height: 200px;
-}
-body.ttrss_main #exceptionDlg .error-contents .dlgButtons {
- text-align: center;
+ font-size: 11px;
}
body.ttrss_main #content-wrap {
padding: 0px;
@@ -714,6 +711,8 @@ body.ttrss_main #toolbar-frame #toolbar i {
margin: 0 4px;
}
body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines {
+ font-size: 12px;
+ background: transparent;
padding-right: 4px;
flex-grow: 2;
display: flex;
@@ -723,7 +722,8 @@ body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left {
display: flex;
align-items: center;
}
-body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left #feed_title {
+body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title,
+body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .cancel_search {
margin-left: 4px;
}
body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .right {
@@ -756,6 +756,17 @@ body.ttrss_main #header {
top: 0px;
z-index: 5;
}
+body.ttrss_main #header i.net-alert,
+body.ttrss_main #header .left i.icon-error {
+ color: red;
+}
+body.ttrss_main #header i.log-alert {
+ color: #ddba1c;
+ cursor: pointer;
+}
+body.ttrss_main #header i {
+ margin: 0 4px;
+}
body.ttrss_main #content-insert {
padding: 0px;
border-color: #222;
@@ -813,16 +824,18 @@ body.ttrss_main #headlines-spacer a:hover {
}
body.ttrss_main ul#filterDlg_Matches,
body.ttrss_main ul#filterDlg_Actions {
- max-height: 100px;
- overflow: auto;
list-style-type: none;
- border-style: solid;
- border-color: #222;
- border-width: 1px 1px 1px 1px;
- background-color: #333;
- margin: 0px 0px 5px 0px;
- padding: 4px;
- min-height: 16px;
+ margin: 0;
+ padding: 0;
+ /*max-height : 100px;
+ overflow : auto;
+ border-style : solid;
+ border-color : @border-default;
+ border-width : 1px 1px 1px 1px;
+ background-color : @default-bg;
+ margin : 0px 0px 5px 0px;
+ padding : 4px;
+ min-height : 16px;*/
}
body.ttrss_main ul#filterDlg_Matches li,
body.ttrss_main ul#filterDlg_Actions li {
@@ -861,9 +874,6 @@ body.ttrss_main span.highlight {
body.ttrss_main #headlines-frame .dijitCheckBox {
margin-right: 4px;
}
-body.ttrss_main #editTagsDlg {
- overflow: visible;
-}
body.ttrss_main #feedEditDlg img.feedIcon {
border: 1px solid #ccc;
padding: 5px;
@@ -889,12 +899,14 @@ body.ttrss_main .dijitTooltipBelow .dijitTooltipConnector {
body.ttrss_main .dijitTooltipAbove .dijitTooltipConnector {
border-top-color: #d29745;
}
-body.ttrss_main .dijitDialog h1:first-of-type,
-body.ttrss_main .dijitDialog h2:first-of-type,
-body.ttrss_main .dijitDialog h3:first-of-type,
-body.ttrss_main .dijitDialog h4:first-of-type {
- margin-top: 0px;
-}
+/*body.ttrss_main .dijitDialog {
+ h1:first-of-type,
+ h2:first-of-type,
+ h3:first-of-type,
+ h4:first-of-type {
+ margin-top: 0px;
+ }
+}*/
body.ttrss_main[view-mode="marked"] #feeds-holder #feedTree .dijitTreeRow.Has_Marked .dijitTreeLabel {
color: #b87d2c;
}
@@ -1404,6 +1416,13 @@ body.ttrss_prefs {
background-color: #222;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
+ /*.dijitContentPane {
+ h1:first-of-type,
+ h2:first-of-type,
+ h3:first-of-type {
+ margin-top: 0px;
+ }
+ }*/
}
body.ttrss_prefs h1,
body.ttrss_prefs h2,
@@ -1413,11 +1432,6 @@ body.ttrss_prefs h4 {
font-weight: 600;
color: #ccc;
}
-body.ttrss_prefs .dijitContentPane h1:first-of-type,
-body.ttrss_prefs .dijitContentPane h2:first-of-type,
-body.ttrss_prefs .dijitContentPane h3:first-of-type {
- margin-top: 0px;
-}
body.ttrss_prefs #footer,
body.ttrss_prefs #header {
padding: 8px;
@@ -1451,22 +1465,13 @@ body.ttrss_prefs .dijitAccordionTitle i.material-icons {
body.ttrss_prefs .dijitAccordionTitleSelected i.material-icons {
color: white;
}
+body.ttrss_prefs #feedsTab {
+ background: #222;
+}
body.ttrss_prefs .dijitDialog #pref-profiles-list .dijitInlineEditBoxDisplayMode {
padding: 0px;
}
-body.ttrss_prefs div#feedlistLoading,
-body.ttrss_prefs div#filterlistLoading,
-body.ttrss_prefs div#labellistLoading {
- text-align: center;
- padding: 5px;
- color: #ccc;
-}
-body.ttrss_prefs div#feedlistLoading img,
-body.ttrss_prefs div#filterlistLoading img,
-body.ttrss_prefs div#labellistLoading {
- margin-right: 5px;
-}
-body.ttrss_prefs #errorButton {
+body.ttrss_prefs #pref_feeds_errors_btn {
color: red;
}
body.ttrss_prefs .user-css-editor {
@@ -1605,6 +1610,13 @@ body.ttrss_utility fieldset > label.checkbox {
font-size: 13px;
padding: 0px;
}
+.flat .dijitToolbar .dijitTextBox .dijitInputInner {
+ line-height: 10px;
+}
+.flat .dijitToolbar label {
+ position: relative;
+ top: 2px;
+}
.flat .dijitAccordionContainer {
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.1);
}
diff --git a/themes/night_blue.css b/themes/night_blue.css
index 4bea2256f..361cd4fbf 100644
--- a/themes/night_blue.css
+++ b/themes/night_blue.css
@@ -457,6 +457,7 @@ body.ttrss_main .dijitDialog .dlgSecCont {
margin: 10px 20px;
}
body.ttrss_main .dijitDialog header.horizontal + section,
+body.ttrss_main .dijitDialog section.horizontal,
body.ttrss_main .dijitDialog .dlgSecHoriz + .dlgSecCont {
margin: 10px 0;
}
@@ -507,6 +508,11 @@ body.ttrss_main .dijitDialog .dlgButtons {
body.ttrss_main .dijitDialog footer.text-center {
text-align: center;
}
+body.ttrss_main .dijitDialog textarea#tags_str {
+ height: 100px;
+ font-size: 12px;
+ width: 98%;
+}
body.ttrss_main i.icon-label {
color: #fff7d5;
}
@@ -527,22 +533,13 @@ body.ttrss_main #feed_browser_spinner {
height: 18px;
width: 18px;
}
-body.ttrss_main #exceptionDlg .dijitDialogTitleBar {
- background: red;
- color: white;
-}
-body.ttrss_main #exceptionDlg .dijitDialogPaneContent {
- background: #fcc;
-}
-body.ttrss_main #exceptionDlg .error-contents .message {
+body.ttrss_main .exception-contents h3 {
color: red;
}
-body.ttrss_main #exceptionDlg .error-contents textarea {
+body.ttrss_main .exception-contents textarea {
width: 99%;
height: 200px;
-}
-body.ttrss_main #exceptionDlg .error-contents .dlgButtons {
- text-align: center;
+ font-size: 11px;
}
body.ttrss_main #content-wrap {
padding: 0px;
@@ -714,6 +711,8 @@ body.ttrss_main #toolbar-frame #toolbar i {
margin: 0 4px;
}
body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines {
+ font-size: 12px;
+ background: transparent;
padding-right: 4px;
flex-grow: 2;
display: flex;
@@ -723,7 +722,8 @@ body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left {
display: flex;
align-items: center;
}
-body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left #feed_title {
+body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title,
+body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .cancel_search {
margin-left: 4px;
}
body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .right {
@@ -756,6 +756,17 @@ body.ttrss_main #header {
top: 0px;
z-index: 5;
}
+body.ttrss_main #header i.net-alert,
+body.ttrss_main #header .left i.icon-error {
+ color: red;
+}
+body.ttrss_main #header i.log-alert {
+ color: #ddba1c;
+ cursor: pointer;
+}
+body.ttrss_main #header i {
+ margin: 0 4px;
+}
body.ttrss_main #content-insert {
padding: 0px;
border-color: #222;
@@ -813,16 +824,18 @@ body.ttrss_main #headlines-spacer a:hover {
}
body.ttrss_main ul#filterDlg_Matches,
body.ttrss_main ul#filterDlg_Actions {
- max-height: 100px;
- overflow: auto;
list-style-type: none;
- border-style: solid;
- border-color: #222;
- border-width: 1px 1px 1px 1px;
- background-color: #333;
- margin: 0px 0px 5px 0px;
- padding: 4px;
- min-height: 16px;
+ margin: 0;
+ padding: 0;
+ /*max-height : 100px;
+ overflow : auto;
+ border-style : solid;
+ border-color : @border-default;
+ border-width : 1px 1px 1px 1px;
+ background-color : @default-bg;
+ margin : 0px 0px 5px 0px;
+ padding : 4px;
+ min-height : 16px;*/
}
body.ttrss_main ul#filterDlg_Matches li,
body.ttrss_main ul#filterDlg_Actions li {
@@ -861,9 +874,6 @@ body.ttrss_main span.highlight {
body.ttrss_main #headlines-frame .dijitCheckBox {
margin-right: 4px;
}
-body.ttrss_main #editTagsDlg {
- overflow: visible;
-}
body.ttrss_main #feedEditDlg img.feedIcon {
border: 1px solid #ccc;
padding: 5px;
@@ -889,12 +899,14 @@ body.ttrss_main .dijitTooltipBelow .dijitTooltipConnector {
body.ttrss_main .dijitTooltipAbove .dijitTooltipConnector {
border-top-color: #2e99d1;
}
-body.ttrss_main .dijitDialog h1:first-of-type,
-body.ttrss_main .dijitDialog h2:first-of-type,
-body.ttrss_main .dijitDialog h3:first-of-type,
-body.ttrss_main .dijitDialog h4:first-of-type {
- margin-top: 0px;
-}
+/*body.ttrss_main .dijitDialog {
+ h1:first-of-type,
+ h2:first-of-type,
+ h3:first-of-type,
+ h4:first-of-type {
+ margin-top: 0px;
+ }
+}*/
body.ttrss_main[view-mode="marked"] #feeds-holder #feedTree .dijitTreeRow.Has_Marked .dijitTreeLabel {
color: #257aa7;
}
@@ -1404,6 +1416,13 @@ body.ttrss_prefs {
background-color: #222;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
+ /*.dijitContentPane {
+ h1:first-of-type,
+ h2:first-of-type,
+ h3:first-of-type {
+ margin-top: 0px;
+ }
+ }*/
}
body.ttrss_prefs h1,
body.ttrss_prefs h2,
@@ -1413,11 +1432,6 @@ body.ttrss_prefs h4 {
font-weight: 600;
color: #ccc;
}
-body.ttrss_prefs .dijitContentPane h1:first-of-type,
-body.ttrss_prefs .dijitContentPane h2:first-of-type,
-body.ttrss_prefs .dijitContentPane h3:first-of-type {
- margin-top: 0px;
-}
body.ttrss_prefs #footer,
body.ttrss_prefs #header {
padding: 8px;
@@ -1451,22 +1465,13 @@ body.ttrss_prefs .dijitAccordionTitle i.material-icons {
body.ttrss_prefs .dijitAccordionTitleSelected i.material-icons {
color: white;
}
+body.ttrss_prefs #feedsTab {
+ background: #222;
+}
body.ttrss_prefs .dijitDialog #pref-profiles-list .dijitInlineEditBoxDisplayMode {
padding: 0px;
}
-body.ttrss_prefs div#feedlistLoading,
-body.ttrss_prefs div#filterlistLoading,
-body.ttrss_prefs div#labellistLoading {
- text-align: center;
- padding: 5px;
- color: #ccc;
-}
-body.ttrss_prefs div#feedlistLoading img,
-body.ttrss_prefs div#filterlistLoading img,
-body.ttrss_prefs div#labellistLoading {
- margin-right: 5px;
-}
-body.ttrss_prefs #errorButton {
+body.ttrss_prefs #pref_feeds_errors_btn {
color: red;
}
body.ttrss_prefs .user-css-editor {
@@ -1605,6 +1610,13 @@ body.ttrss_utility fieldset > label.checkbox {
font-size: 13px;
padding: 0px;
}
+.flat .dijitToolbar .dijitTextBox .dijitInputInner {
+ line-height: 10px;
+}
+.flat .dijitToolbar label {
+ position: relative;
+ top: 2px;
+}
.flat .dijitAccordionContainer {
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.1);
}
diff --git a/update.php b/update.php
index 56158ca48..8d8566db7 100755
--- a/update.php
+++ b/update.php
@@ -1,21 +1,18 @@
#!/usr/bin/env php
run_hooks(PluginHost::HOOK_UPDATE_TASK, $options);
@@ -225,10 +222,10 @@
$log = isset($options['log']) ? '--log '.$options['log'] : '';
$log_level = isset($options['log-level']) ? '--log-level '.$options['log-level'] : '';
- passthru(PHP_EXECUTABLE . " " . $argv[0] ." --daemon-loop $quiet $log $log_level");
+ passthru(Config::get(Config::PHP_EXECUTABLE) . " " . $argv[0] ." --daemon-loop $quiet $log $log_level");
// let's enforce a minimum spawn interval as to not forkbomb the host
- $spawn_interval = max(60, DAEMON_SLEEP_INTERVAL);
+ $spawn_interval = max(60, Config::get(Config::DAEMON_SLEEP_INTERVAL));
Debug::log("Sleeping for $spawn_interval seconds...");
sleep($spawn_interval);
@@ -256,7 +253,7 @@
Debug::log("warning: unable to create stampfile\n");
}
- RSSUtils::update_daemon_common(isset($options["pidlock"]) ? 50 : DAEMON_FEED_LIMIT, $options);
+ RSSUtils::update_daemon_common(isset($options["pidlock"]) ? 50 : Config::get(Config::DAEMON_FEED_LIMIT), $options);
if (!isset($options["pidlock"]) || $options["task"] == 0)
RSSUtils::housekeeping_common();
@@ -278,7 +275,7 @@
Debug::log("clearing existing indexes...");
- if (DB_TYPE == "pgsql") {
+ if (Config::get(Config::DB_TYPE) == "pgsql") {
$sth = $pdo->query( "SELECT relname FROM
pg_catalog.pg_class WHERE relname LIKE 'ttrss_%'
AND relname NOT LIKE '%_pkey'
@@ -289,7 +286,7 @@
}
while ($line = $sth->fetch()) {
- if (DB_TYPE == "pgsql") {
+ if (Config::get(Config::DB_TYPE) == "pgsql") {
$statement = "DROP INDEX " . $line["relname"];
Debug::log($statement);
} else {
@@ -300,9 +297,9 @@
$pdo->query($statement);
}
- Debug::log("reading indexes from schema for: " . DB_TYPE);
+ Debug::log("reading indexes from schema for: " . Config::get(Config::DB_TYPE));
- $fp = fopen("schema/ttrss_schema_" . DB_TYPE . ".sql", "r");
+ $fp = fopen("schema/ttrss_schema_" . Config::get(Config::DB_TYPE) . ".sql", "r");
if ($fp) {
while ($line = fgets($fp)) {
$matches = array();
@@ -376,14 +373,14 @@
}
if (isset($options["update-schema"])) {
- Debug::log("Checking for updates (" . DB_TYPE . ")...");
+ Debug::log("Checking for updates (" . Config::get(Config::DB_TYPE) . ")...");
- $updater = new DbUpdater(Db::pdo(), DB_TYPE, SCHEMA_VERSION);
+ $updater = new DbUpdater(Db::pdo(), Config::get(Config::DB_TYPE), SCHEMA_VERSION);
- if ($updater->isUpdateRequired()) {
- Debug::log("Schema update required, version " . $updater->getSchemaVersion() . " to " . SCHEMA_VERSION);
+ if ($updater->is_update_required()) {
+ Debug::log("Schema update required, version " . $updater->get_schema_version() . " to " . SCHEMA_VERSION);
- if (DB_TYPE == "mysql")
+ if (Config::get(Config::DB_TYPE) == "mysql")
Debug::Log("READ THIS: Due to MySQL limitations, your database is not completely protected while updating.\n".
"Errors may put it in an inconsistent state requiring manual rollback.\nBACKUP YOUR DATABASE BEFORE CONTINUING.");
else
@@ -400,10 +397,10 @@
Debug::log("Performing updates to version " . SCHEMA_VERSION . "...");
- for ($i = $updater->getSchemaVersion() + 1; $i <= SCHEMA_VERSION; $i++) {
+ for ($i = $updater->get_schema_version() + 1; $i <= SCHEMA_VERSION; $i++) {
Debug::log("* Updating to version $i...");
- $result = $updater->performUpdateTo($i, false);
+ $result = $updater->update_to($i, false);
if ($result) {
Debug::log("* Completed.");
@@ -460,8 +457,8 @@
if (isset($options["list-plugins"])) {
$tmppluginhost = new PluginHost();
- $tmppluginhost->load_all($tmppluginhost::KIND_ALL, false);
- $enabled = array_map("trim", explode(",", PLUGINS));
+ $tmppluginhost->load_all($tmppluginhost::KIND_ALL);
+ $enabled = array_map("trim", explode(",", Config::get(Config::PLUGINS)));
echo "List of all available plugins:\n";
@@ -516,8 +513,8 @@
PluginHost::getInstance()->run_commands($options);
- if (file_exists(LOCK_DIRECTORY . "/$lock_filename"))
+ if (file_exists(Config::get(Config::LOCK_DIRECTORY) . "/$lock_filename"))
if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN')
fclose($lock_handle);
- unlink(LOCK_DIRECTORY . "/$lock_filename");
+ unlink(Config::get(Config::LOCK_DIRECTORY) . "/$lock_filename");
?>
diff --git a/update_daemon2.php b/update_daemon2.php
index 61cc85617..b75f06ae5 100755
--- a/update_daemon2.php
+++ b/update_daemon2.php
@@ -1,26 +1,16 @@
#!/usr/bin/env php
MAX_CHILD_RUNTIME) {
+ if (time() - $started > Config::get(Config::DAEMON_MAX_CHILD_RUNTIME)) {
Debug::log("Child process with PID $pid seems to be stuck, aborting...");
posix_kill($pid, SIGKILL);
}
@@ -99,9 +89,9 @@
function shutdown($caller_pid) {
if ($caller_pid == posix_getpid()) {
- if (file_exists(LOCK_DIRECTORY . "/update_daemon.lock")) {
+ if (file_exists(Config::get(Config::LOCK_DIRECTORY) . "/update_daemon.lock")) {
Debug::log("Removing lockfile (master)...");
- unlink(LOCK_DIRECTORY . "/update_daemon.lock");
+ unlink(Config::get(Config::LOCK_DIRECTORY) . "/update_daemon.lock");
}
}
}
@@ -109,9 +99,9 @@
function task_shutdown() {
$pid = posix_getpid();
- if (file_exists(LOCK_DIRECTORY . "/update_daemon-$pid.lock")) {
+ if (file_exists(Config::get(Config::LOCK_DIRECTORY) . "/update_daemon-$pid.lock")) {
Debug::log("Removing task lockfile for PID $pid...");
- unlink(LOCK_DIRECTORY . "/update_daemon-$pid.lock");
+ unlink(Config::get(Config::LOCK_DIRECTORY) . "/update_daemon-$pid.lock");
}
}
@@ -144,9 +134,9 @@
print " --log FILE - log messages to FILE\n";
print " --log-level N - log verbosity level\n";
print " --tasks N - amount of update tasks to spawn\n";
- print " default: " . MAX_JOBS . "\n";
+ print " default: " . Config::get(Config::DAEMON_MAX_JOBS) . "\n";
print " --interval N - task spawn interval\n";
- print " default: " . SPAWN_INTERVAL . " seconds.\n";
+ print " default: " . Config::get(Config::DAEMON_SLEEP_INTERVAL) . " seconds.\n";
print " --quiet - don't output messages to stdout\n";
return;
}
@@ -171,14 +161,14 @@
Debug::log("Set to spawn " . $options["tasks"] . " children.");
$max_jobs = $options["tasks"];
} else {
- $max_jobs = MAX_JOBS;
+ $max_jobs = Config::get(Config::DAEMON_MAX_JOBS);
}
if (isset($options["interval"])) {
Debug::log("Spawn interval: " . $options["interval"] . " seconds.");
$spawn_interval = $options["interval"];
} else {
- $spawn_interval = SPAWN_INTERVAL;
+ $spawn_interval = Config::get(Config::DAEMON_SLEEP_INTERVAL);
}
// let's enforce a minimum spawn interval as to not forkbomb the host
@@ -250,7 +240,7 @@
$my_pid = posix_getpid();
- passthru(PHP_EXECUTABLE . " update.php --daemon-loop $quiet $log --task $j --pidlock $my_pid");
+ passthru(Config::get(Config::PHP_EXECUTABLE) . " update.php --daemon-loop $quiet $log --task $j --pidlock $my_pid");
sleep(1);
diff --git a/utils/phpstan_tunables.php b/utils/phpstan_tunables.php
deleted file mode 100644
index 7d5d8f03a..000000000
--- a/utils/phpstan_tunables.php
+++ /dev/null
@@ -1,44 +0,0 @@
- $DESTINATION
-
-echo -n "define('GENERATED_CONFIG_CHECK', " >> $DESTINATION
-grep CONFIG_VERSION config.php-dist | awk -F ' |\)' '{ print $2 }' | xargs echo -n >> $DESTINATION
-echo ");" >> $DESTINATION
-
-echo -n "\$required_defines = array( " >> $DESTINATION
-
-grep define\( config.php-dist | awk -F\' '{ print "*" $2 "*," }' | grep -v DB_PORT | xargs echo -n | sed -e s/,$// -e s/*/\'/g >> $DESTINATION
-
-echo "); ?>" >> $DESTINATION
-
-