summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rwxr-xr-xplugins/af_redditimgur/init.php154
-rw-r--r--plugins/af_youtube_embed/init.php22
-rw-r--r--plugins/af_zz_vidmute/init.js22
-rw-r--r--plugins/auth_internal/init.php30
-rw-r--r--plugins/nsfw/init.js18
-rw-r--r--plugins/nsfw/init.php25
-rw-r--r--plugins/share/init.php32
-rw-r--r--plugins/shorten_expanded/init.css3
-rw-r--r--plugins/shorten_expanded/init.js81
-rw-r--r--plugins/shorten_expanded/init.php16
10 files changed, 219 insertions, 184 deletions
diff --git a/plugins/af_redditimgur/init.php b/plugins/af_redditimgur/init.php
index 9fd93dde4..3b4094b1b 100755
--- a/plugins/af_redditimgur/init.php
+++ b/plugins/af_redditimgur/init.php
@@ -7,6 +7,7 @@ class Af_RedditImgur extends Plugin {
private $dump_json_data = false;
private $fallback_preview_urls = [];
private $default_max_score = 100;
+ private $generated_enclosures = [];
function about() {
return array(null,
@@ -132,7 +133,7 @@ class Af_RedditImgur extends Plugin {
if (!empty($media["s"]["u"])) {
$media_url = htmlspecialchars_decode($media["s"]["u"]);
- Debug::log("found media_metadata (gallery): $media_url", Debug::$LOG_VERBOSE);
+ Debug::log("found media_metadata (gallery): $media_url", Debug::LOG_VERBOSE);
if ($media_url) {
$this->handle_as_image($doc, $anchor, $media_url);
@@ -153,7 +154,7 @@ class Af_RedditImgur extends Plugin {
else
$poster_url = "";
- Debug::log("found stream fallback_url: $stream_url / poster $poster_url", Debug::$LOG_VERBOSE);
+ Debug::log("found stream fallback_url: $stream_url / poster $poster_url", Debug::LOG_VERBOSE);
$this->handle_as_video($doc, $anchor, $stream_url, $poster_url);
}
@@ -172,12 +173,12 @@ class Af_RedditImgur extends Plugin {
else
$poster_url = "";
- Debug::log("found hosted video url: $media_url / poster $poster_url, looking up fallback url...", Debug::$LOG_VERBOSE);
+ Debug::log("found hosted video url: $media_url / poster $poster_url, looking up fallback url...", Debug::LOG_VERBOSE);
$fallback_url = $data["media"]["reddit_video"]["fallback_url"];
if ($fallback_url) {
- Debug::log("found video fallback_url: $fallback_url", Debug::$LOG_VERBOSE);
+ Debug::log("found video fallback_url: $fallback_url", Debug::LOG_VERBOSE);
$this->handle_as_video($doc, $anchor, $fallback_url, $poster_url);
$found = 1;
@@ -192,7 +193,7 @@ class Af_RedditImgur extends Plugin {
else
$poster_url = "";
- Debug::log("found video url: $media_url / poster $poster_url", Debug::$LOG_VERBOSE);
+ Debug::log("found video url: $media_url / poster $poster_url", Debug::LOG_VERBOSE);
$this->handle_as_video($doc, $anchor, $media_url, $poster_url);
$found = 1;
@@ -201,7 +202,7 @@ class Af_RedditImgur extends Plugin {
if (!$found && $post_hint == "image") {
$media_url = $data["url"];
- Debug::log("found image url: $media_url", Debug::$LOG_VERBOSE);
+ Debug::log("found image url: $media_url", Debug::LOG_VERBOSE);
$this->handle_as_image($doc, $anchor, $media_url);
$found = 1;
@@ -215,12 +216,12 @@ class Af_RedditImgur extends Plugin {
if ($media_url) {
if ($post_hint == "self") {
- Debug::log("found preview image url: $media_url (link: $target_url)", Debug::$LOG_VERBOSE);
+ Debug::log("found preview image url: $media_url (link: $target_url)", Debug::LOG_VERBOSE);
$this->handle_as_image($doc, $anchor, $media_url, $target_url);
$found = 1;
} else { // gonna use this later if nothing is found using generic link processing
- Debug::log("found fallback preview image url: $media_url (link: $target_url);", Debug::$LOG_VERBOSE);
+ Debug::log("found fallback preview image url: $media_url (link: $target_url);", Debug::LOG_VERBOSE);
array_push($this->fallback_preview_urls, $media_url);
}
}
@@ -244,14 +245,17 @@ class Af_RedditImgur extends Plugin {
$post_is_nsfw = false;
$num_comments = 0;
$score = 0;
+ $link_flairs = [];
$apply_nsfw_tags = FeedItem_Common::normalize_categories($this->host->get_array($this, "apply_nsfw_tags", []));
- // embed before reddit <table> post layout
+ $this->generated_enclosures = [];
+
+ // embed anchor element, before reddit <table> post layout
$anchor = $xpath->query('//body/*')->item(0);
// deal with json-provided media content first
if ($article["link"] && $anchor) {
- Debug::log("JSON: requesting from URL: " . $article["link"] . "/.json", Debug::$LOG_VERBOSE);
+ Debug::log("JSON: requesting from URL: " . $article["link"] . "/.json", Debug::LOG_VERBOSE);
$tmp = UrlHelper::fetch($article["link"] . "/.json");
@@ -262,7 +266,7 @@ class Af_RedditImgur extends Plugin {
$json = json_decode($tmp, true);
if ($json) {
- Debug::log("JSON: processing media elements...", Debug::$LOG_EXTENDED);
+ Debug::log("JSON: processing media elements...", Debug::LOG_EXTENDED);
if ($this->dump_json_data) print_r($json);
@@ -275,13 +279,17 @@ class Af_RedditImgur extends Plugin {
$score += $data['score'] ?? 0;
$num_comments += $data["num_comments"] ?? 0;
+ if (!empty($data["link_flair_text"])) {
+ array_push($link_flairs, $data["link_flair_text"]);
+ }
+
if ($over_18) {
- Debug::log("JSON: post is NSFW", Debug::$LOG_EXTENDED);
+ Debug::log("JSON: post is NSFW", Debug::LOG_EXTENDED);
$post_is_nsfw = true;
}
if (isset($data["crosspost_parent_list"])) {
- Debug::log("JSON: processing child crosspost_parent_list", Debug::$LOG_EXTENDED);
+ Debug::log("JSON: processing child crosspost_parent_list", Debug::LOG_EXTENDED);
foreach ($data["crosspost_parent_list"] as $parent) {
if ($this->process_post_media($parent, $doc, $xpath, $anchor)) {
@@ -292,7 +300,7 @@ class Af_RedditImgur extends Plugin {
}
}
- Debug::log("JSON: processing child data element...", Debug::$LOG_EXTENDED);
+ Debug::log("JSON: processing child data element...", Debug::LOG_EXTENDED);
if (!$found && $this->process_post_media($data, $doc, $xpath, $anchor)) {
$found = 1;
@@ -302,28 +310,32 @@ class Af_RedditImgur extends Plugin {
}
}
} else {
- Debug::log("JSON: failed to parse received data.", Debug::$LOG_EXTENDED);
+ Debug::log("JSON: failed to parse received data.", Debug::LOG_EXTENDED);
}
} else {
if (!$tmp) {
- Debug::log("JSON: failed to fetch post:" . UrlHelper::$fetch_last_error, Debug::$LOG_EXTENDED);
+ Debug::log("JSON: failed to fetch post:" . UrlHelper::$fetch_last_error, Debug::LOG_EXTENDED);
}
}
} else if (!$anchor) {
- Debug::log("JSON: anchor element not found, unable to embed", Debug::$LOG_EXTENDED);
+ Debug::log("JSON: anchor element not found, unable to embed", Debug::LOG_EXTENDED);
}
if ($post_is_nsfw && count($apply_nsfw_tags) > 0) {
$article["tags"] = array_merge($article["tags"], $apply_nsfw_tags);
}
+ if (count($link_flairs) > 0) {
+ $article["tags"] = array_merge($article["tags"], FeedItem_Common::normalize_categories($link_flairs));
+ }
+
$article["num_comments"] = $num_comments;
if ($import_score && $score > 0)
$article["score_modifier"] = ($article["score_modifier"] ?? 0) + ($score > $max_score ? $max_score : $score);
if ($found) {
- Debug::log("JSON: found media data, skipping further processing of content", Debug::$LOG_VERBOSE);
+ Debug::log("JSON: found media data, skipping further processing of content", Debug::LOG_VERBOSE);
$this->remove_post_thumbnail($doc, $xpath);
return true;
}
@@ -337,14 +349,14 @@ class Af_RedditImgur extends Plugin {
/* skip links going back to reddit (and any other blacklisted stuff) */
if (!$found && $this->is_blacklisted($entry_href, ["reddit.com"])) {
- Debug::log("BODY: domain of $entry_href is blacklisted, skipping", Debug::$LOG_EXTENDED);
+ Debug::log("BODY: domain of $entry_href is blacklisted, skipping", Debug::LOG_EXTENDED);
continue;
}
- Debug::log("BODY: processing URL: " . $entry_href, Debug::$LOG_VERBOSE);
+ Debug::log("BODY: processing URL: " . $entry_href, Debug::LOG_VERBOSE);
if (!$found && preg_match("/^https?:\/\/twitter.com\/(.*?)\/status\/(.*)/", $entry_href, $matches)) {
- Debug::log("handling as twitter: " . $matches[1] . " " . $matches[2], Debug::$LOG_VERBOSE);
+ Debug::log("handling as twitter: " . $matches[1] . " " . $matches[2], Debug::LOG_VERBOSE);
$oembed_result = UrlHelper::fetch("https://publish.twitter.com/oembed?url=" . urlencode($entry_href));
@@ -376,7 +388,7 @@ class Af_RedditImgur extends Plugin {
if (!$found && preg_match("/https?:\/\/(www\.)?gfycat.com\/([a-z]+)$/i", $entry_href, $matches)) {
- Debug::log("Handling as Gfycat", Debug::$LOG_VERBOSE);
+ Debug::log("Handling as Gfycat", Debug::LOG_VERBOSE);
$source_stream = 'https://giant.gfycat.com/' . $matches[2] . '.mp4';
$poster_url = 'https://thumbs.gfycat.com/' . $matches[2] . '-mobile.jpg';
@@ -391,14 +403,14 @@ class Af_RedditImgur extends Plugin {
// imgur .gif -> .gifv
if (!$found && preg_match("/i\.imgur\.com\/(.*?)\.gif$/i", $entry_href)) {
- Debug::log("Handling as imgur gif (->gifv)", Debug::$LOG_VERBOSE);
+ Debug::log("Handling as imgur gif (->gifv)", Debug::LOG_VERBOSE);
$entry->setAttribute("href",
str_replace(".gif", ".gifv", $entry_href));
}
if (!$found && preg_match("/\.(gifv|mp4)$/i", $entry_href)) {
- Debug::log("Handling as imgur gifv", Debug::$LOG_VERBOSE);
+ Debug::log("Handling as imgur gifv", Debug::LOG_VERBOSE);
$source_stream = str_replace(".gifv", ".mp4", $entry_href);
@@ -413,28 +425,15 @@ class Af_RedditImgur extends Plugin {
}
$matches = array();
- if (!$found && (preg_match("/youtube\.com\/v\/([\w-]+)/", $entry_href, $matches) ||
- preg_match("/youtube\.com\/.*?[\&\?]v=([\w-]+)/", $entry_href, $matches) ||
- preg_match("/youtube\.com\/embed\/([\w-]+)/", $entry_href, $matches) ||
- preg_match("/youtube\.com\/watch\?v=([\w-]+)/", $entry_href, $matches) ||
- preg_match("/\/\/youtu.be\/([\w-]+)/", $entry_href, $matches))) {
+ if (!$found && $vid_id = UrlHelper::url_to_youtube_vid($entry_href)) {
- $vid_id = $matches[1];
+ Debug::log("Handling as youtube: $vid_id", Debug::LOG_VERBOSE);
- Debug::log("Handling as youtube: $vid_id", Debug::$LOG_VERBOSE);
+ /* normalize video URL for af_youtube_... plugins */
+ $video_url = "https://www.youtube.com/v/$vid_id";
- $iframe = $doc->createElement("iframe");
- $iframe->setAttribute("class", "youtube-player");
- $iframe->setAttribute("type", "text/html");
- $iframe->setAttribute("width", "640");
- $iframe->setAttribute("height", "385");
- $iframe->setAttribute("src", "https://www.youtube.com/embed/$vid_id");
- $iframe->setAttribute("allowfullscreen", "1");
- $iframe->setAttribute("frameborder", "0");
-
- $br = $doc->createElement('br');
- $entry->parentNode->insertBefore($iframe, $entry);
- $entry->parentNode->insertBefore($br, $entry);
+ /* push generated video URL to enclosures so that youtube embed plugins would deal with it later (if enabled) */
+ $this->generated_enclosures[] = [$video_url, "text/html", null, null, '', ''];
$found = true;
}
@@ -443,7 +442,7 @@ class Af_RedditImgur extends Plugin {
/* mb_strpos($entry_href, "i.reddituploads.com") !== false || */
mb_strpos($this->get_content_type($entry_href), "image/") !== false)) {
- Debug::log("Handling as a picture", Debug::$LOG_VERBOSE);
+ Debug::log("Handling as a picture", Debug::LOG_VERBOSE);
$img = $doc->createElement('img');
$img->setAttribute("src", $entry_href);
@@ -458,7 +457,7 @@ class Af_RedditImgur extends Plugin {
// imgur via link rel="image_src" href="..."
if (!$found && preg_match("/imgur/", $entry_href)) {
- Debug::log("handling as imgur page/whatever", Debug::$LOG_VERBOSE);
+ Debug::log("handling as imgur page/whatever", Debug::LOG_VERBOSE);
$content = UrlHelper::fetch(["url" => $entry_href,
"http_accept" => "text/*"]);
@@ -490,7 +489,7 @@ class Af_RedditImgur extends Plugin {
if (!$found && preg_match("/^https?:\/\/gyazo\.com\/([^\.\/]+$)/", $entry_href, $matches)) {
$img_id = $matches[1];
- Debug::log("handling as gyazo: $img_id", Debug::$LOG_VERBOSE);
+ Debug::log("handling as gyazo: $img_id", Debug::LOG_VERBOSE);
$img = $doc->createElement('img');
$img->setAttribute("src", "https://i.gyazo.com/$img_id.jpg");
@@ -504,7 +503,7 @@ class Af_RedditImgur extends Plugin {
// let's try meta properties
if (!$found) {
- Debug::log("looking for meta og:image", Debug::$LOG_VERBOSE);
+ Debug::log("looking for meta og:image", Debug::LOG_VERBOSE);
$content = UrlHelper::fetch(["url" => $entry_href,
"http_accept" => "text/*"]);
@@ -555,7 +554,7 @@ class Af_RedditImgur extends Plugin {
}
if (!$found && $anchor && count($this->fallback_preview_urls) > 0) {
- Debug::log("JSON: processing fallback preview urls...", Debug::$LOG_VERBOSE);
+ Debug::log("JSON: processing fallback preview urls...", Debug::LOG_VERBOSE);
foreach ($this->fallback_preview_urls as $media_url) {
$this->handle_as_image($doc, $anchor, $media_url);
@@ -620,7 +619,7 @@ class Af_RedditImgur extends Plugin {
if ($node && $found) {
$article["content"] = $doc->saveHTML($node);
- $article["enclosures"] = [];
+ $article["enclosures"] = $this->generated_enclosures;
} else if ($content_link) {
$article = $this->readability($article, $content_link->getAttribute("href"), $doc, $xpath);
}
@@ -662,7 +661,7 @@ class Af_RedditImgur extends Plugin {
private function handle_as_video($doc, $entry, $source_stream, $poster_url = false) {
- Debug::log("handle_as_video: $source_stream", Debug::$LOG_VERBOSE);
+ Debug::log("handle_as_video: $source_stream", Debug::LOG_VERBOSE);
$video = $doc->createElement('video');
$video->setAttribute("autoplay", "1");
@@ -694,12 +693,13 @@ class Af_RedditImgur extends Plugin {
function testurl() {
- $url = clean($_POST["url"]);
- $article_url = clean($_POST["article_url"]);
+ $url = clean($_POST["url"] ?? "");
+ $article_url = clean($_POST["article_url"] ?? "");
+ $article_id = clean($_POST["article_id"] ?? "");
$this->dump_json_data = true;
- if (!$url && !$article_url) {
+ if (!$url && !$article_url && !$article_id) {
header("Content-type: text/html");
?>
<style type="text/css">
@@ -711,11 +711,16 @@ class Af_RedditImgur extends Plugin {
<input type="hidden" name="method" value="testurl">
<input type="hidden" name="plugin" value="af_redditimgur">
<fieldset>
- <label>URL:</label>
+ <label>Test URL:</label>
<input name="url" size="100" value="<?= htmlspecialchars($url) ?>"></input>
</fieldset>
+ <hr/>
<fieldset>
- <label>Article URL:</label>
+ <label>Article ID:</label>
+ <input name="article_id" size="10" value="<?= htmlspecialchars($article_id) ?>"></input>
+ </fieldset>
+ <fieldset>
+ <label>or Article URL:</label>
<input name="article_url" size="100" value="<?= htmlspecialchars($article_url) ?>"></input>
</fieldset>
<fieldset>
@@ -729,28 +734,51 @@ class Af_RedditImgur extends Plugin {
header("Content-type: text/plain");
Debug::set_enabled(true);
- Debug::set_loglevel(Debug::$LOG_EXTENDED);
+ Debug::set_loglevel(Debug::LOG_EXTENDED);
+
+ if ($article_id) {
+ $stored_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($article_id);
+
+ if (!$stored_article) {
+ Debug::log("Article not found: $article_id", Debug::LOG_VERBOSE);
+ return;
+ }
+
+ $article = [
+ "link" => $stored_article->link,
+ "content" => $stored_article->content,
+ "tags" => explode(",", $stored_article->tag_cache)
+ ];
- Debug::log("URL: $url", Debug::$LOG_VERBOSE);
+ } else {
+ $article = [
+ "link" => $article_url,
+ "content" => "<html><body><table><tr><td><a href=\"$url\">[link]</a></td></tr></table></body>",
+ "tags" => []];
+ }
$doc = new DOMDocument();
- @$doc->loadHTML("<html><body><table><tr><td><a href=\"$url\">[link]</a></td></tr></table></body>");
+ @$doc->loadHTML($article["content"]);
$xpath = new DOMXPath($doc);
- $article = ["link" => $article_url, "tags" => []];
-
$found = $this->inline_stuff($article, $doc, $xpath);
- Debug::log("Inline result: $found", Debug::$LOG_VERBOSE);
+ Debug::log("Inline result: $found", Debug::LOG_VERBOSE);
+
+ print_r($article);
if (!$found) {
- Debug::log("Readability result:", Debug::$LOG_VERBOSE);
+ Debug::log("Readability result:", Debug::LOG_VERBOSE);
$article = $this->readability([], $url, $doc, $xpath);
print_r($article);
} else {
- Debug::log("Resulting HTML:", Debug::$LOG_VERBOSE);
+ Debug::log("Resulting HTML:", Debug::LOG_VERBOSE);
print $doc->saveHTML();
}
diff --git a/plugins/af_youtube_embed/init.php b/plugins/af_youtube_embed/init.php
index b53a92f0e..72d25a826 100644
--- a/plugins/af_youtube_embed/init.php
+++ b/plugins/af_youtube_embed/init.php
@@ -16,23 +16,23 @@ class Af_Youtube_Embed extends Plugin {
}
function hook_iframe_whitelisted($src) {
- return in_array($src, ["www.youtube.com", "youtube.com", "youtu.be"]);
+ return in_array($src, ["www.youtube.com", "youtube.com",
+ "www.youtube-nocookie.com", "youtube-nocookie.com",
+ "youtu.be"]);
}
function hook_render_enclosure($entry, $hide_images) {
- $matches = array();
+ $url = $entry["content_url"];
- if (preg_match("/\/\/www\.youtube\.com\/v\/([\w-]+)/", $entry["content_url"], $matches) ||
- preg_match("/\/\/www\.youtube\.com\/watch?v=([\w-]+)/", $entry["content_url"], $matches) ||
- preg_match("/\/\/youtu.be\/([\w-]+)/", $entry["content_url"], $matches)) {
+ if ($vid_id = UrlHelper::url_to_youtube_vid($url)) {
- $vid_id = $matches[1];
-
- return "<iframe class=\"youtube-player\"
- type=\"text/html\" width=\"640\" height=\"385\"
- src=\"https://www.youtube.com/embed/$vid_id\"
- allowfullscreen frameborder=\"0\"></iframe>";
+ return "<div class='embed-responsive'>
+ <iframe class='youtube-player'
+ type='text/html' width='640' height='385'
+ src=\"https://www.youtube.com/embed/$vid_id\"
+ allowfullscreen frameborder='0'></iframe>
+ </div>";
}
}
diff --git a/plugins/af_zz_vidmute/init.js b/plugins/af_zz_vidmute/init.js
index b8be8cecd..36914cbf0 100644
--- a/plugins/af_zz_vidmute/init.js
+++ b/plugins/af_zz_vidmute/init.js
@@ -1,24 +1,18 @@
+/* global require, PluginHost */
+
require(['dojo/_base/kernel', 'dojo/ready'], function (dojo, ready) {
+ function mute(row) {
+ [...row.querySelectorAll("video")].forEach((vid) => { vid.muted = true; });
+ }
+
ready(function () {
PluginHost.register(PluginHost.HOOK_ARTICLE_RENDERED_CDM, function (row) {
- if (row) {
-
- row.querySelectorAll("video").forEach(function (v) {
- v.muted = true;
- });
- }
-
+ mute(row);
return true;
});
PluginHost.register(PluginHost.HOOK_ARTICLE_RENDERED, function (row) {
- if (row) {
-
- row.querySelectorAll("video").forEach(function (v) {
- v.muted = true;
- });
- }
-
+ mute(row);
return true;
});
});
diff --git a/plugins/auth_internal/init.php b/plugins/auth_internal/init.php
index 9155f8165..3f5a2e977 100644
--- a/plugins/auth_internal/init.php
+++ b/plugins/auth_internal/init.php
@@ -50,7 +50,7 @@ class Auth_Internal extends Auth_Base {
return false;
} */
- if (UserHelper::check_otp($user_id, $otp))
+ if ($this->check_password($user_id, $password) && UserHelper::check_otp($user_id, $otp))
return $user_id;
else
return false;
@@ -109,7 +109,7 @@ class Auth_Internal extends Auth_Base {
<?= \Controls\hidden_tag("op", "login") ?>
<fieldset>
- <label><?= __("Please enter your one time password:") ?></label>
+ <label><?= __("Please enter verification code (OTP):") ?></label>
<input id="otp" dojoType="dijit.form.ValidationTextBox" required="1" autocomplete="off" size="6" name="otp" value=""/>
<?= \Controls\submit_tag(__("Continue")) ?>
</fieldset>
@@ -150,6 +150,32 @@ class Auth_Internal extends Auth_Base {
if ($user) {
+ // don't throttle app passwords
+ if (!$service && get_schema_version() >= 145) {
+
+ if ($user->last_auth_attempt) {
+ $last_auth_attempt = strtotime($user->last_auth_attempt);
+
+ if ($last_auth_attempt && time() - $last_auth_attempt < Config::get(Config::AUTH_MIN_INTERVAL)) {
+ Logger::log(E_USER_NOTICE, "Too many authentication attempts for {$user->login}, throttled.");
+
+ // start an empty session to deliver login error message
+ if (session_status() != PHP_SESSION_ACTIVE)
+ session_start();
+
+ $_SESSION["login_error_msg"] = __("Too many authentication attempts, throttled.");
+
+ $user->last_auth_attempt = Db::NOW();
+ $user->save();
+
+ return false;
+ }
+ }
+
+ $user->last_auth_attempt = Db::NOW();
+ $user->save();
+ }
+
$salt = $user['salt'] ?? "";
$login = $user['login'];
$pwd_hash = $user['pwd_hash'];
diff --git a/plugins/nsfw/init.js b/plugins/nsfw/init.js
deleted file mode 100644
index 71fe4747b..000000000
--- a/plugins/nsfw/init.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/* global Plugins */
-
-Plugins.NSFW = {
- toggle: function(elem) {
- elem = elem.domNode || elem;
-
- const content = elem.closest(".nsfw-wrapper").querySelector('.nsfw-content');
-
- // we can't use .toggle() here because this script could be invoked by the api client
- // so it's back to vanilla js
-
- if (content.style.display == 'none')
- content.style.display = '';
- else
- content.style.display = 'none';
- }
-}
-
diff --git a/plugins/nsfw/init.php b/plugins/nsfw/init.php
index 8ace94b51..fc38fef71 100644
--- a/plugins/nsfw/init.php
+++ b/plugins/nsfw/init.php
@@ -20,10 +20,6 @@ class NSFW extends Plugin {
}
- function get_js() {
- return file_get_contents(__DIR__ . "/init.js");
- }
-
function hook_article_image($enclosures, $content, $site_url, $article) {
$tags = explode(",", $this->host->get($this, "tags"));
$article_tags = $article["tags"];
@@ -35,27 +31,28 @@ class NSFW extends Plugin {
}
}
- private function rewrite_contents($article, bool $add_api_js = false) {
+ private function rewrite_contents($article) {
$tags = explode(",", $this->host->get($this, "tags"));
$article_tags = $article["tags"];
if (count(array_intersect($tags, $article_tags)) > 0) {
- $article["content"] = "<div class='nsfw-wrapper'>".
- \Controls\button_tag(__("Not work safe (click to toggle)"), '', ['onclick' => 'Plugins.NSFW.toggle(this)']).
- "<div class='nsfw-content' style='display : none'>".$article["content"]."</div>
- </div>";
-
- if ($add_api_js) {
- $article["content"] .= "<script type='text/javascript'>const Plugins = {}; " . $this->get_js() . "</script>";
- }
+ $article["content"] = "<details class='nsfw'><summary>" . __("Not safe for work (click to toggle)") . "</summary>" . $article["content"] . "</details>";
}
return $article;
}
+ function get_css() {
+ return
+ 'details.nsfw {
+ cursor : pointer;
+ user-select : none;
+ }';
+ }
+
function hook_render_article_api($row) {
$article = isset($row['headline']) ? $row['headline'] : $row['article'];
- return $this->rewrite_contents($article, true);
+ return $this->rewrite_contents($article);
}
function hook_render_article($article) {
diff --git a/plugins/share/init.php b/plugins/share/init.php
index 64a9054eb..359d86802 100644
--- a/plugins/share/init.php
+++ b/plugins/share/init.php
@@ -142,13 +142,16 @@ class Share extends Plugin {
$line);
$enclosures = Article::_get_enclosures($line["id"]);
- list ($og_image, $og_stream) = Article::_get_image($enclosures, $line['content'], $line["site_url"], $line);
+ list ($og_image, $og_stream) = Article::_get_image($enclosures, $line['content'], $line["site_url"] ?? "", $line);
$content_decoded = html_entity_decode($line["title"], ENT_NOQUOTES | ENT_HTML401);
$parsed_updated = TimeHelper::make_local_datetime($line["updated"], true, $owner_uid, true);
$line['content'] = DiskCache::rewrite_urls($line['content']);
+ if (!$og_image)
+ $og_image = Config::get_self_url() . "/images/favicon-512px.png";
+
ob_start();
?>
@@ -180,27 +183,28 @@ class Share extends Plugin {
strip_tags($content_decoded)
)
), 500, "...")) ?>">
- </head>
- <?php if ($og_image) { ?>
- <meta property='og:image' content="<?= htmlspecialchars($og_image) ?>">
- <?php } ?>
+ <?php if ($og_image) { ?>
+ <meta property='og:image' content="<?= htmlspecialchars($og_image) ?>">
+ <?php } ?>
+ </head>
<body class='flat ttrss_utility ttrss_zoom css_loading'>
<div class='container'>
- <?php if (!empty($line["link"])) { ?>
- <h1>
- <a target='_blank' rel='noopener noreferrer'
- href="<?= htmlspecialchars($line["link"]) ?>"><?= htmlspecialchars($line["title"]) ?></a>
- </h1>
- <?php } else { ?>
- <h1><?= $line["title"] ?></h1>
- <?php } ?>
-
<div class='content post'>
<div class='header'>
<div class='row'>
+ <?php if (!empty($line["link"])) { ?>
+ <h1>
+ <a rel='noopener noreferrer'
+ href="<?= htmlspecialchars($line["link"]) ?>"><?= htmlspecialchars($line["title"]) ?></a>
+ </h1>
+ <?php } else { ?>
+ <h1><?= $line["title"] ?></h1>
+ <?php } ?>
+ </div>
+ <div class='row'>
<div><?= $line['author'] ?></div>
<div><?= $parsed_updated ?></div>
</div>
diff --git a/plugins/shorten_expanded/init.css b/plugins/shorten_expanded/init.css
index 0966aa1f9..e0a209903 100644
--- a/plugins/shorten_expanded/init.css
+++ b/plugins/shorten_expanded/init.css
@@ -1,7 +1,8 @@
.content-shrink-wrap {
overflow : hidden;
text-overflow: ellipsis;
- height : 800px;
+ height : 80vh;
+ margin-bottom : 8px;
}
.expand-prompt {
diff --git a/plugins/shorten_expanded/init.js b/plugins/shorten_expanded/init.js
index 0abc8c129..85d75d313 100644
--- a/plugins/shorten_expanded/init.js
+++ b/plugins/shorten_expanded/init.js
@@ -1,10 +1,55 @@
-/* global Plugins, __, require, PluginHost */
-
-const _shorten_expanded_threshold = 1.5; //window heights
+/* global Plugins, __, require, PluginHost, App, dojo */
Plugins.Shorten_Expanded = {
+ threshold: 1.5, // of window height
+ observer: new ResizeObserver((entries) => {
+ entries.forEach((entry) => {
+ const row = entry.target;
+
+ Plugins.Shorten_Expanded.shorten_if_needed(row);
+ });
+ }),
+ shorten_if_needed: function(row) {
+
+ const content = row.querySelector(".content");
+ const content_inner = row.querySelector(".content-inner");
+
+ //console.log('shorten_expanded', row.id, content.offsetHeight, 'vs', this.threshold * window.innerHeight);
+
+ if (content && content_inner && !row.hasAttribute('data-already-shortened') && content.offsetHeight >= this.threshold * window.innerHeight) {
+
+ row.setAttribute('data-already-shortened', true);
+
+ const attachments = row.querySelector(".attachments-inline"); // optional
+
+ content_inner.innerHTML = `
+ <div class="content-shrink-wrap">
+ ${content_inner.innerHTML}
+ ${attachments ? attachments.innerHTML : ''}
+ </div>
+ <button dojoType="dijit.form.Button" class="alt-info expand-prompt" onclick="return Plugins.Shorten_Expanded.expand('${row.id}')" href="#">
+ ${App.FormFields.icon('add')}
+ ${__("Expand article")}
+ </button>`;
+
+ if (attachments)
+ attachments.innerHTML = "";
+
+ dojo.parser.parse(content_inner);
+
+ return true;
+ }
+ return false;
+ },
+ process_row: function(row) {
+
+ if (this.shorten_if_needed(row))
+ return;
+
+ this.observer.observe(row);
+ },
expand: function(id) {
- const row = $(id);
+ const row = App.byId(id);
if (row) {
const content = row.querySelector(".content-shrink-wrap");
@@ -21,33 +66,7 @@ Plugins.Shorten_Expanded = {
require(['dojo/_base/kernel', 'dojo/ready'], function (dojo, ready) {
ready(function() {
PluginHost.register(PluginHost.HOOK_ARTICLE_RENDERED_CDM, function(row) {
- window.setTimeout(function() {
- if (row) {
-
- const content = row.querySelector(".content-inner");
-
- //console.log('shorten', row.offsetHeight, 'vs', _shorten_expanded_threshold * window.innerHeight);
-
- if (content && row.offsetHeight >= _shorten_expanded_threshold * window.innerHeight) {
-
- const attachments = row.querySelector(".attachments-inline"); // optional
-
- content.innerHTML = `
- <div class="content-shrink-wrap">
- ${content.innerHTML}
- ${attachments ? 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>`;
-
- if (attachments)
- attachments.innerHTML = "";
-
- dojo.parser.parse(content);
- }
- }
- }, 150);
-
+ Plugins.Shorten_Expanded.process_row(row);
return true;
});
});
diff --git a/plugins/shorten_expanded/init.php b/plugins/shorten_expanded/init.php
index 9673f581b..c097f1a0d 100644
--- a/plugins/shorten_expanded/init.php
+++ b/plugins/shorten_expanded/init.php
@@ -10,22 +10,6 @@ class Shorten_Expanded extends Plugin {
function init($host) {
$this->host = $host;
-
- $host->add_hook($host::HOOK_SANITIZE, $this);
- }
-
- // native lazy loading messes with plugin height calculation because images get loaded
- // after headline is actually rendered (off screen) so we force disable it
- function hook_sanitize($doc) {
- $xpath = new DOMXPath($doc);
-
- $entries = $xpath->query('(//*[@loading="lazy"])');
-
- foreach ($entries as $entry) {
- $entry->removeAttribute("loading");
- }
-
- return $doc;
}
function get_css() {