summaryrefslogtreecommitdiff
path: root/classes/feeds.php
diff options
context:
space:
mode:
Diffstat (limited to 'classes/feeds.php')
-rwxr-xr-xclasses/feeds.php323
1 files changed, 213 insertions, 110 deletions
diff --git a/classes/feeds.php b/classes/feeds.php
index c1b7e8022..744c463af 100755
--- a/classes/feeds.php
+++ b/classes/feeds.php
@@ -5,7 +5,10 @@ class Feeds extends Handler_Protected {
const NEVER_GROUP_FEEDS = [ -6, 0 ];
const NEVER_GROUP_BY_DATE = [ -2, -1, -3 ];
- private $params;
+ private $params;
+
+ private $viewfeed_timestamp;
+ private $viewfeed_timestamp_last;
function csrf_ignore($method) {
$csrf_ignored = array("index");
@@ -122,6 +125,8 @@ class Feeds extends Handler_Protected {
$disable_cache = false;
+ $this->mark_timestamp("init");
+
$reply = array();
$rgba_cache = array();
@@ -203,6 +208,8 @@ class Feeds extends Handler_Protected {
$qfh_ret = $this->queryFeedHeadlines($params);
}
+ $this->mark_timestamp("db query");
+
$vfeed_group_enabled = get_pref("VFEED_GROUP_BY_FEED") &&
!(in_array($feed, self::NEVER_GROUP_FEEDS) && !$cat_view);
@@ -233,10 +240,13 @@ class Feeds extends Handler_Protected {
$reply['content'] = [];
+ $this->mark_timestamp("object header");
+
$headlines_count = 0;
if (is_object($result)) {
while ($line = $result->fetch(PDO::FETCH_ASSOC)) {
+ $this->mark_timestamp("article start: " . $line["id"] . " " . $line["title"]);
++$headlines_count;
@@ -248,7 +258,9 @@ class Feeds extends Handler_Protected {
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_QUERY_HEADLINES) as $p) {
$line = $p->hook_query_headlines($line, 250, false);
}
- }
+ }
+
+ $this->mark_timestamp(" hook_query_headlines");
$id = $line["id"];
@@ -293,62 +305,83 @@ class Feeds extends Handler_Protected {
array_push($topmost_article_ids, $id);
}
+ $this->mark_timestamp(" labels");
+
if (!$line["feed_title"]) $line["feed_title"] = "";
- $line["buttons_left"] = "";
- foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_LEFT_BUTTON) as $p) {
- $line["buttons_left"] .= $p->hook_article_left_button($line);
- }
+ $line["buttons_left"] = "";
+ foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_LEFT_BUTTON) as $p) {
+ $line["buttons_left"] .= $p->hook_article_left_button($line);
+ }
+
+ $line["buttons"] = "";
+ foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_BUTTON) as $p) {
+ $line["buttons"] .= $p->hook_article_button($line);
+ }
- $line["buttons"] = "";
- foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_BUTTON) as $p) {
- $line["buttons"] .= $p->hook_article_button($line);
- }
+ $this->mark_timestamp(" pre-sanitize");
- $line["content"] = Sanitizer::sanitize($line["content"],
- $line['hide_images'], false, $line["site_url"], $highlight_words, $line["id"]);
+ $line["content"] = Sanitizer::sanitize($line["content"],
+ $line['hide_images'], false, $line["site_url"], $highlight_words, $line["id"]);
- foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE_CDM) as $p) {
- $line = $p->hook_render_article_cdm($line);
- }
+ $this->mark_timestamp(" sanitize");
- $line['content'] = DiskCache::rewriteUrls($line['content']);
+ foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE_CDM) as $p) {
+ $line = $p->hook_render_article_cdm($line);
- if ($line['note'])
- $line['note'] = Article::format_article_note($id, $line['note']);
- else
- $line['note'] = "";
+ $this->mark_timestamp(" hook_render_cdm: " . get_class($p));
+ }
- if (!get_pref("CDM_EXPANDED")) {
- $line["cdm_excerpt"] = "<span class='collapse'>
- <i class='material-icons' onclick='return Article.cdmUnsetActive(event)'
- title=\"" . __("Collapse article") . "\">remove_circle</i></span>";
+ $this->mark_timestamp(" hook_render_cdm");
- if (get_pref('SHOW_CONTENT_PREVIEW')) {
- $line["cdm_excerpt"] .= "<span class='excerpt'>" . $line["content_preview"] . "</span>";
- }
- }
+ $line['content'] = DiskCache::rewriteUrls($line['content']);
- $line["enclosures"] = Article::format_article_enclosures($id, $line["always_display_enclosures"],
- $line["content"], $line["hide_images"]);
+ $this->mark_timestamp(" disk_cache_rewrite");
- if ($line["orig_feed_id"]) {
+ if ($line['note'])
+ $line['note'] = Article::format_article_note($id, $line['note']);
+ else
+ $line['note'] = "";
- $ofgh = $this->pdo->prepare("SELECT * FROM ttrss_archived_feeds
- WHERE id = ? AND owner_uid = ?");
- $ofgh->execute([$line["orig_feed_id"], $_SESSION['uid']]);
+ $this->mark_timestamp(" note");
- if ($tmp_line = $ofgh->fetch()) {
- $line["orig_feed"] = [ $tmp_line["title"], $tmp_line["site_url"], $tmp_line["feed_url"] ];
- }
- }
+ if (!get_pref("CDM_EXPANDED")) {
+ $line["cdm_excerpt"] = "<span class='collapse'>
+ <i class='material-icons' onclick='return Article.cdmUnsetActive(event)'
+ title=\"" . __("Collapse article") . "\">remove_circle</i></span>";
+
+ if (get_pref('SHOW_CONTENT_PREVIEW')) {
+ $line["cdm_excerpt"] .= "<span class='excerpt'>" . $line["content_preview"] . "</span>";
+ }
+ }
+
+ $this->mark_timestamp(" pre-enclosures");
+
+ $line["enclosures"] = Article::format_article_enclosures($id, $line["always_display_enclosures"],
+ $line["content"], $line["hide_images"]);
+
+ $this->mark_timestamp(" enclosures");
+
+ if ($line["orig_feed_id"]) {
+
+ $ofgh = $this->pdo->prepare("SELECT * FROM ttrss_archived_feeds
+ WHERE id = ? AND owner_uid = ?");
+ $ofgh->execute([$line["orig_feed_id"], $_SESSION['uid']]);
+
+ if ($tmp_line = $ofgh->fetch()) {
+ $line["orig_feed"] = [ $tmp_line["title"], $tmp_line["site_url"], $tmp_line["feed_url"] ];
+ }
+ }
+
+ $this->mark_timestamp(" orig-feed-id");
$line["updated_long"] = TimeHelper::make_local_datetime($line["updated"],true);
$line["updated"] = TimeHelper::make_local_datetime($line["updated"], false, false, false, true);
-
$line['imported'] = T_sprintf("Imported at %s",
- TimeHelper::make_local_datetime($line["date_entered"], false));
+ TimeHelper::make_local_datetime($line["date_entered"], false));
+
+ $this->mark_timestamp(" local-datetime");
if ($line["tag_cache"])
$tags = explode(",", $line["tag_cache"]);
@@ -357,6 +390,8 @@ class Feeds extends Handler_Protected {
$line["tags_str"] = Article::format_tags_string($tags, $id);
+ $this->mark_timestamp(" tags");
+
if (self::feedHasIcon($feed_id)) {
$line['feed_icon'] = "<img class=\"icon\" src=\"".ICONS_URL."/$feed_id.ico\" alt=\"\">";
} else {
@@ -366,6 +401,8 @@ class Feeds extends Handler_Protected {
//setting feed headline background color, needs to change text color based on dark/light
$fav_color = $line['favicon_avg_color'];
+ $this->mark_timestamp(" pre-color");
+
require_once "colors.php";
if (!isset($rgba_cache[$feed_id])) {
@@ -378,17 +415,23 @@ class Feeds extends Handler_Protected {
if (isset($rgba_cache[$feed_id])) {
$line['feed_bg_color'] = 'rgba(' . implode(",", $rgba_cache[$feed_id]) . ',0.3)';
- }
+ }
+
+ $this->mark_timestamp(" color");
/* we don't need those */
- foreach (["date_entered", "guid", "last_published", "last_marked", "tag_cache", "favicon_avg_color",
- "uuid", "label_cache", "yyiw"] as $k)
- unset($line[$k]);
+ foreach (["date_entered", "guid", "last_published", "last_marked", "tag_cache", "favicon_avg_color",
+ "uuid", "label_cache", "yyiw"] as $k)
+ unset($line[$k]);
array_push($reply['content'], $line);
+
+ $this->mark_timestamp("article end");
}
- }
+ }
+
+ $this->mark_timestamp("end of articles");
if (!$headlines_count) {
@@ -450,6 +493,8 @@ class Feeds extends Handler_Protected {
}
}
+ $this->mark_timestamp("end");
+
return array($topmost_article_ids, $headlines_count, $feed, $disable_cache, $reply);
}
@@ -746,8 +791,10 @@ class Feeds extends Handler_Protected {
function update_debugger() {
header("Content-type: text/html");
+ $xdebug = isset($_REQUEST["xdebug"]) ? (int)$_REQUEST["xdebug"] : 1;
+
Debug::set_enabled(true);
- Debug::set_loglevel($_REQUEST["xdebug"]);
+ Debug::set_loglevel($xdebug);
$feed_id = (int)$_REQUEST["feed_id"];
@$do_update = $_REQUEST["action"] == "do_update";
@@ -788,7 +835,7 @@ class Feeds extends Handler_Protected {
</head>
<body class="flat ttrss_utility feed_debugger css_loading">
<script type="text/javascript">
- require(['dojo/parser', "dojo/ready", 'dijit/form/Button','dijit/form/CheckBox', 'dijit/form/Form',
+ require(['dojo/parser', "dojo/ready", 'dijit/form/Button','dijit/form/CheckBox', 'dijit/form/Select', 'dijit/form/Form',
'dijit/form/Select','dijit/form/TextBox','dijit/form/ValidationTextBox'],function(parser, ready){
ready(function() {
parser.parse();
@@ -802,12 +849,19 @@ class Feeds extends Handler_Protected {
<form method="post" action="">
<input type="hidden" name="op" value="feeds">
<input type="hidden" name="method" value="update_debugger">
- <input type="hidden" name="xdebug" value="1">
<input type="hidden" name="csrf_token" value="<?php echo $csrf_token ?>">
<input type="hidden" name="action" value="do_update">
<input type="hidden" name="feed_id" value="<?php echo $feed_id ?>">
- <fieldset class="narrow">
+ <fieldset>
+ <label>
+ <?php print_select_hash("xdebug", $xdebug,
+ [Debug::$LOG_VERBOSE => "LOG_VERBOSE", Debug::$LOG_EXTENDED => "LOG_EXTENDED"],
+ 'dojoType="dijit.form.Select"');
+ ?></label>
+ </fieldset>
+
+ <fieldset>
<label class="checkbox"><input dojoType="dijit.form.CheckBox" type="checkbox" name="force_refetch" value="1" <?php echo $refetch_checked ?>> Force refetch</label>
</fieldset>
@@ -1413,7 +1467,7 @@ class Feeds extends Handler_Protected {
$pdo = Db::pdo();
// WARNING: due to highly dynamic nature of this query its going to quote parameters
- // right before adding them to SQL part
+ // right before adding them to SQL part
$feed = $params["feed"];
$limit = isset($params["limit"]) ? $params["limit"] : 30;
@@ -1647,6 +1701,11 @@ class Feeds extends Handler_Protected {
$vfeed_query_part = $override_vfeed;
}
+ $feed_title = "";
+ $feed_site_url = "";
+ $last_error = "";
+ $last_updated = "";
+
if ($search) {
$feed_title = T_sprintf("Search results: %s", $search);
} else {
@@ -1684,6 +1743,8 @@ class Feeds extends Handler_Protected {
$start_ts_query_part = "";
}
+ $first_id = 0;
+
if (is_numeric($feed)) {
// proper override_order applied above
if ($vfeed_query_part && !$ignore_vfeed_group && get_pref('VFEED_GROUP_BY_FEED', $owner_uid)) {
@@ -1709,11 +1770,11 @@ class Feeds extends Handler_Protected {
} else {
$from_qpart = "${ext_tables_part}ttrss_entries LEFT JOIN ttrss_user_entries ON (ref_id = ttrss_entries.id)
LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
+ $feed_check_qpart = "";
}
if ($vfeed_query_part) $vfeed_query_part .= "favicon_avg_color,";
- $first_id = 0;
$first_id_query_strategy_part = $query_strategy_part;
if ($feed == -3)
@@ -1722,20 +1783,24 @@ class Feeds extends Handler_Protected {
if (DB_TYPE == "pgsql") {
$sanity_interval_qpart = "date_entered >= NOW() - INTERVAL '1 hour' AND";
$yyiw_qpart = "to_char(date_entered, 'IYYY-IW') AS yyiw";
+
+ $distinct_columns = str_replace("desc", "", strtolower($order_by));
+ $distinct_qpart = "DISTINCT ON (id, $distinct_columns)";
} else {
$sanity_interval_qpart = "date_entered >= DATE_SUB(NOW(), INTERVAL 1 hour) AND";
$yyiw_qpart = "date_format(date_entered, '%Y-%u') AS yyiw";
+ $distinct_qpart = "DISTINCT"; //fallback
}
if (!$search && !$skip_first_id_check) {
// if previous topmost article id changed that means our current pagination is no longer valid
- $query = "SELECT DISTINCT
- ttrss_feeds.title,
+ $query = "SELECT
+ ttrss_entries.id,
date_entered,
- $yyiw_qpart,
+ $yyiw_qpart,
guid,
- ttrss_entries.id,
ttrss_entries.title,
+ ttrss_feeds.title,
updated,
score,
marked,
@@ -1754,9 +1819,9 @@ class Feeds extends Handler_Protected {
$sanity_interval_qpart
$first_id_query_strategy_part ORDER BY $order_by LIMIT 1";
- /*if ($_REQUEST["debug"]) {
- print $query;
- }*/
+ if ($_REQUEST["debug"]) {
+ print "\n*** FIRST ID QUERY ***\n$query\n";
+ }
$res = $pdo->query($query);
@@ -1769,11 +1834,12 @@ class Feeds extends Handler_Protected {
}
}
- $query = "SELECT DISTINCT
+ $query = "SELECT $distinct_qpart
+ ttrss_entries.id AS id,
date_entered,
- $yyiw_qpart,
+ $yyiw_qpart,
guid,
- ttrss_entries.id,ttrss_entries.title,
+ ttrss_entries.title,
updated,
label_cache,
tag_cache,
@@ -1805,12 +1871,23 @@ class Feeds extends Handler_Protected {
//if ($_REQUEST["debug"]) print $query;
+ if ($_REQUEST["debug"]) {
+ print "\n*** HEADLINES QUERY ***\n$query\n";
+ }
+
$res = $pdo->query($query);
} else {
// browsing by tag
- $query = "SELECT DISTINCT
+ if (DB_TYPE == "pgsql") {
+ $distinct_columns = str_replace("desc", "", strtolower($order_by));
+ $distinct_qpart = "DISTINCT ON (id, $distinct_columns)";
+ } else {
+ $distinct_qpart = "DISTINCT"; //fallback
+ }
+
+ $query = "SELECT $distinct_qpart
date_entered,
guid,
note,
@@ -1849,7 +1926,11 @@ class Feeds extends Handler_Protected {
$query_strategy_part ORDER BY $order_by
$limit_query_part $offset_query_part";
- if ($_REQUEST["debug"]) print $query;
+ //if ($_REQUEST["debug"]) print $query;
+
+ if ($_REQUEST["debug"]) {
+ print "\n*** TAGS QUERY ***\n$query\n";
+ }
$res = $pdo->query($query);
}
@@ -2023,12 +2104,10 @@ class Feeds extends Handler_Protected {
/**
* Purge a feed old posts.
*
- * @param mixed $link A database connection.
* @param mixed $feed_id The id of the purged feed.
* @param mixed $purge_interval Olderness of purged posts.
- * @param boolean $debug Set to True to enable the debug. False by default.
* @access public
- * @return void
+ * @return mixed
*/
static function purge_feed($feed_id, $purge_interval) {
@@ -2036,63 +2115,68 @@ class Feeds extends Handler_Protected {
$pdo = Db::pdo();
+ $owner_uid = false;
+ $rows_deleted = 0;
+
$sth = $pdo->prepare("SELECT owner_uid FROM ttrss_feeds WHERE id = ?");
$sth->execute([$feed_id]);
- $owner_uid = false;
-
if ($row = $sth->fetch()) {
$owner_uid = $row["owner_uid"];
- }
- if ($purge_interval == -1 || !$purge_interval) {
- return;
- }
+ if (FORCE_ARTICLE_PURGE != 0) {
+ Debug::log("purge_feed: FORCE_ARTICLE_PURGE is set, overriding interval to " . FORCE_ARTICLE_PURGE, Debug::$LOG_VERBOSE);
+ $purge_unread = true;
+ $purge_interval = FORCE_ARTICLE_PURGE;
+ } else {
+ $purge_unread = get_pref("PURGE_UNREAD_ARTICLES", $owner_uid, false);
+ }
- if (!$owner_uid) return;
+ $purge_interval = (int) $purge_interval;
- if (FORCE_ARTICLE_PURGE == 0) {
- $purge_unread = get_pref("PURGE_UNREAD_ARTICLES",
- $owner_uid, false);
- } else {
- $purge_unread = true;
- $purge_interval = FORCE_ARTICLE_PURGE;
- }
+ Debug::log("purge_feed: interval $purge_interval days for feed $feed_id, owner: $owner_uid, purge unread: $purge_unread", Debug::$LOG_VERBOSE);
- if (!$purge_unread)
- $query_limit = " unread = false AND ";
- else
- $query_limit = "";
+ if ($purge_interval <= 0) {
+ Debug::log("purge_feed: purging disabled for this feed, nothing to do.", Debug::$LOG_VERBOSE);
+ return;
+ }
- $purge_interval = (int) $purge_interval;
+ if (!$purge_unread)
+ $query_limit = " unread = false AND ";
+ else
+ $query_limit = "";
- if (DB_TYPE == "pgsql") {
- $sth = $pdo->prepare("DELETE FROM ttrss_user_entries
- USING ttrss_entries
- WHERE ttrss_entries.id = ref_id AND
- marked = false AND
- feed_id = ? AND
- $query_limit
- ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
- $sth->execute([$feed_id]);
+ if (DB_TYPE == "pgsql") {
+ $sth = $pdo->prepare("DELETE FROM ttrss_user_entries
+ USING ttrss_entries
+ WHERE ttrss_entries.id = ref_id AND
+ marked = false AND
+ feed_id = ? AND
+ $query_limit
+ ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
+ $sth->execute([$feed_id]);
- } else {
- $sth = $pdo->prepare("DELETE FROM ttrss_user_entries
- USING ttrss_user_entries, ttrss_entries
- WHERE ttrss_entries.id = ref_id AND
- marked = false AND
- feed_id = ? AND
- $query_limit
- ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)");
- $sth->execute([$feed_id]);
+ } else {
+ $sth = $pdo->prepare("DELETE FROM ttrss_user_entries
+ USING ttrss_user_entries, ttrss_entries
+ WHERE ttrss_entries.id = ref_id AND
+ marked = false AND
+ feed_id = ? AND
+ $query_limit
+ ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)");
+ $sth->execute([$feed_id]);
- }
+ }
+
+ $rows_deleted = $sth->rowCount();
- $rows = $sth->rowCount();
+ Debug::log("purge_feed: deleted $rows_deleted articles.", Debug::$LOG_VERBOSE);
- Debug::log("Purged feed $feed_id ($purge_interval): deleted $rows articles");
+ } else {
+ Debug::log("purge_feed: owner of $feed_id not found", Debug::$LOG_VERBOSE);
+ }
- return $rows;
+ return $rows_deleted;
}
static function feed_purge_interval($feed_id) {
@@ -2107,11 +2191,10 @@ class Feeds extends Handler_Protected {
$purge_interval = $row["purge_interval"];
$owner_uid = $row["owner_uid"];
- if ($purge_interval == 0) $purge_interval = get_pref(
- 'PURGE_OLD_DAYS', $owner_uid);
+ if ($purge_interval == 0)
+ $purge_interval = get_pref('PURGE_OLD_DAYS', $owner_uid, false);
return $purge_interval;
-
} else {
return -1;
}
@@ -2310,5 +2393,25 @@ class Feeds extends Handler_Protected {
return [$query, $skip_first_id];
}
+
+ function mark_timestamp($label) {
+
+ if (!$_REQUEST['timestamps'])
+ return;
+
+
+ if (!$this->viewfeed_timestamp) $this->viewfeed_timestamp = hrtime(true);
+ if (!$this->viewfeed_timestamp_last) $this->viewfeed_timestamp_last = hrtime(true);
+
+ $timestamp = hrtime(true);
+
+ printf("[%4d ms, %4d abs] %s\n",
+ ($timestamp - $this->viewfeed_timestamp_last) / 1e6,
+ ($timestamp - $this->viewfeed_timestamp) / 1e6,
+ $label);
+
+ $this->viewfeed_timestamp_last = $timestamp;
+ }
+
}