diff options
author | Andrew Dolgov <[email protected]> | 2021-02-15 14:10:46 +0300 |
---|---|---|
committer | Andrew Dolgov <[email protected]> | 2021-02-15 14:10:46 +0300 |
commit | 82adb01307e108e8a2b4eeb900552160d730d0b7 (patch) | |
tree | 1048a18b3ad79a546e847ca52267249b6ab3b073 | |
parent | 916c21fe60490c50e9ae587b0a977302b9110800 (diff) |
render enclosures on the client
-rwxr-xr-x | classes/api.php | 6 | ||||
-rwxr-xr-x | classes/article.php | 173 | ||||
-rwxr-xr-x | classes/feeds.php | 6 | ||||
-rwxr-xr-x | classes/handler/public.php | 13 | ||||
-rwxr-xr-x | classes/rpc.php | 2 | ||||
-rwxr-xr-x | include/controls.php | 31 | ||||
-rw-r--r-- | js/App.js | 18 | ||||
-rw-r--r-- | js/Article.js | 58 | ||||
-rwxr-xr-x | js/Headlines.js | 2 | ||||
-rw-r--r-- | plugins/shorten_expanded/init.js | 27 |
10 files changed, 136 insertions, 200 deletions
diff --git a/classes/api.php b/classes/api.php index 6debad2f7..2531f0017 100755 --- a/classes/api.php +++ b/classes/api.php @@ -311,8 +311,6 @@ class API extends Handler { while ($line = $sth->fetch()) { - $attachments = Article::get_article_enclosures($line['id']); - $article = array( "id" => $line["id"], "guid" => $line["guid"], @@ -326,7 +324,7 @@ class API extends Handler { "author" => $line["author"], "updated" => (int) strtotime($line["updated"]), "feed_id" => $line["feed_id"], - "attachments" => $attachments, + "attachments" => Article::get_enclosures($line['id']), "score" => (int)$line["score"], "feed_title" => $line["feed_title"], "note" => $line["note"], @@ -736,7 +734,7 @@ class API extends Handler { "tags" => $tags, ); - $enclosures = Article::get_article_enclosures($line['id']); + $enclosures = Article::get_enclosures($line['id']); if ($include_attachments) $headline_row['attachments'] = $enclosures; diff --git a/classes/article.php b/classes/article.php index 5ea936985..00d9d09c5 100755 --- a/classes/article.php +++ b/classes/article.php @@ -352,145 +352,62 @@ class Article extends Handler_Protected { } } - static function format_article_enclosures($id, $always_display_enclosures, - $article_content, $hide_images = false) { - - $result = self::get_article_enclosures($id); - $rv = ''; + static function format_enclosures($id, + $always_display_enclosures, + $article_content, + $hide_images = false) { + + $enclosures = self::get_enclosures($id); + $rv = []; + $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 (&$rv) { + function ($result) use (&$enclosures_formatted, &$enclosures) { if (is_array($result)) { - $rv = $result[0]; - $result = $result[1]; + $enclosures_formatted = $result[0]; + $enclosures = $result[1]; } else { - $rv = $result; + $enclosures_formatted = $result; } }, - $rv, $result, $id, $always_display_enclosures, $article_content, $hide_images); - - if ($rv === '' && !empty($result)) { - $entries_html = array(); - $entries = array(); - $entries_inline = array(); - - foreach ($result as $line) { - - PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_ENCLOSURE_ENTRY, - function($result) use (&$line) { - $line = $result; - }, - $line, $id); - - $url = $line["content_url"]; - $ctype = $line["content_type"]; - $title = $line["title"]; - $width = $line["width"]; - $height = $line["height"]; - - if (!$ctype) $ctype = __("unknown type"); - - //$filename = substr($url, strrpos($url, "/")+1); - $filename = basename($url); - - $player = format_inline_player($url, $ctype); - - if ($player) array_push($entries_inline, $player); - -# $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\" rel=\"noopener noreferrer\">" . -# $filename . " (" . $ctype . ")" . "</a>"; - - $entry = "<div onclick=\"Article.popupOpenUrl('".htmlspecialchars($url)."')\" - dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>"; - - array_push($entries_html, $entry); - - $entry = array(); - - $entry["type"] = $ctype; - $entry["filename"] = $filename; - $entry["url"] = $url; - $entry["title"] = $title; - $entry["width"] = $width; - $entry["height"] = $height; + $enclosures_formatted, $enclosures, $id, $always_display_enclosures, $article_content, $hide_images); - array_push($entries, $entry); - } - - if ($_SESSION['uid'] && !get_pref("STRIP_IMAGES") && !$_SESSION["bw_limit"]) { - if ($always_display_enclosures || - !preg_match("/<img/i", $article_content)) { - - foreach ($entries as $entry) { - - $retval = null; - - PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_RENDER_ENCLOSURE, - function($result) use (&$retval) { - $retval = $result; - }, - $entry, $hide_images); - - if (!empty($retval)) { - $rv .= $retval; - } else { - - if (preg_match("/image/", $entry["type"])) { - - if (!$hide_images) { - $encsize = ''; - if ($entry['height'] > 0) - $encsize .= ' height="' . intval($entry['height']) . '"'; - if ($entry['width'] > 0) - $encsize .= ' width="' . intval($entry['width']) . '"'; - $rv .= "<p><img loading=\"lazy\" - alt=\"".htmlspecialchars($entry["filename"])."\" - src=\"" .htmlspecialchars($entry["url"]) . "\" - " . $encsize . " /></p>"; - } else { - $rv .= "<p><a target=\"_blank\" rel=\"noopener noreferrer\" - href=\"".htmlspecialchars($entry["url"])."\" - >" .htmlspecialchars($entry["url"]) . "</a></p>"; - } - - if ($entry['title']) { - $rv.= "<div class=\"enclosure_title\">${entry['title']}</div>"; - } - } - } - } - } - } - - if (count($entries_inline) > 0) { - //$rv .= "<hr clear='both'/>"; - foreach ($entries_inline as $entry) { $rv .= $entry; }; - $rv .= "<br clear='both'/>"; - } + if (!empty($enclosures_formatted)) { + $rv['formatted'] = $enclosures_formatted; + return $rv; + } - $rv .= "<div class=\"attachments\" dojoType=\"fox.form.DropDownButton\">". - "<span>" . __('Attachments')."</span>"; + $rv['can_inline'] = isset($_SESSION["uid"]) && + empty($_SESSION["bw_limit"]) && + !get_pref("STRIP_IMAGES") && + ($always_display_enclosures || !preg_match("/<img/i", $article_content)); - $rv .= "<div dojoType=\"dijit.Menu\" style=\"display: none;\">"; + $rv['inline_text_only'] = $hide_images && $rv['can_inline']; - foreach ($entries as $entry) { - if ($entry["title"]) - $title = " — " . truncate_string($entry["title"], 30); - else - $title = ""; + $rv['entries'] = []; - if ($entry["filename"]) - $filename = truncate_middle(htmlspecialchars($entry["filename"]), 60); - else - $filename = ""; + foreach ($enclosures as $enc) { - $rv .= "<div onclick='Article.popupOpenUrl(\"".htmlspecialchars($entry["url"])."\")' - dojoType=\"dijit.MenuItem\">".$filename . $title."</div>"; + // this is highly approximate + $enc["filename"] = basename($enc["content_url"]); - }; - - $rv .= "</div>"; - $rv .= "</div>"; + PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_ENCLOSURE_ENTRY, + function ($result) use (&$enc) { + $enc = $result; + }, + $enc, $id); + array_push($rv['entries'], $enc); } return $rv; @@ -613,7 +530,7 @@ class Article extends Handler_Protected { } } - static function get_article_enclosures($id) { + static function get_enclosures($id) { $pdo = Db::pdo(); @@ -625,7 +542,7 @@ class Article extends Handler_Protected { $cache = new DiskCache("images"); - while ($line = $sth->fetch()) { + while ($line = $sth->fetch(PDO::FETCH_ASSOC)) { if ($cache->exists(sha1($line["content_url"]))) { $line["content_url"] = $cache->getUrl(sha1($line["content_url"])); diff --git a/classes/feeds.php b/classes/feeds.php index ba320b52f..d49fe7698 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -283,8 +283,10 @@ class Feeds extends Handler_Protected { $this->mark_timestamp(" pre-enclosures"); - $line["enclosures"] = Article::format_article_enclosures($id, $line["always_display_enclosures"], - $line["content"], $line["hide_images"]); + $line["enclosures"] = Article::format_enclosures($id, + $line["always_display_enclosures"], + $line["content"], + $line["hide_images"]); $this->mark_timestamp(" enclosures"); diff --git a/classes/handler/public.php b/classes/handler/public.php index 79f3a9e6c..0613e9a28 100755 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -131,7 +131,7 @@ class Handler_Public extends Handler { $tpl->addBlock('category'); } - $enclosures = Article::get_article_enclosures($line["id"]); + $enclosures = Article::get_enclosures($line["id"]); if (count($enclosures) > 0) { foreach ($enclosures as $e) { @@ -218,7 +218,7 @@ class Handler_Public extends Handler { } } - $enclosures = Article::get_article_enclosures($line["id"]); + $enclosures = Article::get_enclosures($line["id"]); if (count($enclosures) > 0) { $article['enclosures'] = array(); @@ -356,9 +356,7 @@ class Handler_Public extends Handler { $line['content'] = DiskCache::rewriteUrls($line['content']); - $enclosures = Article::get_article_enclosures($line["id"]); - - header("Content-Type: text/html"); + header("Content-Type: text/html"); $rv .= "<!DOCTYPE html> <html><head> @@ -392,6 +390,7 @@ class Handler_Public extends Handler { $rv .= "</head>"; + $enclosures = Article::get_enclosures($line["id"]); list ($og_image, $og_stream) = Article::get_article_image($enclosures, $line['content'], $line["site_url"]); if ($og_image) { @@ -436,10 +435,10 @@ class Handler_Public extends Handler { $rv .= $line["content"]; - $rv .= Article::format_article_enclosures($id, + /* $rv .= Article::format_article_enclosures($id, $line["always_display_enclosures"], $line["content"], - $line["hide_images"]); + $line["hide_images"]); */ $rv .= "</div>"; # content diff --git a/classes/rpc.php b/classes/rpc.php index f6b57775d..e0753a08e 100755 --- a/classes/rpc.php +++ b/classes/rpc.php @@ -192,9 +192,7 @@ class RPC extends Handler_Protected { } function sanityCheck() { - $_SESSION["hasAudio"] = clean($_REQUEST["hasAudio"]) === "true"; $_SESSION["hasSandbox"] = clean($_REQUEST["hasSandbox"]) === "true"; - $_SESSION["hasMp3"] = clean($_REQUEST["hasMp3"]) === "true"; $_SESSION["clientTzOffset"] = clean($_REQUEST["clientTzOffset"]); $reply = array(); diff --git a/include/controls.php b/include/controls.php index e6678db9a..f706931db 100755 --- a/include/controls.php +++ b/include/controls.php @@ -298,37 +298,6 @@ function print_error($msg) { return print format_error($msg); } -function format_inline_player($url, $ctype) { - - $entry = ""; - - $url = htmlspecialchars($url); - - if (strpos($ctype, "audio/") === 0) { - - $entry .= "<div class='inline-player'>"; - - if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false || - $_SESSION["hasMp3"])) { - - $entry .= "<audio preload=\"none\" controls> - <source type=\"$ctype\" src=\"$url\"/> - </audio> "; - - } - - if ($entry) $entry .= "<a target=\"_blank\" rel=\"noopener noreferrer\" - href=\"$url\">" . basename($url) . "</a>"; - - $entry .= "</div>"; - - return $entry; - - } - - return ""; -} - function print_label_select($name, $value, $attributes = "") { $pdo = Db::pdo(); @@ -586,6 +586,10 @@ const App = { isPrefs() { return this.is_prefs; }, + audioCanPlay: function(ctype) { + const a = document.createElement('audio'); + return a.canPlayType(ctype); + }, init: function(parser, is_prefs) { this.is_prefs = is_prefs; window.onerror = this.Error.onWindowError; @@ -604,17 +608,11 @@ const App = { this.initHotkeyActions(); this.enableCsrfSupport(); - const a = document.createElement('audio'); - const hasAudio = !!a.canPlayType; - const hasSandbox = "sandbox" in document.createElement("iframe"); - const hasMp3 = !!(a.canPlayType && a.canPlayType('audio/mpeg;').replace(/no/, '')); - const clientTzOffset = new Date().getTimezoneOffset() * 60; - const params = { - op: "rpc", method: "sanityCheck", hasAudio: hasAudio, - hasMp3: hasMp3, - clientTzOffset: clientTzOffset, - hasSandbox: hasSandbox + op: "rpc", + method: "sanityCheck", + clientTzOffset: new Date().getTimezoneOffset() * 60, + hasSandbox: "sandbox" in document.createElement("iframe") }; xhrPost("backend.php", params, (transport) => { diff --git a/js/Article.js b/js/Article.js index f8b0415b9..efe33bc99 100644 --- a/js/Article.js +++ b/js/Article.js @@ -140,6 +140,62 @@ const Article = { Headlines.toggleUnread(id, 0); }, + renderEnclosures: function (enclosures) { + + // enclosure list was handled by backend (HOOK_FORMAT_ENCLOSURES) + if (enclosures.formatted) + return enclosures.formatted; + + return ` + ${enclosures.can_inline ? + `<div class='attachments-inline'> + ${enclosures.entries.map((enc) => { + if (!enclosures.inline_text_only) { + if (enc.content_type && enc.content_type.indexOf("image/") != -1) { + return `<p> + <img loading="lazy" + width="${enc.width ? enc.width : ''}" + height="${enc.height ? enc.height : ''}" + src="${App.escapeHtml(enc.content_url)}" + title="${App.escapeHtml(enc.title ? enc.title : enc.content_url)}"/> + </p>` + } else if (enc.content_type && enc.content_type.indexOf("audio/") != -1 && App.audioCanPlay(enc.content_type)) { + return `<p class='inline-player' title="${App.escapeHtml(enc.content_url)}"> + <audio preload="none" controls="controls"> + <source type="${App.escapeHtml(enc.content_type)}" src="${App.escapeHtml(enc.content_url)}"/> + </audio> + </p> + `; + } else { + return `<p> + <a target="_blank" href="${App.escapeHtml(enc.content_url)}" + title="${App.escapeHtml(enc.title ? enc.title : enc.content_url)}" + rel="noopener noreferrer">${App.escapeHtml(enc.content_url)}</a> + </p>` + } + } else { + return `<p> + <a target="_blank" href="${App.escapeHtml(enc.content_url)}" + title="${App.escapeHtml(enc.title ? enc.title : enc.content_url)}" + rel="noopener noreferrer">${App.escapeHtml(enc.content_url)}</a> + </p>` + } + }).join("")} + </div>` : ''} + ${enclosures.entries.length > 0 ? + `<div class="attachments" dojoType="fox.form.DropDownButton"> + <span>${__('Attachments')}</span> + <div dojoType="dijit.Menu" style="display: none"> + ${enclosures.entries.map((enc) => ` + <div onclick='Article.popupOpenUrl("${App.escapeHtml(enc.content_url)}")' + title="${App.escapeHtml(enc.title ? enc.title : enc.content_url)}" dojoType="dijit.MenuItem"> + ${enc.title ? enc.title : enc.filename} + </div> + `).join("")} + </div> + </div>` : ''} + ` + }, render: function (article) { App.cleanupMemory("content-insert"); @@ -241,7 +297,7 @@ const Article = { <div id="POSTNOTE-${hl.id}">${hl.note}</div> <div class="content" lang="${hl.lang ? hl.lang : 'en'}"> ${hl.content} - ${hl.enclosures} + ${Article.renderEnclosures(hl.enclosures)} </div> </div>`; diff --git a/js/Headlines.js b/js/Headlines.js index 94541b974..dadf79c1a 100755 --- a/js/Headlines.js +++ b/js/Headlines.js @@ -488,7 +488,7 @@ const Headlines = { <img src="${App.getInitParam('icon_indicator_white')}"> </div> <div class="intermediate"> - ${hl.enclosures} + ${Article.renderEnclosures(hl.enclosures)} </div> <div class="footer" onclick="event.stopPropagation()"> diff --git a/plugins/shorten_expanded/init.js b/plugins/shorten_expanded/init.js index 30bfac6ba..181e426a4 100644 --- a/plugins/shorten_expanded/init.js +++ b/plugins/shorten_expanded/init.js @@ -1,3 +1,5 @@ +/* global Plugins, __, require, PluginHost */ + const _shorten_expanded_threshold = 1.5; //window heights Plugins.Shorten_Expanded = { @@ -22,26 +24,23 @@ require(['dojo/_base/kernel', 'dojo/ready'], function (dojo, ready) { window.setTimeout(function() { if (row) { - const c_inner = row.select(".content-inner")[0]; - const c_inter = row.select(".intermediate")[0]; + const content = row.querySelector(".content-inner"); + const attachments = row.querySelector(".attachments-inline"); - if (c_inner && c_inter && + if (content && attachments && row.offsetHeight >= _shorten_expanded_threshold * window.innerHeight) { - let tmp = document.createElement("div"); - - c_inter.select("> *:not([class*='attachments'])").each(function(p) { - p.parentNode.removeChild(p); - tmp.appendChild(p); - }); - - c_inner.innerHTML = `<div class="content-shrink-wrap"> - ${c_inner.innerHTML} - ${tmp.innerHTML}</div> + content.innerHTML = ` + <div class="content-shrink-wrap"> + ${content.innerHTML} + ${attachments.innerHTML} + </div> <button dojoType="dijit.form.Button" class="alt-info expand-prompt" onclick="return Plugins.Shorten_Expanded.expand('${row.id}')" href="#"> ${__("Click to expand article")}</button>`; - dojo.parser.parse(c_inner); + attachments.innerHTML = ""; + + dojo.parser.parse(content); } } }, 150); |