Merge branch 'master' of git.tt-rss.org:fox/tt-rss

This commit is contained in:
Andrew Dolgov 2021-03-21 09:35:55 +03:00
commit 1f5adf1600
1 changed files with 171 additions and 206 deletions

View File

@ -132,49 +132,48 @@ class API extends Handler {
// TODO do not return empty categories, return Uncategorized and standard virtual cats // TODO do not return empty categories, return Uncategorized and standard virtual cats
if ($enable_nested) $categories = ORM::for_table('ttrss_feed_categories')
$nested_qpart = "parent_cat IS NULL"; ->select_many('id', 'title', 'order_id')
else ->select_many_expr([
$nested_qpart = "true"; 'num_feeds' => '(SELECT COUNT(id) FROM ttrss_feeds WHERE ttrss_feed_categories.id IS NOT NULL AND cat_id = ttrss_feed_categories.id)',
'num_cats' => '(SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE c2.parent_cat = ttrss_feed_categories.id)',
])
->where('owner_uid', $_SESSION['uid']);
$sth = $this->pdo->prepare("SELECT if ($enable_nested) {
id, title, order_id, (SELECT COUNT(id) FROM $categories->where_null('parent_cat');
ttrss_feeds WHERE }
ttrss_feed_categories.id IS NOT NULL AND cat_id = ttrss_feed_categories.id) AS num_feeds,
(SELECT COUNT(id) FROM
ttrss_feed_categories AS c2 WHERE
c2.parent_cat = ttrss_feed_categories.id) AS num_cats
FROM ttrss_feed_categories
WHERE $nested_qpart AND owner_uid = ?");
$sth->execute([$_SESSION['uid']]);
$cats = array(); $cats = [];
while ($line = $sth->fetch()) { foreach ($categories->find_many() as $category) {
if ($include_empty || $line["num_feeds"] > 0 || $line["num_cats"] > 0) { if ($include_empty || $category->num_feeds > 0 || $category->num_cats > 0) {
$unread = getFeedUnread($line["id"], true); $unread = getFeedUnread($category->id, true);
if ($enable_nested) if ($enable_nested)
$unread += Feeds::_get_cat_children_unread($line["id"]); $unread += Feeds::_get_cat_children_unread($category->id);
if ($unread || !$unread_only) { if ($unread || !$unread_only) {
array_push($cats, array("id" => (int) $line["id"], array_push($cats, [
"title" => $line["title"], 'id' => (int) $category->id,
"unread" => (int) $unread, 'title' => $category->title,
"order_id" => (int) $line["order_id"], 'unread' => (int) $unread,
)); 'order_id' => (int) $category->order_id,
]);
} }
} }
} }
foreach (array(-2,-1,0) as $cat_id) { foreach ([-2,-1,0] as $cat_id) {
if ($include_empty || !$this->_is_cat_empty($cat_id)) { if ($include_empty || !$this->_is_cat_empty($cat_id)) {
$unread = getFeedUnread($cat_id, true); $unread = getFeedUnread($cat_id, true);
if ($unread || !$unread_only) { if ($unread || !$unread_only) {
array_push($cats, array("id" => $cat_id, array_push($cats, [
"title" => Feeds::_get_cat_title($cat_id), 'id' => $cat_id,
"unread" => (int) $unread)); 'title' => Feeds::_get_cat_title($cat_id),
'unread' => (int) $unread,
]);
} }
} }
} }
@ -300,60 +299,58 @@ class API extends Handler {
} }
function getArticle() { function getArticle() {
$article_ids = explode(',', clean($_REQUEST['article_id'] ?? ''));
$sanitize_content = self::_param_to_bool($_REQUEST['sanitize'] ?? true);
$article_ids = explode(",", clean($_REQUEST["article_id"])); if (count($article_ids)) {
$sanitize_content = !isset($_REQUEST["sanitize"]) || $entries = ORM::for_table('ttrss_entries')
self::_param_to_bool($_REQUEST["sanitize"]); ->table_alias('e')
->select_many('e.id', 'e.guid', 'e.title', 'e.link', 'e.author', 'e.content', 'e.lang', 'e.comments',
'ue.feed_id', 'ue.int_id', 'ue.marked', 'ue.unread', 'ue.published', 'ue.score', 'ue.note')
->select_many_expr([
'updated' => SUBSTRING_FOR_DATE.'(updated,1,16)',
'feed_title' => '(SELECT title FROM ttrss_feeds WHERE id = ue.feed_id)',
'site_url' => '(SELECT site_url FROM ttrss_feeds WHERE id = ue.feed_id)',
'hide_images' => '(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id)',
])
->join('ttrss_user_entries', [ 'ue.ref_id', '=', 'e.id'], 'ue')
->where_in('e.id', array_map('intval', $article_ids))
->where('ue.owner_uid', $_SESSION['uid'])
->find_many();
if (count($article_ids) > 0) { $articles = [];
$article_qmarks = arr_qmarks($article_ids); foreach ($entries as $entry) {
$article = [
$sth = $this->pdo->prepare("SELECT id,guid,title,link,content,feed_id,comments,int_id, 'id' => $entry->id,
marked,unread,published,score,note,lang, 'guid' => $entry->guid,
".SUBSTRING_FOR_DATE."(updated,1,16) as updated, 'title' => $entry->title,
author,(SELECT title FROM ttrss_feeds WHERE id = feed_id) AS feed_title, 'link' => $entry->link,
(SELECT site_url FROM ttrss_feeds WHERE id = feed_id) AS site_url, 'labels' => Article::_get_labels($entry->id),
(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images 'unread' => self::_param_to_bool($entry->unread),
FROM ttrss_entries,ttrss_user_entries 'marked' => self::_param_to_bool($entry->marked),
WHERE id IN ($article_qmarks) AND ref_id = id AND owner_uid = ?"); 'published' => self::_param_to_bool($entry->published),
'comments' => $entry->comments,
$sth->execute(array_merge($article_ids, [$_SESSION['uid']])); 'author' => $entry->author,
'updated' => (int) strtotime($entry->updated),
$articles = array(); 'feed_id' => $entry->feed_id,
'attachments' => Article::_get_enclosures($entry->id),
while ($line = $sth->fetch()) { 'score' => (int) $entry->score,
'feed_title' => $entry->feed_title,
$article = array( 'note' => $entry->note,
"id" => $line["id"], 'lang' => $entry->lang,
"guid" => $line["guid"], ];
"title" => $line["title"],
"link" => $line["link"],
"labels" => Article::_get_labels($line['id']),
"unread" => self::_param_to_bool($line["unread"]),
"marked" => self::_param_to_bool($line["marked"]),
"published" => self::_param_to_bool($line["published"]),
"comments" => $line["comments"],
"author" => $line["author"],
"updated" => (int) strtotime($line["updated"]),
"feed_id" => $line["feed_id"],
"attachments" => Article::_get_enclosures($line['id']),
"score" => (int)$line["score"],
"feed_title" => $line["feed_title"],
"note" => $line["note"],
"lang" => $line["lang"]
);
if ($sanitize_content) { if ($sanitize_content) {
$article["content"] = Sanitizer::sanitize( $article['content'] = Sanitizer::sanitize(
$line["content"], $entry->content,
self::_param_to_bool($line['hide_images']), self::_param_to_bool($entry->hide_images),
false, $line["site_url"], false, $line["id"]); false, $entry->site_url, false, $entry->id);
} else { } else {
$article["content"] = $line["content"]; $article['content'] = $entry->content;
} }
$hook_object = ["article" => &$article]; $hook_object = ['article' => &$article];
PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_RENDER_ARTICLE_API, PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_RENDER_ARTICLE_API,
function ($result) use (&$article) { function ($result) use (&$article) {
@ -364,13 +361,12 @@ class API extends Handler {
$article['content'] = DiskCache::rewrite_urls($article['content']); $article['content'] = DiskCache::rewrite_urls($article['content']);
array_push($articles, $article); array_push($articles, $article);
} }
$this->_wrap(self::STATUS_OK, $articles); $this->_wrap(self::STATUS_OK, $articles);
// @phpstan-ignore-next-line // @phpstan-ignore-next-line
} else { } else {
$this->_wrap(self::STATUS_ERR, array("error" => self::E_INCORRECT_USAGE)); $this->_wrap(self::STATUS_ERR, ['error' => self::E_INCORRECT_USAGE]);
} }
} }
@ -382,12 +378,9 @@ class API extends Handler {
$config["daemon_is_running"] = file_is_locked("update_daemon.lock"); $config["daemon_is_running"] = file_is_locked("update_daemon.lock");
$sth = $this->pdo->prepare("SELECT COUNT(*) AS cf FROM $config["num_feeds"] = ORM::for_table('ttrss_feeds')
ttrss_feeds WHERE owner_uid = ?"); ->where('owner_uid', $_SESSION['uid'])
$sth->execute([$_SESSION['uid']]); ->count();
$row = $sth->fetch();
$config["num_feeds"] = $row["cf"];
$this->_wrap(self::STATUS_OK, $config); $this->_wrap(self::STATUS_OK, $config);
} }
@ -422,36 +415,36 @@ class API extends Handler {
} }
function getLabels() { function getLabels() {
$article_id = (int)clean($_REQUEST['article_id']); $article_id = (int)clean($_REQUEST['article_id'] ?? -1);
$rv = array(); $rv = [];
$sth = $this->pdo->prepare("SELECT id, caption, fg_color, bg_color $labels = ORM::for_table('ttrss_labels2')
FROM ttrss_labels2 ->where('owner_uid', $_SESSION['uid'])
WHERE owner_uid = ? ORDER BY caption"); ->order_by_asc('caption')
$sth->execute([$_SESSION['uid']]); ->find_many();
if ($article_id) if ($article_id)
$article_labels = Article::_get_labels($article_id); $article_labels = Article::_get_labels($article_id);
else else
$article_labels = array(); $article_labels = [];
while ($line = $sth->fetch()) {
foreach ($labels as $label) {
$checked = false; $checked = false;
foreach ($article_labels as $al) { foreach ($article_labels as $al) {
if (Labels::feed_to_label_id($al[0]) == $line['id']) { if (Labels::feed_to_label_id($al[0]) == $label->id) {
$checked = true; $checked = true;
break; break;
} }
} }
array_push($rv, array( array_push($rv, [
"id" => (int)Labels::label_to_feed_id($line['id']), 'id' => (int) Labels::label_to_feed_id($label->id),
"caption" => $line['caption'], 'caption' => $label->caption,
"fg_color" => $line['fg_color'], 'fg_color' => $label->fg_color,
"bg_color" => $line['bg_color'], 'bg_color' => $label->bg_color,
"checked" => $checked)); 'checked' => $checked,
]);
} }
$this->_wrap(self::STATUS_OK, $rv); $this->_wrap(self::STATUS_OK, $rv);
@ -512,10 +505,7 @@ class API extends Handler {
} }
private static function _api_get_feeds($cat_id, $unread_only, $limit, $offset, $include_nested = false) { private static function _api_get_feeds($cat_id, $unread_only, $limit, $offset, $include_nested = false) {
$feeds = [];
$feeds = array();
$pdo = Db::pdo();
$limit = (int) $limit; $limit = (int) $limit;
$offset = (int) $offset; $offset = (int) $offset;
@ -528,17 +518,15 @@ class API extends Handler {
$counters = Counters::get_labels(); $counters = Counters::get_labels();
foreach (array_values($counters) as $cv) { foreach (array_values($counters) as $cv) {
$unread = $cv['counter'];
$unread = $cv["counter"];
if ($unread || !$unread_only) { if ($unread || !$unread_only) {
$row = [
$row = array( 'id' => (int) $cv['id'],
"id" => (int) $cv["id"], 'title' => $cv['description'],
"title" => $cv["description"], 'unread' => $cv['counter'],
"unread" => $cv["counter"], 'cat_id' => -2,
"cat_id" => -2, ];
);
array_push($feeds, $row); array_push($feeds, $row);
} }
@ -548,45 +536,45 @@ class API extends Handler {
/* Virtual feeds */ /* Virtual feeds */
if ($cat_id == -4 || $cat_id == -1) { if ($cat_id == -4 || $cat_id == -1) {
foreach (array(-1, -2, -3, -4, -6, 0) as $i) { foreach ([-1, -2, -3, -4, -6, 0] as $i) {
$unread = getFeedUnread($i); $unread = getFeedUnread($i);
if ($unread || !$unread_only) { if ($unread || !$unread_only) {
$title = Feeds::_get_title($i); $title = Feeds::_get_title($i);
$row = array( $row = [
"id" => $i, 'id' => $i,
"title" => $title, 'title' => $title,
"unread" => $unread, 'unread' => $unread,
"cat_id" => -1, 'cat_id' => -1,
); ];
array_push($feeds, $row); array_push($feeds, $row);
} }
} }
} }
/* Child cats */ /* Child cats */
if ($include_nested && $cat_id) { if ($include_nested && $cat_id) {
$sth = $pdo->prepare("SELECT $categories = ORM::for_table('ttrss_feed_categories')
id, title, order_id FROM ttrss_feed_categories ->where(['parent_cat' => $cat_id, 'owner_uid' => $_SESSION['uid']])
WHERE parent_cat = ? AND owner_uid = ? ORDER BY order_id, title"); ->order_by_asc('order_id')
->order_by_asc('title')
->find_many();
$sth->execute([$cat_id, $_SESSION['uid']]); foreach ($categories as $category) {
$unread = getFeedUnread($category->id, true) +
while ($line = $sth->fetch()) { Feeds::_get_cat_children_unread($category->id);
$unread = getFeedUnread($line["id"], true) +
Feeds::_get_cat_children_unread($line["id"]);
if ($unread || !$unread_only) { if ($unread || !$unread_only) {
$row = array( $row = [
"id" => (int) $line["id"], 'id' => (int) $category->id,
"title" => $line["title"], 'title' => $category->title,
"unread" => $unread, 'unread' => $unread,
"is_cat" => true, 'is_cat' => true,
"order_id" => (int) $line["order_id"] 'order_id' => (int) $category->order_id,
); ];
array_push($feeds, $row); array_push($feeds, $row);
} }
} }
@ -594,51 +582,36 @@ class API extends Handler {
/* Real feeds */ /* Real feeds */
if ($limit) {
$limit_qpart = "LIMIT $limit OFFSET $offset";
} else {
$limit_qpart = "";
}
/* API only: -3 All feeds, excluding virtual feeds (e.g. Labels and such) */ /* API only: -3 All feeds, excluding virtual feeds (e.g. Labels and such) */
if ($cat_id == -4 || $cat_id == -3) { $feeds_obj = ORM::for_table('ttrss_feeds')
$sth = $pdo->prepare("SELECT ->select_many('id', 'feed_url', 'cat_id', 'title', 'order_id')
id, feed_url, cat_id, title, order_id, ". ->select_expr(SUBSTRING_FOR_DATE.'(last_updated,1,19)', 'last_updated')
SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated ->where('owner_uid', $_SESSION['uid'])
FROM ttrss_feeds WHERE owner_uid = ? ->order_by_asc('order_id')
ORDER BY order_id, title " . $limit_qpart); ->order_by_asc('title');
$sth->execute([$_SESSION['uid']]);
} else { if ($limit) $feeds_obj->limit($limit);
if ($offset) $feeds_obj->offset($offset);
$sth = $pdo->prepare("SELECT if ($cat_id != -3 && $cat_id != -4) {
id, feed_url, cat_id, title, order_id, ". $feeds_obj->where_raw('(cat_id = ? OR (? = 0 AND cat_id IS NULL))', [$cat_id, $cat_id]);
SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated
FROM ttrss_feeds WHERE
(cat_id = :cat OR (:cat = 0 AND cat_id IS NULL))
AND owner_uid = :uid
ORDER BY order_id, title " . $limit_qpart);
$sth->execute([":uid" => $_SESSION['uid'], ":cat" => $cat_id]);
} }
while ($line = $sth->fetch()) { foreach ($feeds_obj->find_many() as $feed) {
$unread = getFeedUnread($feed->id);
$unread = getFeedUnread($line["id"]); $has_icon = Feeds::_has_icon($feed->id);
$has_icon = Feeds::_has_icon($line['id']);
if ($unread || !$unread_only) { if ($unread || !$unread_only) {
$row = [
$row = array( 'feed_url' => $feed->feed_url,
"feed_url" => $line["feed_url"], 'title' => $feed->title,
"title" => $line["title"], 'id' => (int) $feed->id,
"id" => (int)$line["id"], 'unread' => (int) $unread,
"unread" => (int)$unread, 'has_icon' => $has_icon,
"has_icon" => $has_icon, 'cat_id' => (int) $feed->cat_id,
"cat_id" => (int)$line["cat_id"], 'last_updated' => (int) strtotime($feed->last_updated),
"last_updated" => (int) strtotime($line["last_updated"]), 'order_id' => (int) $feed->order_id,
"order_id" => (int) $line["order_id"], ];
);
array_push($feeds, $row); array_push($feeds, $row);
} }
@ -653,26 +626,24 @@ class API extends Handler {
$search = "", $include_nested = false, $sanitize_content = true, $search = "", $include_nested = false, $sanitize_content = true,
$force_update = false, $excerpt_length = 100, $check_first_id = false, $skip_first_id_check = false) { $force_update = false, $excerpt_length = 100, $check_first_id = false, $skip_first_id_check = false) {
$pdo = Db::pdo();
if ($force_update && $feed_id > 0 && is_numeric($feed_id)) { if ($force_update && $feed_id > 0 && is_numeric($feed_id)) {
// Update the feed if required with some basic flood control // Update the feed if required with some basic flood control
$sth = $pdo->prepare( $feed = ORM::for_table('ttrss_feeds')
"SELECT cache_images,".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated ->select_many('id', 'cache_images')
FROM ttrss_feeds WHERE id = ?"); ->select_expr(SUBSTRING_FOR_DATE.'(last_updated,1,19)', 'last_updated')
$sth->execute([$feed_id]); ->find_one($feed_id);
if ($row = $sth->fetch()) { if ($feed) {
$last_updated = strtotime($row["last_updated"]); $last_updated = strtotime($feed->last_updated);
$cache_images = self::_param_to_bool($row["cache_images"]); $cache_images = self::_param_to_bool($feed->cache_images);
if (!$cache_images && time() - $last_updated > 120) { if (!$cache_images && time() - $last_updated > 120) {
RSSUtils::update_rss_feed($feed_id, true); RSSUtils::update_rss_feed($feed_id, true);
} else { } else {
$sth = $pdo->prepare("UPDATE ttrss_feeds SET last_updated = '1970-01-01', last_update_started = '1970-01-01' $feed->last_updated = '1970-01-01';
WHERE id = ?"); $feed->last_update_started = '1970-01-01';
$sth->execute([$feed_id]); $feed->save();
} }
} }
} }
@ -823,15 +794,15 @@ class API extends Handler {
function unsubscribeFeed() { function unsubscribeFeed() {
$feed_id = (int) clean($_REQUEST["feed_id"]); $feed_id = (int) clean($_REQUEST["feed_id"]);
$sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE $feed_exists = ORM::for_table('ttrss_feeds')
id = ? AND owner_uid = ?"); ->where(['id' => $feed_id, 'owner_uid' => $_SESSION['uid']])
$sth->execute([$feed_id, $_SESSION['uid']]); ->count();
if ($row = $sth->fetch()) { if ($feed_exists) {
Pref_Feeds::remove_feed($feed_id, $_SESSION["uid"]); Pref_Feeds::remove_feed($feed_id, $_SESSION['uid']);
$this->_wrap(self::STATUS_OK, array("status" => "OK")); $this->_wrap(self::STATUS_OK, ['status' => 'OK']);
} else { } else {
$this->_wrap(self::STATUS_ERR, array("error" => self::E_OPERATION_FAILED)); $this->_wrap(self::STATUS_ERR, ['error' => self::E_OPERATION_FAILED]);
} }
} }
@ -864,27 +835,21 @@ class API extends Handler {
// only works for labels or uncategorized for the time being // only works for labels or uncategorized for the time being
private function _is_cat_empty($id) { private function _is_cat_empty($id) {
if ($id == -2) { if ($id == -2) {
$sth = $this->pdo->prepare("SELECT COUNT(id) AS count FROM ttrss_labels2 $label_count = ORM::for_table('ttrss_labels2')
WHERE owner_uid = ?"); ->where('owner_uid', $_SESSION['uid'])
$sth->execute([$_SESSION['uid']]); ->count();
$row = $sth->fetch();
return $row["count"] == 0;
return $label_count == 0;
} else if ($id == 0) { } else if ($id == 0) {
$sth = $this->pdo->prepare("SELECT COUNT(id) AS count FROM ttrss_feeds $uncategorized_count = ORM::for_table('ttrss_feeds')
WHERE cat_id IS NULL AND owner_uid = ?"); ->where_null('cat_id')
$sth->execute([$_SESSION['uid']]); ->where('owner_uid', $_SESSION['uid'])
$row = $sth->fetch(); ->count();
return $row["count"] == 0;
return $uncategorized_count == 0;
} }
return false; return false;
} }
} }