diff options
-rwxr-xr-x | classes/handler/public.php | 162 | ||||
-rwxr-xr-x | classes/pluginhost.php | 5 | ||||
-rw-r--r-- | plugins/share/init.php | 169 | ||||
-rw-r--r-- | public.php | 11 |
4 files changed, 180 insertions, 167 deletions
diff --git a/classes/handler/public.php b/classes/handler/public.php index b0bed5d1c..b810019c1 100755 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -294,168 +294,6 @@ class Handler_Public extends Handler { } } - function share() { - $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"]; - - print $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]); - - $rv = ''; - - 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); - - $line['content'] = DiskCache::rewrite_urls($line['content']); - - header("Content-Type: text/html"); - - $rv .= "<!DOCTYPE html> - <html><head> - <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/> - <title>".$line["title"]."</title>". - javascript_tag("lib/prototype.js"). - javascript_tag("js/utility.js")." - <style type='text/css'> - @media (prefers-color-scheme: dark) { - body { - background : #222; - } - } - body.css_loading * { - display : none; - } - </style> - <link rel='shortcut icon' type='image/png' href='images/favicon.png'> - <link rel='icon' type='image/png' sizes='72x72' href='images/favicon-72px.png'>"; - - $rv .= "<meta property='og:title' content=\"".htmlspecialchars(html_entity_decode($line["title"], ENT_NOQUOTES | ENT_HTML401))."\"/>\n"; - $rv .= "<meta property='og:description' content=\"". - htmlspecialchars( - truncate_string( - preg_replace("/[\r\n\t]/", "", - preg_replace("/ {1,}/", " ", - strip_tags(html_entity_decode($line["content"], ENT_NOQUOTES | ENT_HTML401)) - ) - ), 500, "...") - )."\"/>\n"; - - $rv .= "</head>"; - - $enclosures = Article::_get_enclosures($line["id"]); - list ($og_image, $og_stream) = Article::_get_image($enclosures, $line['content'], $line["site_url"]); - - if ($og_image) { - $rv .= "<meta property='og:image' content=\"" . htmlspecialchars($og_image) . "\"/>"; - } - - $rv .= "<body class='flat ttrss_utility ttrss_zoom css_loading'>"; - $rv .= "<div class='container'>"; - - if ($line["link"]) { - $rv .= "<h1><a target='_blank' rel='noopener noreferrer' - title=\"".htmlspecialchars($line['title'])."\" - href=\"" .htmlspecialchars($line["link"]) . "\">" . $line["title"] . "</a></h1>"; - } else { - $rv .= "<h1>" . $line["title"] . "</h1>"; - } - - $rv .= "<div class='content post'>"; - - /* header */ - - $rv .= "<div class='header'>"; - $rv .= "<div class='row'>"; # row - - //$entry_author = $line["author"] ? " - " . $line["author"] : ""; - $parsed_updated = TimeHelper::make_local_datetime($line["updated"], true, - $owner_uid, true); - - $rv .= "<div>".$line['author']."</div>"; - $rv .= "<div>$parsed_updated</div>"; - - $rv .= "</div>"; # row - - $rv .= "</div>"; # header - - /* content */ - - $lang = $line['lang'] ? $line['lang'] : "en"; - $rv .= "<div class='content' lang='$lang'>"; - - /* content body */ - - $rv .= $line["content"]; - - /* $rv .= Article::format_article_enclosures($id, - $line["always_display_enclosures"], - $line["content"], - $line["hide_images"]); */ - - $rv .= "</div>"; # content - - $rv .= "</div>"; # post - - } - - PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_FORMAT_ARTICLE, - function ($result) use (&$rv) { - $rv = $result; - }, - $rv, $line); - - return $rv; - - } - function rss() { $feed = clean($_REQUEST["id"]); $key = clean($_REQUEST["key"]); diff --git a/classes/pluginhost.php b/classes/pluginhost.php index 6f223ee11..097bf987c 100755 --- a/classes/pluginhost.php +++ b/classes/pluginhost.php @@ -18,6 +18,7 @@ class PluginHost { private static $instance; const API_VERSION = 2; + const PUBLIC_METHOD_DELIMITER = "--"; // Hooks marked with *1 are run in global context and available // to plugins loaded in config.php only @@ -617,9 +618,7 @@ class PluginHost { http_build_query( array_merge( [ - "op" => "pluginhandler", - "plugin" => strtolower(get_class($sender)), - "pmethod" => $method + "op" => strtolower(get_class($sender) . PluginHost::PUBLIC_METHOD_DELIMITER . $method), ], $params)); } else { diff --git a/plugins/share/init.php b/plugins/share/init.php index a569393fe..6b7b81a2d 100644 --- a/plugins/share/init.php +++ b/plugins/share/init.php @@ -16,6 +16,10 @@ 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(__DIR__ . "/share.js"); } @@ -78,6 +82,168 @@ class Share extends Plugin { title='".__('Share by URL')."'>link</i>"; } + 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"]; + + print $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]); + + $rv = ''; + + 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); + + $line['content'] = DiskCache::rewrite_urls($line['content']); + + header("Content-Type: text/html"); + + $rv .= "<!DOCTYPE html> + <html><head> + <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/> + <title>".$line["title"]."</title>". + javascript_tag("lib/prototype.js"). + javascript_tag("js/utility.js")." + <style type='text/css'> + @media (prefers-color-scheme: dark) { + body { + background : #222; + } + } + body.css_loading * { + display : none; + } + </style> + <link rel='shortcut icon' type='image/png' href='images/favicon.png'> + <link rel='icon' type='image/png' sizes='72x72' href='images/favicon-72px.png'>"; + + $rv .= "<meta property='og:title' content=\"".htmlspecialchars(html_entity_decode($line["title"], ENT_NOQUOTES | ENT_HTML401))."\"/>\n"; + $rv .= "<meta property='og:description' content=\"". + htmlspecialchars( + truncate_string( + preg_replace("/[\r\n\t]/", "", + preg_replace("/ {1,}/", " ", + strip_tags(html_entity_decode($line["content"], ENT_NOQUOTES | ENT_HTML401)) + ) + ), 500, "...") + )."\"/>\n"; + + $rv .= "</head>"; + + $enclosures = Article::_get_enclosures($line["id"]); + list ($og_image, $og_stream) = Article::_get_image($enclosures, $line['content'], $line["site_url"]); + + if ($og_image) { + $rv .= "<meta property='og:image' content=\"" . htmlspecialchars($og_image) . "\"/>"; + } + + $rv .= "<body class='flat ttrss_utility ttrss_zoom css_loading'>"; + $rv .= "<div class='container'>"; + + if ($line["link"]) { + $rv .= "<h1><a target='_blank' rel='noopener noreferrer' + title=\"".htmlspecialchars($line['title'])."\" + href=\"" .htmlspecialchars($line["link"]) . "\">" . $line["title"] . "</a></h1>"; + } else { + $rv .= "<h1>" . $line["title"] . "</h1>"; + } + + $rv .= "<div class='content post'>"; + + /* header */ + + $rv .= "<div class='header'>"; + $rv .= "<div class='row'>"; # row + + //$entry_author = $line["author"] ? " - " . $line["author"] : ""; + $parsed_updated = TimeHelper::make_local_datetime($line["updated"], true, + $owner_uid, true); + + $rv .= "<div>".$line['author']."</div>"; + $rv .= "<div>$parsed_updated</div>"; + + $rv .= "</div>"; # row + + $rv .= "</div>"; # header + + /* content */ + + $lang = $line['lang'] ? $line['lang'] : "en"; + $rv .= "<div class='content' lang='$lang'>"; + + /* content body */ + + $rv .= $line["content"]; + + /* $rv .= Article::format_article_enclosures($id, + $line["always_display_enclosures"], + $line["content"], + $line["hide_images"]); */ + + $rv .= "</div>"; # content + + $rv .= "</div>"; # post + + } + + PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_FORMAT_ARTICLE, + function ($result) use (&$rv) { + $rv = $result; + }, + $rv, $line); + + return $rv; + + } + function shareDialog() { $id = (int)clean($_REQUEST['id'] ?? 0); @@ -96,8 +262,7 @@ class Share extends Plugin { $sth->execute([$uuid, $id, $_SESSION['uid']]); } - $url_path = get_self_url_prefix() . "/public.php?op=share&key=$uuid"; - + $url_path = $this->host->get_public_method_url($this, "get", ["key" => $uuid]); ?> <header><?= __("You can share this article by the following unique URL:") ?></header> diff --git a/public.php b/public.php index 59b5a499c..fadb2f14d 100644 --- a/public.php +++ b/public.php @@ -17,6 +17,17 @@ $method = (string)clean($_REQUEST["op"]); + // shortcut syntax for public (exposed) methods (?op=plugin--pmethod&...params) + if (strpos($method, PluginHost::PUBLIC_METHOD_DELIMITER) !== false) { + list ($plugin, $pmethod) = explode(PluginHost::PUBLIC_METHOD_DELIMITER, $method, 2); + + // TODO: better implementation that won't modify $_REQUEST + $_REQUEST["plugin"] = $plugin; + $_REQUEST["pmethod"] = $pmethod; + + $method = "pluginhandler"; + } + $override = PluginHost::getInstance()->lookup_handler("public", $method); if ($override) { |