diff options
Diffstat (limited to 'classes/feeds.php')
| -rwxr-xr-x | classes/feeds.php | 323 |
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; + } + } |