diff options
author | Andrew Dolgov <[email protected]> | 2023-10-25 12:55:09 +0300 |
---|---|---|
committer | Andrew Dolgov <[email protected]> | 2023-10-25 12:55:09 +0300 |
commit | 865ecc87963dc3b26e66296616eef2a1cc41ac3f (patch) | |
tree | bf2ecd8a391103bdb2c8b70cd33c47467310754b /classes/article.php | |
parent | 0a5507d3bd79d04c860455664f919bf8e7274fda (diff) |
move to psr-4 autoloader
Diffstat (limited to 'classes/article.php')
-rwxr-xr-x | classes/article.php | 730 |
1 files changed, 0 insertions, 730 deletions
diff --git a/classes/article.php b/classes/article.php deleted file mode 100755 index 0b446479b..000000000 --- a/classes/article.php +++ /dev/null @@ -1,730 +0,0 @@ -<?php -class Article extends Handler_Protected { - const ARTICLE_KIND_ALBUM = 1; - const ARTICLE_KIND_VIDEO = 2; - const ARTICLE_KIND_YOUTUBE = 3; - - const CATCHUP_MODE_MARK_AS_READ = 0; - const CATCHUP_MODE_MARK_AS_UNREAD = 1; - const CATCHUP_MODE_TOGGLE = 2; - - function redirect(): void { - $article = ORM::for_table('ttrss_entries') - ->table_alias('e') - ->join('ttrss_user_entries', [ 'ref_id', '=', 'e.id'], 'ue') - ->where('ue.owner_uid', $_SESSION['uid']) - ->find_one((int)$_REQUEST['id']); - - if ($article) { - $article_url = UrlHelper::validate($article->link); - - if ($article_url) { - header("Location: $article_url"); - return; - } - } - - header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found"); - print "Article not found or has an empty URL."; - } - - static function _create_published_article(string $title, string $url, string $content, string $labels_str, int $owner_uid): bool { - - $guid = 'SHA1:' . sha1("ttshared:" . $url . $owner_uid); // include owner_uid to prevent global GUID clash - - if (!$content) { - $pluginhost = new PluginHost(); - $pluginhost->load_all(PluginHost::KIND_ALL, $owner_uid); - //$pluginhost->load_data(); - - $pluginhost->run_hooks_callback(PluginHost::HOOK_GET_FULL_TEXT, - function ($result) use (&$content) { - if ($result) { - $content = $result; - return true; - } - }, - $url); - } - - $content_hash = sha1($content); - - if ($labels_str != "") { - $labels = explode(",", $labels_str); - } else { - $labels = array(); - } - - $rc = false; - - if (!$title) $title = $url; - if (!$title && !$url) return false; - - if (filter_var($url, FILTER_VALIDATE_URL) === false) return false; - - $pdo = Db::pdo(); - - $pdo->beginTransaction(); - - // only check for our user data here, others might have shared this with different content etc - $sth = $pdo->prepare("SELECT id FROM ttrss_entries, ttrss_user_entries WHERE - guid = ? AND ref_id = id AND owner_uid = ? LIMIT 1"); - $sth->execute([$guid, $owner_uid]); - - if ($row = $sth->fetch()) { - $ref_id = $row['id']; - - $sth = $pdo->prepare("SELECT int_id FROM ttrss_user_entries WHERE - ref_id = ? AND owner_uid = ? LIMIT 1"); - $sth->execute([$ref_id, $owner_uid]); - - if ($row = $sth->fetch()) { - $int_id = $row['int_id']; - - $sth = $pdo->prepare("UPDATE ttrss_entries SET - content = ?, content_hash = ? WHERE id = ?"); - $sth->execute([$content, $content_hash, $ref_id]); - - if (Config::get(Config::DB_TYPE) == "pgsql") { - $sth = $pdo->prepare("UPDATE ttrss_entries - SET tsvector_combined = to_tsvector( :ts_content) - WHERE id = :id"); - $params = [ - ":ts_content" => mb_substr(\Soundasleep\Html2Text::convert($content), 0, 900000), - ":id" => $ref_id]; - $sth->execute($params); - } - - $sth = $pdo->prepare("UPDATE ttrss_user_entries SET published = true, - last_published = NOW() WHERE - int_id = ? AND owner_uid = ?"); - $sth->execute([$int_id, $owner_uid]); - - } else { - - $sth = $pdo->prepare("INSERT INTO ttrss_user_entries - (ref_id, uuid, feed_id, orig_feed_id, owner_uid, published, tag_cache, label_cache, - last_read, note, unread, last_published) - VALUES - (?, '', NULL, NULL, ?, true, '', '', NOW(), '', false, NOW())"); - $sth->execute([$ref_id, $owner_uid]); - } - - if (count($labels) != 0) { - foreach ($labels as $label) { - Labels::add_article($ref_id, trim($label), $owner_uid); - } - } - - $rc = true; - - } else { - $sth = $pdo->prepare("INSERT INTO ttrss_entries - (title, guid, link, updated, content, content_hash, date_entered, date_updated) - VALUES - (?, ?, ?, NOW(), ?, ?, NOW(), NOW())"); - $sth->execute([$title, $guid, $url, $content, $content_hash]); - - $sth = $pdo->prepare("SELECT id FROM ttrss_entries WHERE guid = ?"); - $sth->execute([$guid]); - - if ($row = $sth->fetch()) { - $ref_id = $row["id"]; - if (Config::get(Config::DB_TYPE) == "pgsql"){ - $sth = $pdo->prepare("UPDATE ttrss_entries - SET tsvector_combined = to_tsvector( :ts_content) - WHERE id = :id"); - $params = [ - ":ts_content" => mb_substr(\Soundasleep\Html2Text::convert($content), 0, 900000), - ":id" => $ref_id]; - $sth->execute($params); - } - $sth = $pdo->prepare("INSERT INTO ttrss_user_entries - (ref_id, uuid, feed_id, orig_feed_id, owner_uid, published, tag_cache, label_cache, - last_read, note, unread, last_published) - VALUES - (?, '', NULL, NULL, ?, true, '', '', NOW(), '', false, NOW())"); - $sth->execute([$ref_id, $owner_uid]); - - if (count($labels) != 0) { - foreach ($labels as $label) { - Labels::add_article($ref_id, trim($label), $owner_uid); - } - } - - $rc = true; - } - } - - $pdo->commit(); - - return $rc; - } - - function printArticleTags(): void { - $id = (int) clean($_REQUEST['id'] ?? 0); - - print json_encode(["id" => $id, - "tags" => self::_get_tags($id)]); - } - - function setScore(): void { - $ids = array_map("intval", clean($_REQUEST['ids'] ?? [])); - $score = (int)clean($_REQUEST['score']); - - $ids_qmarks = arr_qmarks($ids); - - $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET - score = ? WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); - - $sth->execute([$score, ...$ids, $_SESSION['uid']]); - - print json_encode(["id" => $ids, "score" => $score]); - } - - function setArticleTags(): void { - - $id = clean($_REQUEST["id"]); - - //$tags_str = clean($_REQUEST["tags_str"]); - //$tags = array_unique(array_map('trim', explode(",", $tags_str))); - - $tags = FeedItem_Common::normalize_categories(explode(",", clean($_REQUEST["tags_str"] ?? ""))); - - $this->pdo->beginTransaction(); - - $sth = $this->pdo->prepare("SELECT int_id FROM ttrss_user_entries WHERE - ref_id = ? AND owner_uid = ? LIMIT 1"); - $sth->execute([$id, $_SESSION['uid']]); - - if ($row = $sth->fetch()) { - - $tags_to_cache = array(); - - $int_id = $row['int_id']; - - $dsth = $this->pdo->prepare("DELETE FROM ttrss_tags WHERE - post_int_id = ? AND owner_uid = ?"); - $dsth->execute([$int_id, $_SESSION['uid']]); - - $csth = $this->pdo->prepare("SELECT post_int_id FROM ttrss_tags - WHERE post_int_id = ? AND owner_uid = ? AND tag_name = ?"); - - $usth = $this->pdo->prepare("INSERT INTO ttrss_tags - (post_int_id, owner_uid, tag_name) - VALUES (?, ?, ?)"); - - foreach ($tags as $tag) { - $csth->execute([$int_id, $_SESSION['uid'], $tag]); - - if (!$csth->fetch()) { - $usth->execute([$int_id, $_SESSION['uid'], $tag]); - } - - array_push($tags_to_cache, $tag); - } - - /* update tag cache */ - - $tags_str = join(",", $tags_to_cache); - - $sth = $this->pdo->prepare("UPDATE ttrss_user_entries - SET tag_cache = ? WHERE ref_id = ? AND owner_uid = ?"); - $sth->execute([$tags_str, $id, $_SESSION['uid']]); - } - - $this->pdo->commit(); - - // get latest tags from the database, original $tags is sometimes JSON-encoded as a hash ({}) - ??? - print json_encode(["id" => (int)$id, "tags" => $this->_get_tags($id)]); - } - - function completeTags(): void { - $search = clean($_REQUEST["search"]); - - $sth = $this->pdo->prepare("SELECT DISTINCT tag_name FROM ttrss_tags - WHERE owner_uid = ? AND - tag_name LIKE ? ORDER BY tag_name - LIMIT 10"); - - $sth->execute([$_SESSION['uid'], "$search%"]); - - $results = []; - - while ($line = $sth->fetch()) { - array_push($results, $line["tag_name"]); - } - - print json_encode($results); - } - - function assigntolabel(): void { - $this->_label_ops(true); - } - - function removefromlabel(): void { - $this->_label_ops(false); - } - - private function _label_ops(bool $assign): void { - $reply = array(); - - $ids = array_map("intval", array_filter(explode(",", clean($_REQUEST["ids"] ?? "")), "strlen")); - $label_id = clean($_REQUEST["lid"]); - - $label = Labels::find_caption($label_id, $_SESSION["uid"]); - - $reply["labels-for"] = []; - - if ($label) { - foreach ($ids as $id) { - if ($assign) - Labels::add_article($id, $label, $_SESSION["uid"]); - else - Labels::remove_article($id, $label, $_SESSION["uid"]); - - array_push($reply["labels-for"], - ["id" => (int)$id, "labels" => $this->_get_labels($id)]); - } - } - - $reply["message"] = "UPDATE_COUNTERS"; - - print json_encode($reply); - } - - /** - * @param int $id article id - * @return array{'formatted': string, 'entries': array<int, array<string, mixed>>} - */ - static function _format_enclosures(int $id, bool $always_display_enclosures, string $article_content, bool $hide_images = false): array { - $span = Tracer::start(__METHOD__); - - $enclosures = self::_get_enclosures($id); - $enclosures_formatted = ""; - - /*foreach ($enclosures as &$enc) { - array_push($enclosures, [ - "type" => $enc["content_type"], - "filename" => basename($enc["content_url"]), - "url" => $enc["content_url"], - "title" => $enc["title"], - "width" => (int) $enc["width"], - "height" => (int) $enc["height"] - ]); - }*/ - - PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_FORMAT_ENCLOSURES, - function ($result) use (&$enclosures_formatted, &$enclosures) { - if (is_array($result)) { - $enclosures_formatted = $result[0]; - $enclosures = $result[1]; - } else { - $enclosures_formatted = $result; - } - }, - $enclosures_formatted, $enclosures, $id, $always_display_enclosures, $article_content, $hide_images); - - if (!empty($enclosures_formatted)) { - $span->end(); - return [ - 'formatted' => $enclosures_formatted, - 'entries' => [] - ]; - } - - $rv = [ - 'formatted' => '', - 'entries' => [] - ]; - - $rv['can_inline'] = isset($_SESSION["uid"]) && - empty($_SESSION["bw_limit"]) && - !get_pref(Prefs::STRIP_IMAGES) && - ($always_display_enclosures || !preg_match("/<img/i", $article_content)); - - $rv['inline_text_only'] = $hide_images && $rv['can_inline']; - - foreach ($enclosures as $enc) { - - // this is highly approximate - $enc["filename"] = basename($enc["content_url"]); - - $rendered_enc = ""; - PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_RENDER_ENCLOSURE, - function ($result) use (&$rendered_enc) { - $rendered_enc = $result; - }, - $enc, $id, $rv); - - if ($rendered_enc) { - $rv['formatted'] .= $rendered_enc; - } else { - PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_ENCLOSURE_ENTRY, - function ($result) use (&$enc) { - $enc = $result; - }, - $enc, $id, $rv); - - array_push($rv['entries'], $enc); - } - } - - $span->end(); - return $rv; - } - - /** - * @return array<int, string> - */ - static function _get_tags(int $id, int $owner_uid = 0, ?string $tag_cache = null): array { - $span = Tracer::start(__METHOD__); - - $a_id = $id; - - if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - - $pdo = Db::pdo(); - - $sth = $pdo->prepare("SELECT DISTINCT tag_name, - owner_uid as owner FROM ttrss_tags - WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE - ref_id = ? AND owner_uid = ? LIMIT 1) ORDER BY tag_name"); - - $tags = array(); - - /* check cache first */ - - if (!$tag_cache) { - $csth = $pdo->prepare("SELECT tag_cache FROM ttrss_user_entries - WHERE ref_id = ? AND owner_uid = ?"); - $csth->execute([$id, $owner_uid]); - - if ($row = $csth->fetch()) { - $tag_cache = $row["tag_cache"]; - } - } - - if ($tag_cache) { - $tags = explode(",", $tag_cache); - } else { - - /* do it the hard way */ - - $sth->execute([$a_id, $owner_uid]); - - while ($tmp_line = $sth->fetch()) { - array_push($tags, $tmp_line["tag_name"]); - } - - /* update the cache */ - - $tags_str = join(",", $tags); - - $sth = $pdo->prepare("UPDATE ttrss_user_entries - SET tag_cache = ? WHERE ref_id = ? - AND owner_uid = ?"); - $sth->execute([$tags_str, $id, $owner_uid]); - } - - $span->end(); - return $tags; - } - - function getmetadatabyid(): void { - $article = ORM::for_table('ttrss_entries') - ->join('ttrss_user_entries', ['ref_id', '=', 'id'], 'ue') - ->where('ue.owner_uid', $_SESSION['uid']) - ->find_one((int)$_REQUEST['id']); - - if ($article) { - echo json_encode(["link" => $article->link, "title" => $article->title]); - } else { - echo json_encode([]); - } - } - - /** - * @return array<int, array<string, mixed>> - */ - static function _get_enclosures(int $id): array { - $encs = ORM::for_table('ttrss_enclosures') - ->where('post_id', $id) - ->find_many(); - - $rv = []; - - $cache = DiskCache::instance("images"); - - foreach ($encs as $enc) { - $cache_key = sha1($enc->content_url); - - if ($cache->exists($cache_key)) { - $enc->content_url = $cache->get_url($cache_key); - } - - array_push($rv, $enc->as_array()); - } - - return $rv; - - } - - static function _purge_orphans(): void { - - // purge orphaned posts in main content table - - if (Config::get(Config::DB_TYPE) == "mysql") - $limit_qpart = "LIMIT 5000"; - else - $limit_qpart = ""; - - $pdo = Db::pdo(); - $res = $pdo->query("DELETE FROM ttrss_entries WHERE - NOT EXISTS (SELECT ref_id FROM ttrss_user_entries WHERE ref_id = id) $limit_qpart"); - - if (Debug::enabled()) { - $rows = $res->rowCount(); - Debug::log("Purged $rows orphaned posts."); - } - } - - /** - * @param array<int, int> $ids - * @param int $cmode Article::CATCHUP_MODE_* - */ - static function _catchup_by_id($ids, int $cmode, ?int $owner_uid = null): void { - - if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - - $pdo = Db::pdo(); - - $ids_qmarks = arr_qmarks($ids); - - if ($cmode == Article::CATCHUP_MODE_MARK_AS_UNREAD) { - $sth = $pdo->prepare("UPDATE ttrss_user_entries SET - unread = true - WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); - } else if ($cmode == Article::CATCHUP_MODE_TOGGLE) { - $sth = $pdo->prepare("UPDATE ttrss_user_entries SET - unread = NOT unread,last_read = NOW() - WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); - } else { - $sth = $pdo->prepare("UPDATE ttrss_user_entries SET - unread = false,last_read = NOW() - WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); - } - - $sth->execute([...$ids, $owner_uid]); - } - - /** - * @return array<int, array<int, int|string>> - */ - static function _get_labels(int $id, ?int $owner_uid = null): array { - $span = Tracer::start(__METHOD__); - - $rv = array(); - - if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - - $pdo = Db::pdo(); - - $sth = $pdo->prepare("SELECT label_cache FROM - ttrss_user_entries WHERE ref_id = ? AND owner_uid = ?"); - $sth->execute([$id, $owner_uid]); - - if ($row = $sth->fetch()) { - $label_cache = $row["label_cache"]; - - if ($label_cache) { - $tmp = json_decode($label_cache, true); - - if (empty($tmp) || ($tmp["no-labels"] ?? 0) == 1) - return $rv; - else - return $tmp; - } - } - - $sth = $pdo->prepare("SELECT DISTINCT label_id,caption,fg_color,bg_color - FROM ttrss_labels2, ttrss_user_labels2 - WHERE id = label_id - AND article_id = ? - AND owner_uid = ? - ORDER BY caption"); - $sth->execute([$id, $owner_uid]); - - while ($line = $sth->fetch()) { - $rk = array(Labels::label_to_feed_id($line["label_id"]), - $line["caption"], $line["fg_color"], - $line["bg_color"]); - array_push($rv, $rk); - } - - if (count($rv) > 0) - // PHPStan has issues with the shape of $rv for some reason (array vs non-empty-array). - // @phpstan-ignore-next-line - Labels::update_cache($owner_uid, $id, $rv); - else - Labels::update_cache($owner_uid, $id, array("no-labels" => 1)); - - $span->end(); - - return $rv; - } - - /** - * @param array<int, array<string, mixed>> $enclosures - * @param array<string, mixed> $headline - * - * @return array<int, Article::ARTICLE_KIND_*|string> - */ - static function _get_image(array $enclosures, string $content, string $site_url, array $headline) { - $span = Tracer::start(__METHOD__); - - $article_image = ""; - $article_stream = ""; - $article_kind = 0; - - PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_ARTICLE_IMAGE, - function ($result, $plugin) use (&$article_image, &$article_stream, &$content) { - list ($article_image, $article_stream, $content) = $result; - - // run until first hard match - return !empty($article_image); - }, - $enclosures, $content, $site_url, $headline); - - if (!$article_image && !$article_stream) { - $tmpdoc = new DOMDocument(); - - if (@$tmpdoc->loadHTML('<?xml encoding="UTF-8">' . mb_substr($content, 0, 131070))) { - $tmpxpath = new DOMXPath($tmpdoc); - $elems = $tmpxpath->query('(//img[@src]|//video[@poster]|//iframe[contains(@src , "youtube.com/embed/")])'); - - foreach ($elems as $e) { - if ($e->nodeName == "iframe") { - $matches = []; - if ($rrr = preg_match("/\/embed\/([\w-]+)/", $e->getAttribute("src"), $matches)) { - $article_image = "https://img.youtube.com/vi/" . $matches[1] . "/hqdefault.jpg"; - $article_stream = "https://youtu.be/" . $matches[1]; - $article_kind = Article::ARTICLE_KIND_YOUTUBE; - break; - } - } else if ($e->nodeName == "video") { - $article_image = $e->getAttribute("poster"); - - /** @var DOMElement|null $src */ - $src = $tmpxpath->query("//source[@src]", $e)->item(0); - - if ($src) { - $article_stream = $src->getAttribute("src"); - $article_kind = Article::ARTICLE_KIND_VIDEO; - } - - break; - } else if ($e->nodeName == 'img') { - if (mb_strpos($e->getAttribute("src"), "data:") !== 0) { - $article_image = $e->getAttribute("src"); - } - break; - } - } - } - - if (!$article_image) - foreach ($enclosures as $enc) { - if (strpos($enc["content_type"], "image/") !== false) { - $article_image = $enc["content_url"]; - break; - } - } - - if ($article_image) { - $article_image = UrlHelper::rewrite_relative($site_url, $article_image); - - if (!$article_kind && (count($enclosures) > 1 || (isset($elems) && $elems->length > 1))) - $article_kind = Article::ARTICLE_KIND_ALBUM; - } - - if ($article_stream) - $article_stream = UrlHelper::rewrite_relative($site_url, $article_stream); - } - - $cache = DiskCache::instance("images"); - - if ($article_image && $cache->exists(sha1($article_image))) - $article_image = $cache->get_url(sha1($article_image)); - - if ($article_stream && $cache->exists(sha1($article_stream))) - $article_stream = $cache->get_url(sha1($article_stream)); - - $span->end(); - - return [$article_image, $article_stream, $article_kind]; - } - - /** - * only cached, returns label ids (not label feed ids) - * - * @param array<int, int> $article_ids - * @return array<int, int> - */ - static function _labels_of(array $article_ids) { - if (count($article_ids) == 0) - return []; - - $span = Tracer::start(__METHOD__); - - $entries = ORM::for_table('ttrss_entries') - ->table_alias('e') - ->join('ttrss_user_entries', ['ref_id', '=', 'id'], 'ue') - ->where_in('id', $article_ids) - ->find_many(); - - $rv = []; - - foreach ($entries as $entry) { - $labels = json_decode($entry->label_cache); - - if (isset($labels) && is_array($labels)) { - foreach ($labels as $label) { - if (empty($label["no-labels"])) - array_push($rv, Labels::feed_to_label_id($label[0])); - } - } - } - - $span->end(); - - return array_unique($rv); - } - - /** - * @param array<int, int> $article_ids - * @return array<int, int> - */ - static function _feeds_of(array $article_ids) { - if (count($article_ids) == 0) - return []; - - $span = Tracer::start(__METHOD__); - - $entries = ORM::for_table('ttrss_entries') - ->table_alias('e') - ->join('ttrss_user_entries', ['ref_id', '=', 'id'], 'ue') - ->where_in('id', $article_ids) - ->find_many(); - - $rv = []; - - foreach ($entries as $entry) { - array_push($rv, $entry->feed_id); - } - - $span->end(); - - return array_unique($rv); - } -} |