summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/af_comics/filters/af_comics_gocomics.php2
-rw-r--r--plugins/af_comics/filters/af_comics_gocomics_farside.php2
-rwxr-xr-xplugins/af_comics/init.php26
-rw-r--r--plugins/af_fsckportal/init.php5
-rw-r--r--plugins/af_proxy_http/init.php73
-rw-r--r--plugins/af_psql_trgm/init.js17
-rw-r--r--plugins/af_psql_trgm/init.php210
-rw-r--r--plugins/af_readability/init.js8
-rwxr-xr-xplugins/af_readability/init.php166
-rwxr-xr-xplugins/af_redditimgur/init.php121
-rwxr-xr-xplugins/af_tumblr_1280/init.php91
-rwxr-xr-xplugins/af_unburn/init.php8
-rw-r--r--plugins/af_youtube_embed/init.php6
-rw-r--r--plugins/af_zz_vidmute/init.js4
-rw-r--r--plugins/auth_internal/init.php20
-rw-r--r--plugins/auth_remote/init.php2
-rwxr-xr-xplugins/auto_assign_labels/init.php1
-rw-r--r--plugins/bookmarklets/init.php384
-rwxr-xr-xplugins/cache_starred_images/init.php71
-rw-r--r--plugins/close_button/init.php2
-rw-r--r--plugins/mail/init.php165
-rw-r--r--plugins/mail/mail.js21
-rw-r--r--plugins/mail/mail.pngbin641 -> 0 bytes
-rw-r--r--plugins/mailto/init.js19
-rw-r--r--plugins/mailto/init.php40
-rw-r--r--plugins/mailto/mail.pngbin821 -> 0 bytes
-rw-r--r--plugins/note/init.php42
-rw-r--r--plugins/note/note.js31
-rw-r--r--plugins/nsfw/init.js13
-rw-r--r--plugins/nsfw/init.php80
-rw-r--r--plugins/share/init.php228
-rw-r--r--plugins/share/share.css2
-rw-r--r--plugins/share/share.js59
-rw-r--r--plugins/share/share_prefs.js8
-rw-r--r--plugins/shorten_expanded/init.js32
-rw-r--r--plugins/toggle_sidebar/init.php2
-rw-r--r--plugins/vf_shared/init.php2
37 files changed, 1162 insertions, 801 deletions
diff --git a/plugins/af_comics/filters/af_comics_gocomics.php b/plugins/af_comics/filters/af_comics_gocomics.php
index 949b7a235..6c5c7c0d3 100644
--- a/plugins/af_comics/filters/af_comics_gocomics.php
+++ b/plugins/af_comics/filters/af_comics_gocomics.php
@@ -11,7 +11,7 @@ class Af_Comics_Gocomics extends Af_ComicFilter {
public function on_subscribe($url) {
if (preg_match('#^https?://www\.gocomics\.com/([-a-z0-9]+)$#i', $url))
- return '<?xml version="1.0" encoding="utf-8"?>'; // Get is_html() to return false.
+ return '<?xml version="1.0" encoding="utf-8"?>'; // Get _is_html() to return false.
else
return false;
}
diff --git a/plugins/af_comics/filters/af_comics_gocomics_farside.php b/plugins/af_comics/filters/af_comics_gocomics_farside.php
index 9663da8f9..89322209d 100644
--- a/plugins/af_comics/filters/af_comics_gocomics_farside.php
+++ b/plugins/af_comics/filters/af_comics_gocomics_farside.php
@@ -11,7 +11,7 @@ class Af_Comics_Gocomics_FarSide extends Af_ComicFilter {
public function on_subscribe($url) {
if (preg_match("#^https?://www\.thefarside\.com#", $url))
- return '<?xml version="1.0" encoding="utf-8"?>'; // Get is_html() to return false.
+ return '<?xml version="1.0" encoding="utf-8"?>'; // Get _is_html() to return false.
else
return false;
}
diff --git a/plugins/af_comics/init.php b/plugins/af_comics/init.php
index c99d4b1d8..c8a8f8637 100755
--- a/plugins/af_comics/init.php
+++ b/plugins/af_comics/init.php
@@ -47,11 +47,6 @@ class Af_Comics extends Plugin {
function hook_prefs_tab($args) {
if ($args != "prefFeeds") return;
- print "<div dojoType=\"dijit.layout.AccordionPane\"
- title=\"<i class='material-icons'>photo</i> ".__('Feeds supported by af_comics')."\">";
-
- print "<p>" . __("The following comics are currently supported:") . "</p>";
-
$comics = [];
foreach ($this->filters as $f) {
@@ -62,17 +57,22 @@ class Af_Comics extends Plugin {
asort($comics);
- print "<ul class='panel panel-scrollable list list-unstyled'>";
- foreach ($comics as $comic) {
- print "<li>$comic</li>";
- }
- print "</ul>";
+ ?>
+ <div dojoType="dijit.layout.AccordionPane"
+ title="<i class='material-icons'>photo</i> <?= __('Feeds supported by af_comics') ?>">
- print_notice("To subscribe to GoComics use the comic's regular web page as the feed URL (e.g. for the <em>Garfield</em> comic use <code>http://www.gocomics.com/garfield</code>).");
+ <h3><?= __("The following comics are currently supported:") ?></h3>
- print_notice('Drop any updated filters into <code>filters.local</code> in plugin directory.');
+ <ul class='panel panel-scrollable list list-unstyled'>
+ <?php foreach ($comics as $comic) { ?>
+ <li><?= htmlspecialchars($comic) ?></li>
+ <?php } ?>
+ </ul>
- print "</div>";
+ <?= format_notice("To subscribe to GoComics use the comic's regular web page as the feed URL (e.g. for the <em>Garfield</em> comic use <code>http://www.gocomics.com/garfield</code>).") ?>
+ <?= format_notice('Drop any updated filters into <code>filters.local</code> in plugin directory.') ?>
+ </div>
+ <?php
}
function hook_article_filter($article) {
diff --git a/plugins/af_fsckportal/init.php b/plugins/af_fsckportal/init.php
index 04b77a15a..8caa617c6 100644
--- a/plugins/af_fsckportal/init.php
+++ b/plugins/af_fsckportal/init.php
@@ -19,9 +19,7 @@ class Af_Fsckportal extends Plugin {
$doc = new DOMDocument();
- @$doc->loadHTML('<?xml encoding="UTF-8">' . $article["content"]);
-
- if ($doc) {
+ if (@$doc->loadHTML('<?xml encoding="UTF-8">' . $article["content"])) {
$xpath = new DOMXPath($doc);
$entries = $xpath->query('(//img[@src]|//a[@href])');
@@ -34,7 +32,6 @@ class Af_Fsckportal extends Plugin {
}
$article["content"] = $doc->saveHTML();
-
}
return $article;
diff --git a/plugins/af_proxy_http/init.php b/plugins/af_proxy_http/init.php
index 3bde08fdb..b03cacfe4 100644
--- a/plugins/af_proxy_http/init.php
+++ b/plugins/af_proxy_http/init.php
@@ -50,8 +50,14 @@ class Af_Proxy_Http extends Plugin {
public function imgproxy() {
$url = UrlHelper::validate(clean($_REQUEST["url"]));
- // called without user context, let's just redirect to original URL
- if (!$_SESSION["uid"] || $_REQUEST['af_proxy_http_token'] != $_SESSION['af_proxy_http_token']) {
+ // immediately redirect to original URL if:
+ // - url points back to ourselves
+ // - called without user context
+ // - session-spefific token is invalid
+ if (
+ strpos($url, get_self_url_prefix()) === 0 ||
+ empty($_SESSION["uid"]) ||
+ $_REQUEST['af_proxy_http_token'] != $_SESSION['af_proxy_http_token']) {
header("Location: $url");
return;
}
@@ -59,14 +65,14 @@ class Af_Proxy_Http extends Plugin {
$local_filename = sha1($url);
if ($this->cache->exists($local_filename)) {
- header("Location: " . $this->cache->getUrl($local_filename));
+ header("Location: " . $this->cache->get_url($local_filename));
return;
} else {
- $data = UrlHelper::fetch(["url" => $url, "max_size" => MAX_CACHE_FILE_SIZE]);
+ $data = UrlHelper::fetch(["url" => $url, "max_size" => Config::get(Config::MAX_CACHE_FILE_SIZE)]);
if ($data) {
if ($this->cache->put($local_filename, $data)) {
- header("Location: " . $this->cache->getUrl($local_filename));
+ header("Location: " . $this->cache->get_url($local_filename));
return;
}
} else {
@@ -104,6 +110,11 @@ class Af_Proxy_Http extends Plugin {
}
private function rewrite_url_if_needed($url, $all_remote = false) {
+ /* don't rewrite urls pointing to ourselves */
+
+ if (strpos($url, get_self_url_prefix()) === 0)
+ return $url;
+
/* we don't need to handle URLs where local cache already exists, tt-rss rewrites those automatically */
if (!$this->cache->exists(sha1($url))) {
@@ -198,43 +209,41 @@ class Af_Proxy_Http extends Plugin {
function hook_prefs_tab($args) {
if ($args != "prefFeeds") return;
+ ?>
- print "<div dojoType=\"dijit.layout.AccordionPane\"
- title=\"<i class='material-icons'>extension</i> ".__('Image proxy settings (af_proxy_http)')."\">";
+ <div dojoType="dijit.layout.AccordionPane"
+ title="<i class='material-icons'>extension</i> <?= __('Image proxy settings (af_proxy_http)') ?>">
- print "<form dojoType=\"dijit.form.Form\">";
+ <form dojoType="dijit.form.Form">
- print "<script type=\"dojo/method\" event=\"onSubmit\" args=\"evt\">
- evt.preventDefault();
- if (this.validate()) {
- console.log(dojo.objectToQuery(this.getValues()));
- new Ajax.Request('backend.php', {
- parameters: dojo.objectToQuery(this.getValues()),
- onComplete: function(transport) {
- Notify.info(transport.responseText);
- }
- });
- //this.reset();
- }
- </script>";
+ <?= \Controls\pluginhandler_tags($this, "save") ?>
- print_hidden("op", "pluginhandler");
- print_hidden("method", "save");
- print_hidden("plugin", "af_proxy_http");
-
- $proxy_all = $this->host->get($this, "proxy_all");
- print_checkbox("proxy_all", $proxy_all);
- print "&nbsp;<label for=\"proxy_all\">" . __("Enable proxy for all remote images.") . "</label><br/>";
+ <script type="dojo/method" event="onSubmit" args="evt">
+ evt.preventDefault();
+ if (this.validate()) {
+ xhr.post("backend.php", this.getValues(), (reply) => {
+ Notify.info(reply);
+ })
+ }
+ </script>
- print "<p>"; print_button("submit", __("Save"));
+ <fieldset>
+ <label class="checkbox">
+ <?= \Controls\checkbox_tag("proxy_all", $this->host->get($this, "proxy_all")) ?>
+ <?= __("Enable proxy for all remote images.") ?>
+ </label>
+ </fieldset>
- print "</form>";
+ <hr/>
- print "</div>";
+ <?= \Controls\submit_tag(__("Save")) ?>
+ </form>
+ </div>
+ <?php
}
function save() {
- $proxy_all = checkbox_to_sql_bool($_POST["proxy_all"]);
+ $proxy_all = checkbox_to_sql_bool($_POST["proxy_all"] ?? "");
$this->host->set($this, "proxy_all", $proxy_all);
diff --git a/plugins/af_psql_trgm/init.js b/plugins/af_psql_trgm/init.js
index a22e673f6..921272c4b 100644
--- a/plugins/af_psql_trgm/init.js
+++ b/plugins/af_psql_trgm/init.js
@@ -1,15 +1,18 @@
-/* global dijit, Plugins, __ */
+/* global dijit, dojo, Plugins, xhr, __ */
Plugins.Psql_Trgm = {
showRelated: function (id) {
- const query = "backend.php?op=pluginhandler&plugin=af_psql_trgm&method=showrelated&param=" + encodeURIComponent(id);
-
const dialog = new dijit.Dialog({
title: __("Related articles"),
- execute: function () {
- //
- },
- href: query,
+ content: __("Loading, please wait...")
+ });
+
+ const tmph = dojo.connect(dialog, "onShow", null, function (/* e */) {
+ dojo.disconnect(tmph);
+
+ xhr.post("backend.php", {op: 'pluginhandler', plugin: 'af_psql_trgm', method: 'showrelated', id: id}, (reply) => {
+ dialog.attr('content', reply);
+ });
});
dialog.show();
diff --git a/plugins/af_psql_trgm/init.php b/plugins/af_psql_trgm/init.php
index 163b0ec38..5611d8998 100644
--- a/plugins/af_psql_trgm/init.php
+++ b/plugins/af_psql_trgm/init.php
@@ -15,7 +15,7 @@ class Af_Psql_Trgm extends Plugin {
function save() {
$similarity = (float) $_POST["similarity"];
$min_title_length = (int) $_POST["min_title_length"];
- $enable_globally = checkbox_to_sql_bool($_POST["enable_globally"]);
+ $enable_globally = checkbox_to_sql_bool($_POST["enable_globally"] ?? "");
if ($similarity < 0) $similarity = 0;
if ($similarity > 1) $similarity = 1;
@@ -46,7 +46,7 @@ class Af_Psql_Trgm extends Plugin {
}
function showrelated() {
- $id = (int) $_REQUEST['param'];
+ $id = (int) $_REQUEST['id'];
$owner_uid = $_SESSION["uid"];
$sth = $this->pdo->prepare("SELECT title FROM ttrss_entries, ttrss_user_entries
@@ -124,115 +124,117 @@ class Af_Psql_Trgm extends Plugin {
function hook_prefs_tab($args) {
if ($args != "prefFeeds") return;
- print "<div dojoType=\"dijit.layout.AccordionPane\"
- title=\"<i class='material-icons'>extension</i> ".__('Mark similar articles as read (af_psql_trgm)')."\">";
+ $similarity = $this->host->get($this, "similarity", $this->default_similarity);
+ $min_title_length = $this->host->get($this, "min_title_length", $this->default_min_length);
+ $enable_globally = sql_bool_to_bool($this->host->get($this, "enable_globally"));
- if (DB_TYPE != "pgsql") {
- print_error("Database type not supported.");
- } else {
-
- $res = $this->pdo->query("select 'similarity'::regproc");
-
- if (!$res || !$res->fetch()) {
- print_error("pg_trgm extension not found.");
- }
-
- $similarity = $this->host->get($this, "similarity", $this->default_similarity);
- $min_title_length = $this->host->get($this, "min_title_length", $this->default_min_length);
- $enable_globally = $this->host->get($this, "enable_globally");
-
- print "<form dojoType=\"dijit.form.Form\">";
-
- print "<script type=\"dojo/method\" event=\"onSubmit\" args=\"evt\">
- evt.preventDefault();
- if (this.validate()) {
- console.log(dojo.objectToQuery(this.getValues()));
- new Ajax.Request('backend.php', {
- parameters: dojo.objectToQuery(this.getValues()),
- onComplete: function(transport) {
- Notify.info(transport.responseText);
- }
- });
- //this.reset();
- }
- </script>";
-
- print_hidden("op", "pluginhandler");
- print_hidden("method", "save");
- print_hidden("plugin", "af_psql_trgm");
-
- print "<h2>" . __("Global settings") . "</h2>";
-
- print_notice("Enable for specific feeds in the feed editor.");
-
- print "<fieldset>";
-
- print "<label>" . __("Minimum similarity:") . "</label> ";
- print "<input dojoType=\"dijit.form.NumberSpinner\"
- placeholder=\"0.75\" id='psql_trgm_similarity'
- required=\"1\" name=\"similarity\" value=\"$similarity\">";
-
- print "<div dojoType='dijit.Tooltip' connectId='psql_trgm_similarity' position='below'>" .
- __("PostgreSQL trigram extension returns string similarity as a floating point number (0-1). Setting it too low might produce false positives, zero disables checking.") .
- "</div>";
-
- print "</fieldset><fieldset>";
-
- print "<label>" . __("Minimum title length:") . "</label> ";
- print "<input dojoType=\"dijit.form.NumberSpinner\"
- placeholder=\"32\"
- required=\"1\" name=\"min_title_length\" value=\"$min_title_length\">";
+ ?>
- print "</fieldset><fieldset>";
+ <div dojoType="dijit.layout.AccordionPane"
+ title="<i class='material-icons'>extension</i> <?= __('Mark similar articles as read (af_psql_trgm)') ?>">
- print "<label class='checkbox'>";
- print_checkbox("enable_globally", $enable_globally);
- print " " . __("Enable for all feeds:");
- print "</label>";
+ <?php
+ if (Config::get(Config::DB_TYPE) != "pgsql") {
+ print_error("Database type not supported.");
+ } else {
+ $res = $this->pdo->query("select 'similarity'::regproc");
- print "</fieldset>";
-
- print_button("submit", __("Save"), "class='alt-primary'");
- print "</form>";
-
- /* cleanup */
- $enabled_feeds = $this->filter_unknown_feeds(
- $this->get_stored_array("enabled_feeds"));
-
- $this->host->set($this, "enabled_feeds", $enabled_feeds);
-
- if (count($enabled_feeds) > 0) {
- print "<h3>" . __("Currently enabled for (click to edit):") . "</h3>";
-
- print "<ul class=\"panel panel-scrollable list list-unstyled\">";
- foreach ($enabled_feeds as $f) {
- print "<li>" .
- "<i class='material-icons'>rss_feed</i> <a href='#'
- onclick='CommonDialogs.editFeed($f)'>" .
- Feeds::getFeedTitle($f) . "</a></li>";
+ if (!$res || !$res->fetch()) {
+ print_error("pg_trgm extension not found.");
}
- print "</ul>";
- }
- }
-
- print "</div>";
+ } ?>
+
+ <form dojoType="dijit.form.Form">
+
+ <?= \Controls\pluginhandler_tags($this, "save") ?>
+
+ <script type="dojo/method" event="onSubmit" args="evt">
+ evt.preventDefault();
+ if (this.validate()) {
+ Notify.progress('Saving data...', true);
+ xhr.post("backend.php", this.getValues(), (reply) => {
+ Notify.info(reply);
+ })
+ }
+ </script>
+
+ <?= format_notice("Enable for specific feeds in the feed editor.") ?>
+
+ <fieldset>
+ <label><?= __("Minimum similarity:") ?></label>
+ <input dojoType="dijit.form.NumberSpinner"
+ placeholder="<?= $this->default_similarity ?>"
+ id='psql_trgm_similarity'
+ required="1"
+ name="similarity" value="<?= htmlspecialchars($similarity) ?>">
+
+ <div dojoType='dijit.Tooltip' connectId='psql_trgm_similarity' position='below'>
+ <?= __("PostgreSQL trigram extension returns string similarity as a floating point number (0-1). Setting it too low might produce false positives, zero disables checking.") ?>
+ </div>
+ </fieldset>
+
+ <fieldset>
+ <label><?= __("Minimum title length:") ?></label>
+ <input dojoType="dijit.form.NumberSpinner"
+ placeholder="<?= $this->default_min_length ?>"
+ required="1"
+ name="min_title_length" value="<?= htmlspecialchars($min_title_length) ?>">
+ </fieldset>
+
+ <fieldset>
+ <label class='checkbox'>
+ <?= \Controls\checkbox_tag("enable_globally", $enable_globally) ?>
+ <?= __("Enable for all feeds.") ?>
+ </label>
+ </fieldset>
+
+ <hr/>
+
+ <?= \Controls\submit_tag(__("Save")) ?>
+ </form>
+
+ <?php
+ /* cleanup */
+ $enabled_feeds = $this->filter_unknown_feeds(
+ $this->get_stored_array("enabled_feeds"));
+
+ $this->host->set($this, "enabled_feeds", $enabled_feeds);
+ ?>
+
+ <?php if (count($enabled_feeds) > 0) { ?>
+ <hr/>
+ <h3><?= __("Currently enabled for (click to edit):") ?></h3>
+
+ <ul class="panel panel-scrollable list list-unstyled">
+ <?php foreach ($enabled_feeds as $f) { ?>
+ <li>
+ <i class='material-icons'>rss_feed</i>
+ <a href='#' onclick="CommonDialogs.editFeed(<?= $f ?>)">
+ <?= Feeds::_get_title($f) ?>
+ </a>
+ </li>
+ <?php } ?>
+ </ul>
+ <?php } ?>
+ </div>
+ <?php
}
function hook_prefs_edit_feed($feed_id) {
- print "<header>".__("Similarity (af_psql_trgm)")."</header>";
- print "<section>";
-
- $enabled_feeds = $this->get_stored_array("enabled_feeds");
- $checked = in_array($feed_id, $enabled_feeds) ? "checked" : "";
-
- print "<fieldset>";
-
- print "<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' id='trgm_similarity_enabled'
- name='trgm_similarity_enabled' $checked> ".__('Mark similar articles as read')."</label>";
-
- print "</fieldset>";
-
- print "</section>";
+ $enabled_feeds = $this->get_stored_array("enabled_feeds");
+ ?>
+ <header><?= __("Similarity (af_psql_trgm)") ?></header>
+
+ <section>
+ <fieldset>
+ <label class="checkbox">
+ <?= \Controls\checkbox_tag("trgm_similarity_enabled", in_array($feed_id, $enabled_feeds)) ?>
+ <?= __('Mark similar articles as read') ?>
+ </label>
+ </fieldset>
+ </section>
+ </section>
+ <?php
}
function hook_prefs_save_feed($feed_id) {
@@ -256,7 +258,7 @@ class Af_Psql_Trgm extends Plugin {
function hook_article_filter($article) {
- if (DB_TYPE != "pgsql") return $article;
+ if (Config::get(Config::DB_TYPE) != "pgsql") return $article;
$res = $this->pdo->query("select 'similarity'::regproc");
if (!$res || !$res->fetch()) return $article;
diff --git a/plugins/af_readability/init.js b/plugins/af_readability/init.js
index 3155475cc..0232ed32d 100644
--- a/plugins/af_readability/init.js
+++ b/plugins/af_readability/init.js
@@ -1,9 +1,11 @@
+/* global xhr, App, Plugins, Article, Notify */
+
Plugins.Af_Readability = {
orig_attr_name: 'data-readability-orig-content',
self: this,
embed: function(id) {
- const content = $$(App.isCombinedMode() ? ".cdm[data-article-id=" + id + "] .content-inner" :
- ".post[data-article-id=" + id + "] .content")[0];
+ const content = App.find(App.isCombinedMode() ? `.cdm[data-article-id="${id}"] .content-inner` :
+ `.post[data-article-id="${id}"] .content`);
if (content.hasAttribute(self.orig_attr_name)) {
content.innerHTML = content.getAttribute(self.orig_attr_name);
@@ -16,7 +18,7 @@ Plugins.Af_Readability = {
Notify.progress("Loading, please wait...");
- xhrJson("backend.php",{ op: "pluginhandler", plugin: "af_readability", method: "embed", param: id }, (reply) => {
+ xhr.json("backend.php", App.getPhArgs("af_readability", "embed", {id: id}), (reply) => {
if (content && reply.content) {
content.setAttribute(self.orig_attr_name, content.innerHTML);
diff --git a/plugins/af_readability/init.php b/plugins/af_readability/init.php
index a76c98380..be9220cda 100755
--- a/plugins/af_readability/init.php
+++ b/plugins/af_readability/init.php
@@ -18,7 +18,7 @@ class Af_Readability extends Plugin {
}
function save() {
- $enable_share_anything = checkbox_to_sql_bool($_POST["enable_share_anything"]);
+ $enable_share_anything = checkbox_to_sql_bool($_POST["enable_share_anything"] ?? "");
$this->host->set($this, "enable_share_anything", $enable_share_anything);
@@ -29,11 +29,6 @@ class Af_Readability extends Plugin {
{
$this->host = $host;
- if (version_compare(PHP_VERSION, '7.0.0', '<')) {
- user_error("af_readability requires PHP 7.0", E_USER_WARNING);
- return;
- }
-
$host->add_hook($host::HOOK_ARTICLE_FILTER, $this);
$host->add_hook($host::HOOK_PREFS_TAB, $this);
$host->add_hook($host::HOOK_PREFS_EDIT_FEED, $this);
@@ -60,99 +55,92 @@ class Af_Readability extends Plugin {
function hook_prefs_tab($args) {
if ($args != "prefFeeds") return;
- print "<div dojoType='dijit.layout.AccordionPane'
- title=\"<i class='material-icons'>extension</i> ".__('Readability settings (af_readability)')."\">";
+ $enable_share_anything = sql_bool_to_bool($this->host->get($this, "enable_share_anything"));
- if (version_compare(PHP_VERSION, '7.0.0', '<')) {
- print_error("This plugin requires PHP 7.0.");
- } else {
+ ?>
+ <div dojoType='dijit.layout.AccordionPane'
+ title="<i class='material-icons'>extension</i> <?= __('Readability settings (af_readability)') ?>">
- print "<h2>" . __("Global settings") . "</h2>";
+ <?= format_notice("Enable for specific feeds in the feed editor.") ?>
- print_notice("Enable for specific feeds in the feed editor.");
+ <form dojoType='dijit.form.Form'>
- print "<form dojoType='dijit.form.Form'>";
+ <?= \Controls\pluginhandler_tags($this, "save") ?>
- print "<script type='dojo/method' event='onSubmit' args='evt'>
- evt.preventDefault();
- if (this.validate()) {
- console.log(dojo.objectToQuery(this.getValues()));
- new Ajax.Request('backend.php', {
- parameters: dojo.objectToQuery(this.getValues()),
- onComplete: function(transport) {
- Notify.info(transport.responseText);
+ <script type="dojo/method" event="onSubmit" args="evt">
+ evt.preventDefault();
+ if (this.validate()) {
+ Notify.progress('Saving data...', true);
+ xhr.post("backend.php", this.getValues(), (reply) => {
+ Notify.info(reply);
+ })
}
- });
- //this.reset();
- }
- </script>";
-
- print_hidden("op", "pluginhandler");
- print_hidden("method", "save");
- print_hidden("plugin", "af_readability");
-
- $enable_share_anything = $this->host->get($this, "enable_share_anything");
-
- print "<fieldset>";
- print "<label class='checkbox'> ";
- print_checkbox("enable_share_anything", $enable_share_anything);
- print " " . __("Provide full-text services to core code (bookmarklets) and other plugins");
- print "</label>";
- print "</fieldset>";
-
- print_button("submit", __("Save"), "class='alt-primary'");
- print "</form>";
-
- /* cleanup */
- $enabled_feeds = $this->filter_unknown_feeds(
- $this->get_stored_array("enabled_feeds"));
-
- $append_feeds = $this->filter_unknown_feeds(
- $this->get_stored_array("append_feeds"));
-
- $this->host->set($this, "enabled_feeds", $enabled_feeds);
- $this->host->set($this, "append_feeds", $append_feeds);
-
- if (count($enabled_feeds) > 0) {
- print "<h3>" . __("Currently enabled for (click to edit):") . "</h3>";
-
- print "<ul class='panel panel-scrollable list list-unstyled'>";
- foreach ($enabled_feeds as $f) {
- $is_append = in_array($f, $append_feeds);
-
- print "<li><i class='material-icons'>rss_feed</i> <a href='#'
- onclick='CommonDialogs.editFeed($f)'>".
- Feeds::getFeedTitle($f) . " " . ($is_append ? __("(append)") : "") . "</a></li>";
- }
- print "</ul>";
- }
-
- }
-
- print "</div>";
+ </script>
+
+ <fieldset>
+ <label class='checkbox'>
+ <?= \Controls\checkbox_tag("enable_share_anything", $enable_share_anything) ?>
+ <?= __("Provide full-text services to core code (bookmarklets) and other plugins") ?>
+ </label>
+ </fieldset>
+
+ <hr/>
+
+ <?= \Controls\submit_tag(__("Save")) ?>
+ </form>
+
+ <?php
+ /* cleanup */
+ $enabled_feeds = $this->filter_unknown_feeds(
+ $this->get_stored_array("enabled_feeds"));
+
+ $append_feeds = $this->filter_unknown_feeds(
+ $this->get_stored_array("append_feeds"));
+
+ $this->host->set($this, "enabled_feeds", $enabled_feeds);
+ $this->host->set($this, "append_feeds", $append_feeds);
+ ?>
+
+ <?php if (count($enabled_feeds) > 0) { ?>
+ <hr/>
+ <h3><?= __("Currently enabled for (click to edit):") ?></h3>
+
+ <ul class='panel panel-scrollable list list-unstyled'>
+ <?php foreach ($enabled_feeds as $f) { ?>
+ <li>
+ <i class='material-icons'>rss_feed</i>
+ <a href='#' onclick="CommonDialogs.editFeed(<?= $f ?>)">
+ <?= Feeds::_get_title($f) . " " . (in_array($f, $append_feeds) ? __("(append)") : "") ?>
+ </a>
+ </li>
+ <?php } ?>
+ </ul>
+ <?php } ?>
+ </div>
+ <?php
}
function hook_prefs_edit_feed($feed_id) {
- print "<header>".__("Readability")."</header>";
- print "<section>";
-
$enabled_feeds = $this->get_stored_array("enabled_feeds");
$append_feeds = $this->get_stored_array("append_feeds");
-
- $enable_checked = in_array($feed_id, $enabled_feeds) ? "checked" : "";
- $append_checked = in_array($feed_id, $append_feeds) ? "checked" : "";
-
- print "<fieldset>";
-
- print "<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' id='af_readability_enabled'
- name='af_readability_enabled' $enable_checked>&nbsp;".__('Inline article content')."</label>";
-
- print "</fieldset><fieldset>";
-
- print "<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' id='af_readability_append'
- name='af_readability_append' $append_checked>&nbsp;".__('Append to summary, instead of replacing it')."</label>";
-
- print "</section>";
+ ?>
+
+ <header><?= __("Readability") ?></header>
+ <section>
+ <fieldset>
+ <label class='checkbox'>
+ <?= \Controls\checkbox_tag("af_readability_enabled", in_array($feed_id, $enabled_feeds)) ?>
+ <?= __('Inline article content') ?>
+ </label>
+ </fieldset>
+ <fieldset>
+ <label class='checkbox'>
+ <?= \Controls\checkbox_tag("af_readability_append", in_array($feed_id, $append_feeds)) ?>
+ <?= __('Append to summary, instead of replacing it') ?>
+ </label>
+ </fieldset>
+ </section>
+ <?php
}
function hook_prefs_save_feed($feed_id) {
@@ -333,7 +321,7 @@ class Af_Readability extends Plugin {
}
function embed() {
- $article_id = (int) $_REQUEST["param"];
+ $article_id = (int) $_REQUEST["id"];
$sth = $this->pdo->prepare("SELECT link FROM ttrss_entries WHERE id = ?");
$sth->execute([$article_id]);
diff --git a/plugins/af_redditimgur/init.php b/plugins/af_redditimgur/init.php
index 2e89fcdff..1aa4793ea 100755
--- a/plugins/af_redditimgur/init.php
+++ b/plugins/af_redditimgur/init.php
@@ -31,65 +31,62 @@ class Af_RedditImgur extends Plugin {
function hook_prefs_tab($args) {
if ($args != "prefFeeds") return;
- print "<div dojoType=\"dijit.layout.AccordionPane\"
- title=\"<i class='material-icons'>extension</i> ".__('Reddit content settings (af_redditimgur)')."\">";
+ $enable_readability = $this->host->get($this, "enable_readability");
+ $enable_content_dupcheck = $this->host->get($this, "enable_content_dupcheck");
+ $reddit_to_teddit = $this->host->get($this, "reddit_to_teddit");
+ ?>
- $enable_readability = $this->host->get($this, "enable_readability");
- $enable_content_dupcheck = $this->host->get($this, "enable_content_dupcheck");
- $reddit_to_teddit = $this->host->get($this, "reddit_to_teddit");
+ <div dojoType="dijit.layout.AccordionPane"
+ title="<i class='material-icons'>extension</i> <?= __('Reddit content settings (af_redditimgur)') ?>">
- if (version_compare(PHP_VERSION, '5.6.0', '<')) {
- print_error("Readability requires PHP version 5.6.");
- }
+ <form dojoType='dijit.form.Form'>
- print "<form dojoType='dijit.form.Form'>";
+ <?= \Controls\pluginhandler_tags($this, "save") ?>
- print "<script type='dojo/method' event='onSubmit' args='evt'>
- evt.preventDefault();
- if (this.validate()) {
- console.log(dojo.objectToQuery(this.getValues()));
- new Ajax.Request('backend.php', {
- parameters: dojo.objectToQuery(this.getValues()),
- onComplete: function(transport) {
- Notify.info(transport.responseText);
+ <script type="dojo/method" event="onSubmit" args="evt">
+ evt.preventDefault();
+ if (this.validate()) {
+ Notify.progress('Saving data...', true);
+ xhr.post("backend.php", this.getValues(), (reply) => {
+ Notify.info(reply);
+ })
}
- });
- //this.reset();
- }
- </script>";
-
- print_hidden("op", "pluginhandler");
- print_hidden("method", "save");
- print_hidden("plugin", "af_redditimgur");
-
- print "<fieldset class='narrow'>";
- print "<label class='checkbox'>";
- print_checkbox("enable_readability", $enable_readability);
- print " " . __("Extract missing content using Readability (requires af_readability)") . "</label>";
- print "</fieldset>";
-
- print "<fieldset class='narrow'>";
- print "<label class='checkbox'>";
- print_checkbox("enable_content_dupcheck", $enable_content_dupcheck);
- print " " . __("Enable additional duplicate checking") . "</label>";
- print "</fieldset>";
-
- print "<fieldset class='narrow'>";
- print "<label class='checkbox'>";
- print_checkbox("reddit_to_teddit", $reddit_to_teddit);
- print " " . T_sprintf("Rewrite Reddit URLs to %s",
- "<a target=\"_blank\" href=\"https://teddit.net/about\">Teddit</a>") . "</label>";
-
- print_button("submit", __("Save"), 'class="alt-primary"');
- print "</form>";
-
- print "</div>";
+ </script>
+
+ <fieldset class='narrow'>
+ <label class='checkbox'>
+ <?= \Controls\checkbox_tag("enable_readability", $enable_readability) ?>
+ <?= __("Extract missing content using Readability (requires af_readability)") ?>
+ </label>
+ </fieldset>
+
+ <fieldset class='narrow'>
+ <label class='checkbox'>
+ <?= \Controls\checkbox_tag("enable_content_dupcheck", $enable_content_dupcheck) ?>
+ <?= __("Enable additional duplicate checking") ?>
+ </label>
+ </fieldset>
+
+ <fieldset class='narrow'>
+ <label class='checkbox'>
+ <?= \Controls\checkbox_tag("reddit_to_teddit", $reddit_to_teddit) ?>
+ <?= T_sprintf("Rewrite Reddit URLs to %s",
+ "<a target=\"_blank\" href=\"https://teddit.net/about\">Teddit</a>") ?>
+ </label>
+ </fieldset>
+
+ <hr/>
+ <?= \Controls\submit_tag(__("Save")) ?>
+ </form>
+ </div>
+
+ <?php
}
function save() {
- $enable_readability = checkbox_to_sql_bool($_POST["enable_readability"]);
- $enable_content_dupcheck = checkbox_to_sql_bool($_POST["enable_content_dupcheck"]);
- $reddit_to_teddit = checkbox_to_sql_bool($_POST["reddit_to_teddit"]);
+ $enable_readability = checkbox_to_sql_bool($_POST["enable_readability"] ?? "");
+ $enable_content_dupcheck = checkbox_to_sql_bool($_POST["enable_content_dupcheck"] ?? "");
+ $reddit_to_teddit = checkbox_to_sql_bool($_POST["reddit_to_teddit"] ?? "");
$this->host->set($this, "enable_readability", $enable_readability, false);
$this->host->set($this, "reddit_to_teddit", $reddit_to_teddit, false);
@@ -220,6 +217,7 @@ class Af_RedditImgur extends Plugin {
$this->fallback_preview_urls = [];
+ // @phpstan-ignore-next-line
if ($tmp && $anchor) {
$json = json_decode($tmp, true);
@@ -349,6 +347,8 @@ class Af_RedditImgur extends Plugin {
if (strpos($source_stream, "imgur.com") !== false)
$poster_url = str_replace(".mp4", "h.jpg", $source_stream);
+ else
+ $poster_url = false;
$this->handle_as_video($doc, $entry, $source_stream, $poster_url);
@@ -530,7 +530,7 @@ class Af_RedditImgur extends Plugin {
$entry_guid = $article["guid_hashed"];
$owner_uid = $article["owner_uid"];
- if (DB_TYPE == "pgsql") {
+ if (Config::get(Config::DB_TYPE) == "pgsql") {
$interval_qpart = "date_entered < NOW() - INTERVAL '1 day'";
} else {
$interval_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 DAY)";
@@ -631,6 +631,10 @@ class Af_RedditImgur extends Plugin {
$entry->parentNode->insertBefore($img, $entry);*/
}
+ function csrf_ignore($method) {
+ return $method === "testurl";
+ }
+
function testurl() {
$url = clean($_POST["url"]);
@@ -645,14 +649,17 @@ class Af_RedditImgur extends Plugin {
fieldset { border : 0; }
label { display : inline-block; min-width : 120px; }
</style>
- <form action="backend.php?op=pluginhandler&method=testurl&plugin=af_redditimgur" method="post">
+ <form action="backend.php" method="post">
+ <input type="hidden" name="op" value="pluginhandler">
+ <input type="hidden" name="method" value="testurl">
+ <input type="hidden" name="plugin" value="af_redditimgur">
<fieldset>
<label>URL:</label>
- <input name="url" size="100" value="<?php echo htmlspecialchars($url) ?>"></input>
+ <input name="url" size="100" value="<?= htmlspecialchars($url) ?>"></input>
</fieldset>
<fieldset>
<label>Article URL:</label>
- <input name="article_url" size="100" value="<?php echo htmlspecialchars($article_url) ?>"></input>
+ <input name="article_url" size="100" value="<?= htmlspecialchars($article_url) ?>"></input>
</fieldset>
<fieldset>
<button type="submit">Test</button>
@@ -694,7 +701,7 @@ class Af_RedditImgur extends Plugin {
private function get_header($url, $header, $useragent = SELF_USER_AGENT) {
$ret = false;
- if (function_exists("curl_init") && !defined("NO_CURL")) {
+ if (function_exists("curl_init")) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
@@ -720,7 +727,7 @@ class Af_RedditImgur extends Plugin {
private function readability($article, $url, $doc, $xpath, $debug = false) {
- if (!defined('NO_CURL') && function_exists("curl_init") && $this->host->get($this, "enable_readability") &&
+ if (function_exists("curl_init") && $this->host->get($this, "enable_readability") &&
mb_strlen(strip_tags($article["content"])) <= 150) {
// do not try to embed posts linking back to other reddit posts
diff --git a/plugins/af_tumblr_1280/init.php b/plugins/af_tumblr_1280/init.php
deleted file mode 100755
index 5d7f366a4..000000000
--- a/plugins/af_tumblr_1280/init.php
+++ /dev/null
@@ -1,91 +0,0 @@
-<?php
-class Af_Tumblr_1280 extends Plugin {
- private $host;
-
- function about() {
- return array(1.0,
- "Replace Tumblr pictures and videos with largest size if available (requires CURL)",
- "fox");
- }
-
- function flags() {
- return array("needs_curl" => true);
- }
-
- function init($host) {
- $this->host = $host;
-
- if (function_exists("curl_init")) {
- $host->add_hook($host::HOOK_ARTICLE_FILTER, $this);
- }
- }
-
- function hook_article_filter($article) {
-
- if (!function_exists("curl_init") || ini_get("open_basedir"))
- return $article;
-
- $doc = new DOMDocument();
- $doc->loadHTML('<?xml encoding="UTF-8">' . $article["content"]);
-
- $found = false;
-
- if ($doc) {
- $xpath = new DOMXpath($doc);
-
- $images = $xpath->query('(//img[contains(@src, \'media.tumblr.com\')])');
-
- foreach ($images as $img) {
- $src = $img->getAttribute("src");
-
- $test_src = preg_replace("/_\d{3}.(jpg|gif|png)/", "_1280.$1", $src);
-
- if ($src != $test_src) {
-
- $ch = curl_init($test_src);
- curl_setopt($ch, CURLOPT_TIMEOUT, 5);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($ch, CURLOPT_HEADER, true);
- curl_setopt($ch, CURLOPT_NOBODY, true);
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
- curl_setopt($ch, CURLOPT_USERAGENT, SELF_USER_AGENT);
-
- @$result = curl_exec($ch);
- $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
-
- if ($result && $http_code == 200) {
- $img->setAttribute("src", $test_src);
- $found = true;
- }
- }
- }
-
- $video_sources = $xpath->query('//video/source[contains(@src, \'.tumblr.com/video_file\')]');
-
- foreach ($video_sources as $source) {
- $src = $source->getAttribute("src");
-
- $new_src = preg_replace("/\/\d{3}$/", "", $src);
-
- if ($src != $new_src) {
- $source->setAttribute("src", $new_src);
- $found = true;
- }
- }
-
- if ($found) {
- $doc->removeChild($doc->firstChild); //remove doctype
- $article["content"] = $doc->saveHTML();
- }
- }
-
- return $article;
-
- }
-
-
- function api_version() {
- return 2;
- }
-
-}
diff --git a/plugins/af_unburn/init.php b/plugins/af_unburn/init.php
index 4d0c56740..386b6387f 100755
--- a/plugins/af_unburn/init.php
+++ b/plugins/af_unburn/init.php
@@ -21,7 +21,7 @@ class Af_Unburn extends Plugin {
function hook_article_filter($article) {
$owner_uid = $article["owner_uid"];
- if (defined('NO_CURL') || !function_exists("curl_init") || ini_get("open_basedir"))
+ if (!function_exists("curl_init") || ini_get("open_basedir"))
return $article;
if ((strpos($article["link"], "feedproxy.google.com") !== false ||
@@ -37,8 +37,8 @@ class Af_Unburn extends Plugin {
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_USERAGENT, SELF_USER_AGENT);
- if (defined('_CURL_HTTP_PROXY')) {
- curl_setopt($ch, CURLOPT_PROXY, _CURL_HTTP_PROXY);
+ if (Config::get(Config::HTTP_PROXY)) {
+ curl_setopt($ch, CURLOPT_PROXY, Config::get(Config::HTTP_PROXY));
}
@curl_exec($ch);
@@ -80,4 +80,4 @@ class Af_Unburn extends Plugin {
return 2;
}
-} \ No newline at end of file
+}
diff --git a/plugins/af_youtube_embed/init.php b/plugins/af_youtube_embed/init.php
index db82dc9f5..6309aac02 100644
--- a/plugins/af_youtube_embed/init.php
+++ b/plugins/af_youtube_embed/init.php
@@ -23,9 +23,9 @@ class Af_Youtube_Embed extends Plugin {
$matches = array();
- if (preg_match("/\/\/www\.youtube\.com\/v\/([\w-]+)/", $entry["url"], $matches) ||
- preg_match("/\/\/www\.youtube\.com\/watch?v=([\w-]+)/", $entry["url"], $matches) ||
- preg_match("/\/\/youtu.be\/([\w-]+)/", $entry["url"], $matches)) {
+ 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)) {
$vid_id = $matches[1];
diff --git a/plugins/af_zz_vidmute/init.js b/plugins/af_zz_vidmute/init.js
index fab9b99e6..b8be8cecd 100644
--- a/plugins/af_zz_vidmute/init.js
+++ b/plugins/af_zz_vidmute/init.js
@@ -3,7 +3,7 @@ require(['dojo/_base/kernel', 'dojo/ready'], function (dojo, ready) {
PluginHost.register(PluginHost.HOOK_ARTICLE_RENDERED_CDM, function (row) {
if (row) {
- row.select("video").each(function (v) {
+ row.querySelectorAll("video").forEach(function (v) {
v.muted = true;
});
}
@@ -14,7 +14,7 @@ require(['dojo/_base/kernel', 'dojo/ready'], function (dojo, ready) {
PluginHost.register(PluginHost.HOOK_ARTICLE_RENDERED, function (row) {
if (row) {
- row.select("video").each(function (v) {
+ row.querySelectorAll("video").forEach(function (v) {
v.muted = true;
});
}
diff --git a/plugins/auth_internal/init.php b/plugins/auth_internal/init.php
index a69ea444c..13a7bc969 100644
--- a/plugins/auth_internal/init.php
+++ b/plugins/auth_internal/init.php
@@ -63,21 +63,21 @@ class Auth_Internal extends Auth_Base {
<title>Tiny Tiny RSS</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
- <?php echo stylesheet_tag("themes/light.css") ?>
+ <?= stylesheet_tag("themes/light.css") ?>
<body class="ttrss_utility otp">
- <h1><?php echo __("Authentication") ?></h1>
+ <h1><?= __("Authentication") ?></h1>
<div class="content">
- <form action="public.php?return=<?php echo $return ?>"
+ <form action="public.php?return=<?= $return ?>"
method="POST" class="otpform">
<input type="hidden" name="op" value="login">
- <input type="hidden" name="login" value="<?php echo htmlspecialchars($login) ?>">
- <input type="hidden" name="password" value="<?php echo htmlspecialchars($password) ?>">
- <input type="hidden" name="bw_limit" value="<?php echo htmlspecialchars($_POST["bw_limit"]) ?>">
- <input type="hidden" name="remember_me" value="<?php echo htmlspecialchars($_POST["remember_me"]) ?>">
- <input type="hidden" name="profile" value="<?php echo htmlspecialchars($_POST["profile"]) ?>">
+ <input type="hidden" name="login" value="<?= htmlspecialchars($login) ?>">
+ <input type="hidden" name="password" value="<?= htmlspecialchars($password) ?>">
+ <input type="hidden" name="bw_limit" value="<?= htmlspecialchars($_POST["bw_limit"]) ?>">
+ <input type="hidden" name="remember_me" value="<?= htmlspecialchars($_POST["remember_me"]) ?>">
+ <input type="hidden" name="profile" value="<?= htmlspecialchars($_POST["profile"]) ?>">
<fieldset>
- <label><?php echo __("Please enter your one time password:") ?></label>
+ <label><?= __("Please enter your one time password:") ?></label>
<input autocomplete="off" size="6" name="otp" value=""/>
<input type="submit" value="Continue"/>
</fieldset>
@@ -244,7 +244,7 @@ class Auth_Internal extends Auth_Base {
$tpl->readTemplateFromFile("password_change_template.txt");
$tpl->setVariable('LOGIN', $row["login"]);
- $tpl->setVariable('TTRSS_HOST', SELF_URL_PATH);
+ $tpl->setVariable('TTRSS_HOST', Config::get(Config::SELF_URL_PATH));
$tpl->addBlock('message');
diff --git a/plugins/auth_remote/init.php b/plugins/auth_remote/init.php
index 85be67d05..f2dcfb318 100644
--- a/plugins/auth_remote/init.php
+++ b/plugins/auth_remote/init.php
@@ -56,7 +56,7 @@ class Auth_Remote extends Auth_Base {
$_SESSION["hide_logout"] = true;
// LemonLDAP can send user informations via HTTP HEADER
- if (defined('AUTH_AUTO_CREATE') && AUTH_AUTO_CREATE){
+ if (Config::get(Config::AUTH_AUTO_CREATE)) {
// update user name
$fullname = isset($_SERVER['HTTP_USER_NAME']) ? $_SERVER['HTTP_USER_NAME'] : ($_SERVER['AUTHENTICATE_CN'] ?? "");
if ($fullname){
diff --git a/plugins/auto_assign_labels/init.php b/plugins/auto_assign_labels/init.php
index 3fa4ad8c0..341895cef 100755
--- a/plugins/auto_assign_labels/init.php
+++ b/plugins/auto_assign_labels/init.php
@@ -19,6 +19,7 @@ class Auto_Assign_Labels extends Plugin {
function get_all_labels_filter_format($owner_uid) {
$rv = array();
+ // TODO: use Labels::get_all()
$sth = $this->pdo->prepare("SELECT id, fg_color, bg_color, caption FROM ttrss_labels2 WHERE owner_uid = ?");
$sth->execute([$owner_uid]);
diff --git a/plugins/bookmarklets/init.php b/plugins/bookmarklets/init.php
index fa1bb8cf6..967918823 100644
--- a/plugins/bookmarklets/init.php
+++ b/plugins/bookmarklets/init.php
@@ -1,53 +1,371 @@
<?php
class Bookmarklets extends Plugin {
- private $host;
-
- function about() {
- return array(1.0,
- "Easy feed subscription and web page sharing using bookmarklets",
- "fox",
- false,
- "https://git.tt-rss.org/fox/tt-rss/wiki/ShareAnything");
+ private $host;
+
+ function about() {
+ return array(1.0,
+ "Easy feed subscription and web page sharing using bookmarklets",
+ "fox",
+ false,
+ "https://git.tt-rss.org/fox/tt-rss/wiki/ShareAnything");
}
- function init($host) {
- $this->host = $host;
+ function init($host) {
+ $this->host = $host;
- $host->add_hook($host::HOOK_PREFS_TAB, $this);
- }
+ $host->add_hook($host::HOOK_PREFS_TAB, $this);
+ }
+
+ function is_public_method($method) {
+ return in_array($method, ["subscribe", "sharepopup"]);
+ }
- function hook_prefs_tab($args) {
- if ($args == "prefFeeds") {
+ function subscribe() {
+ if (Config::get(Config::SINGLE_USER_MODE)) {
+ UserHelper::login_sequence();
+ }
- print "<div dojoType=\"dijit.layout.AccordionPane\"
- title=\"<i class='material-icons'>bookmark</i> ".__('Bookmarklets')."\">";
+ if (!empty($_SESSION["uid"])) {
- print "<h3>" . __("Drag the link below to your browser toolbar, open the feed you're interested in in your browser and click on the link to subscribe to it.") . "</h3>";
+ $feed_url = clean($_REQUEST["feed_url"] ?? "");
+ $csrf_token = clean($_POST["csrf_token"] ?? "");
- $bm_subscribe_url = str_replace('%s', '', Pref_Feeds::subscribe_to_feed_url());
+ header('Content-Type: text/html; charset=utf-8');
+ ?>
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <title><?= __("Subscribe to feed...") ?></title>
+ <?= javascript_tag("lib/dojo/dojo.js") ?>
+ <?= javascript_tag("js/utility.js") ?>
+ <?= javascript_tag("js/common.js") ?>
+ <?= javascript_tag("lib/dojo/tt-rss-layer.js") ?>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <link rel="shortcut icon" type="image/png" href="images/favicon.png">
+ <link rel="icon" type="image/png" sizes="72x72" href="images/favicon-72px.png">
+ <style type="text/css">
+ @media (prefers-color-scheme: dark) {
+ body {
+ background : #303030;
+ }
+ }
- $confirm_str = str_replace("'", "\'", __('Subscribe to %s in Tiny Tiny RSS?'));
+ body.css_loading * {
+ display : none;
+ }
+ </style>
+ </head>
+ <body class='flat ttrss_utility css_loading'>
+ <script type="text/javascript">
+ const UtilityApp = {
+ init: function() {
+ require(['dojo/parser', "dojo/ready", 'dijit/form/Button','dijit/form/CheckBox', 'dijit/form/Form',
+ 'dijit/form/Select','dijit/form/TextBox','dijit/form/ValidationTextBox'], function(parser, ready){
+ ready(function() {
+ parser.parse();
+ });
+ });
+ }
+ };
+ </script>
+ <div class="container">
+ <h1><?= __("Subscribe to feed...") ?></h1>
+ <div class='content'>
+ <?php
- $bm_url = htmlspecialchars("javascript:{if(confirm('$confirm_str'.replace('%s',window.location.href)))window.location.href='$bm_subscribe_url'+encodeURIComponent(window.location.href)}");
+ if (!$feed_url || !validate_csrf($csrf_token)) {
+ ?>
+ <form method="post" action='public.php'>
+ <?= \Controls\public_method_tags($this, "subscribe") ?>
+ <?= \Controls\hidden_tag("csrf_token", $_SESSION["csrf_token"]) ?>
- print "<p><label class='dijitButton'>";
- print "<a href=\"$bm_url\">" . __('Subscribe in Tiny Tiny RSS'). "</a>";
- print "</label></p>";
+ <fieldset>
+ <label>Feed or site URL:</label>
+ <input style="width: 300px" dojoType="dijit.form.ValidationTextBox" required="1" name="feed_url" value="<?= htmlspecialchars($feed_url) ?>">
+ </fieldset>
- print "<h3>" . __("Use this bookmarklet to publish arbitrary pages using Tiny Tiny RSS") . "</h3>";
+ <button class="alt-primary" dojoType="dijit.form.Button" type="submit">
+ <?= __("Subscribe") ?>
+ </button>
- print "<label class='dijitButton'>";
- $bm_url = htmlspecialchars("javascript:(function(){var d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),f='".get_self_url_prefix()."/public.php?op=sharepopup',l=d.location,e=encodeURIComponent,g=f+'&title='+((e(s))?e(s):e(document.title))+'&url='+e(l.href);function a(){if(!w.open(g,'t','toolbar=0,resizable=0,scrollbars=1,status=1,width=500,height=250')){l.href=g;}}a();})()");
- print "<a href=\"$bm_url\">" . __('Share with Tiny Tiny RSS'). "</a>";
- print "</label>";
+ <a href="index.php"><?= __("Return to Tiny Tiny RSS") ?></a>
+ </form>
+ <?php
+ } else {
- print "<button dojoType='dijit.form.Button' class='alt-info' onclick='window.open(\"https://tt-rss.org/wiki/ShareAnything\")'>
- <i class='material-icons'>help</i> ".__("More info...")."</button>";
+ $rc = Feeds::_subscribe($feed_url);
+ $feed_urls = false;
- print "</div>"; #pane
+ switch ($rc['code']) {
+ case 0:
+ print_warning(T_sprintf("Already subscribed to <b>%s</b>.", $feed_url));
+ break;
+ case 1:
+ print_notice(T_sprintf("Subscribed to <b>%s</b>.", $feed_url));
+ break;
+ case 2:
+ print_error(T_sprintf("Could not subscribe to <b>%s</b>.", $feed_url));
+ break;
+ case 3:
+ print_error(T_sprintf("No feeds found in <b>%s</b>.", $feed_url));
+ break;
+ case 4:
+ $feed_urls = $rc["feeds"];
+ break;
+ case 5:
+ print_error(T_sprintf("Could not subscribe to <b>%s</b>.<br>Can't download the Feed URL.", $feed_url));
+ break;
+ }
- }
- }
+ if ($feed_urls) {
+ ?>
+ <form method='post' action='public.php'>
+ <?= \Controls\public_method_tags($this, "subscribe") ?>
+ <?= \Controls\hidden_tag("csrf_token", $_SESSION["csrf_token"]) ?>
+
+ <fieldset>
+ <label style='display : inline'><?= __("Multiple feed URLs found:") ?></label>
+ <select name='feed_url' dojoType='dijit.form.Select'>
+ <?php foreach ($feed_urls as $url => $name) { ?>
+ <option value="<?= htmlspecialchars($url) ?>"><?= htmlspecialchars($name) ?></option>
+ <?php } ?>
+ </select>
+ </fieldset>
+
+ <button class='alt-primary' dojoType='dijit.form.Button' type='submit'><?= __("Subscribe to selected feed") ?></button>
+ <a href='index.php'><?= __("Return to Tiny Tiny RSS") ?></a>
+ </form>
+ <?php
+ }
+
+ if ($rc['code'] <= 2) {
+ $feed_id = Feeds::_find_by_url($feed_url, $_SESSION["uid"]);
+ } else {
+ $feed_id = 0;
+ }
+
+ if ($feed_id) {
+ ?>
+ <form method='GET' action="<?= htmlspecialchars(get_self_url_prefix() . "/prefs.php") ?>">
+ <input type='hidden' name='tab' value='feeds'>
+ <input type='hidden' name='method' value='editfeed'>
+ <input type='hidden' name='methodparam' value="<?= $feed_id ?>">
+ <button dojoType='dijit.form.Button' class='alt-info' type='submit'><?= __("Edit subscription options") ?></button>
+ <a href='index.php'><?= __("Return to Tiny Tiny RSS") ?></a>
+ </form>
+ <?php
+ } else {
+ ?>
+ <a href='index.php'><?= __("Return to Tiny Tiny RSS") ?></a>
+ <?php
+ }
+ }
+ ?>
+ </div>
+ </div>
+ </body>
+ </html>
+ <?php
+ } else {
+ Handler_Public::_render_login_form();
+ }
+ }
+
+ function sharepopup() {
+ if (Config::get(Config::SINGLE_USER_MODE)) {
+ UserHelper::login_sequence();
+ }
+
+ header('Content-Type: text/html; charset=utf-8');
+ ?>
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <title><?= __("Share with Tiny Tiny RSS") ?></title>
+ <?= javascript_tag("lib/dojo/dojo.js") ?>
+ <?= javascript_tag("js/utility.js") ?>
+ <?= javascript_tag("js/common.js") ?>
+ <?= javascript_tag("lib/dojo/tt-rss-layer.js") ?>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <link rel="shortcut icon" type="image/png" href="images/favicon.png">
+ <link rel="icon" type="image/png" sizes="72x72" href="images/favicon-72px.png">
+ <style type="text/css">
+ @media (prefers-color-scheme: dark) {
+ body {
+ background : #303030;
+ }
+ }
+
+ body.css_loading * {
+ display : none;
+ }
+ </style>
+ </head>
+ <body class='flat ttrss_utility share_popup css_loading'>
+ <script type="text/javascript">
+ const UtilityApp = {
+ init: function() {
+ require(['dojo/parser', "dojo/ready", 'dijit/form/Button','dijit/form/CheckBox', 'dijit/form/Form',
+ 'dijit/form/Select','dijit/form/TextBox','dijit/form/ValidationTextBox'], function(parser, ready){
+ ready(function() {
+ parser.parse();
+
+ /* new Ajax.Autocompleter('labels_value', 'labels_choices',
+ "backend.php?op=rpc&method=completeLabels",
+ { tokens: ',', paramName: "search" }); */
+ });
+ });
+ }
+ };
+ </script>
+ <div class="content">
+
+ <?php
+ if ($_SESSION["uid"]) {
+
+ $action = clean($_REQUEST["action"] ?? "");
+
+ if ($action == 'share') {
+
+ $title = strip_tags(clean($_REQUEST["title"]));
+ $url = strip_tags(clean($_REQUEST["url"]));
+ $content = strip_tags(clean($_REQUEST["content"]));
+ $labels = strip_tags(clean($_REQUEST["labels"]));
+
+ Article::_create_published_article($title, $url, $content, $labels,
+ $_SESSION["uid"]);
+
+ ?>
+ <script type="text/javascript">
+ window.close();
+ </script>
+ <?php
+
+ } else {
+ $title = htmlspecialchars(clean($_REQUEST["title"]));
+ $url = htmlspecialchars(clean($_REQUEST["url"]));
+
+ ?>
+ <form method='post' action='public.php'>
+
+ <?= \Controls\public_method_tags($this, "sharepopup") ?>
+ <?= \Controls\hidden_tag("csrf_token", $_SESSION["csrf_token"]) ?>
+ <?= \Controls\hidden_tag("action", "share") ?>
+
+ <fieldset>
+ <label><?= __("Title:") ?></label>
+ <input style='width : 270px' dojoType='dijit.form.TextBox' name='title' value="<?= $title ?>">
+ </fieldset>
+
+ <fieldset>
+ <label><?= __("URL:") ?></label>
+ <input style='width : 270px' name='url' dojoType='dijit.form.TextBox' value="<?= $url ?>">
+ </fieldset>
+
+ <fieldset>
+ <label><?= __("Content:") ?></label>
+ <input style='width : 270px' name='content' dojoType='dijit.form.TextBox' value="">
+ </fieldset>
+
+ <fieldset>
+ <label><?= __("Labels:") ?></label>
+ <input style='width : 270px' name='labels' dojoType='dijit.form.TextBox' id="labels_value"
+ placeholder='Alpha, Beta, Gamma' value="">
+ <!-- <div class="autocomplete" id="labels_choices"
+ style="display : block"></div> -->
+ </fieldset>
+
+ <hr/>
+
+ <fieldset>
+ <?= \Controls\submit_tag(__("Share")) ?>
+ <?= \Controls\button_tag(__("Cancel"), "", ["onclick" => "window.close()"]) ?>
+ <span class="text-muted small"><?= __("Shared article will appear in the Published feed.") ?></span>
+ </fieldset>
+
+ </form>
+ <?php
+
+ }
+
+ } else {
+ print_error("Not logged in");
+ ?>
+
+ <form action="public.php?return=<?= urlencode(make_self_url()) ?>" method="post">
+
+ <input type="hidden" name="op" value="login">
+
+ <fieldset>
+ <label><?= __("Login:") ?></label>
+ <input name="login" id="login" dojoType="dijit.form.TextBox" type="text"
+ onchange="fetchProfiles()" onfocus="fetchProfiles()" onblur="fetchProfiles()"
+ required="1" value="<?= $_SESSION["fake_login"] ?>" />
+ </fieldset>
+
+ <fieldset>
+ <label><?= __("Password:") ?></label>
+
+ <input type="password" name="password" required="1"
+ dojoType="dijit.form.TextBox"
+ class="input input-text"
+ value="<?= $_SESSION["fake_password"] ?>"/>
+ </fieldset>
+
+ <hr/>
+
+ <fieldset>
+ <label> </label>
+
+ <button dojoType="dijit.form.Button" type="submit" class="alt-primary"><?= __('Log in') ?></button>
+ </fieldset>
+
+ </form>
+ <?php
+ }
+ ?>
+ </div>
+ </body>
+ </html>
+ <?php
+ }
+
+
+ function hook_prefs_tab($args) {
+ if ($args != "prefFeeds")
+ return;
+
+ $bm_subscribe_url = $this->host->get_public_method_url($this, "subscribe");
+ $bm_share_url = $this->host->get_public_method_url($this, "sharepopup");
+
+ $confirm_str = str_replace("'", "\'", __('Subscribe to %s in Tiny Tiny RSS?'));
+
+ $bm_subscribe_url = htmlspecialchars("javascript:{if(confirm('$confirm_str'.replace('%s',window.location.href)))window.location.href='$bm_subscribe_url&feed_url='+encodeURIComponent(window.location.href)}");
+ $bm_share_url = htmlspecialchars("javascript:(function(){var d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),f='$bm_share_url',l=d.location,e=encodeURIComponent,g=f+'&title='+((e(s))?e(s):e(document.title))+'&url='+e(l.href);function a(){if(!w.open(g,'t','toolbar=0,resizable=0,scrollbars=1,status=1,width=500,height=250')){l.href=g;}}a();})()");
+ ?>
+
+ <div dojoType="dijit.layout.AccordionPane"
+ title="<i class='material-icons'>bookmark</i> <?= __('Bookmarklets') ?>">
+
+ <h3><?= __("Drag the link below to your browser toolbar, open the feed you're interested in in your browser and click on the link to subscribe to it.") ?></h3>
+
+ <label class='dijitButton'>
+ <a href="<?= $bm_subscribe_url ?>"><?= __('Subscribe in Tiny Tiny RSS') ?></a>
+ </label>
+
+ <h3><?= __("Use this bookmarklet to publish arbitrary pages using Tiny Tiny RSS") ?></h3>
+
+ <label class='dijitButton'>
+ <a href="<?= $bm_share_url ?>"><?= __('Share with Tiny Tiny RSS') ?></a>
+ </label>
+
+ <?= \Controls\button_tag(\Controls\icon("help") . " " . __("More info..."), "",
+ ["class" => 'alt-info', "onclick" => "window.open('https://tt-rss.org/wiki/ShareAnything')"]) ?>
+
+ </div>
+
+ <?php
+ }
function api_version() {
return 2;
diff --git a/plugins/cache_starred_images/init.php b/plugins/cache_starred_images/init.php
index 9dd4cd49d..2dbdb99cc 100755
--- a/plugins/cache_starred_images/init.php
+++ b/plugins/cache_starred_images/init.php
@@ -5,7 +5,7 @@ class Cache_Starred_Images extends Plugin {
private $host;
/* @var DiskCache $cache */
private $cache;
- private $max_cache_attempts = 5; // per-article
+ private $max_cache_attempts = 5; // per-article
function about() {
return array(1.0,
@@ -17,18 +17,18 @@ class Cache_Starred_Images extends Plugin {
$this->host = $host;
$this->cache = new DiskCache("starred-images");
- if ($this->cache->makeDir())
- chmod($this->cache->getDir(), 0777);
+ if ($this->cache->make_dir())
+ chmod($this->cache->get_dir(), 0777);
if (!$this->cache->exists(".no-auto-expiry"))
$this->cache->touch(".no-auto-expiry");
- if ($this->cache->isWritable()) {
+ if ($this->cache->is_writable()) {
$host->add_hook($host::HOOK_HOUSE_KEEPING, $this);
$host->add_hook($host::HOOK_ENCLOSURE_ENTRY, $this);
$host->add_hook($host::HOOK_SANITIZE, $this);
} else {
- user_error("Starred cache directory ".$this->cache->getDir()." is not writable.", E_USER_WARNING);
+ user_error("Starred cache directory ".$this->cache->get_dir()." is not writable.", E_USER_WARNING);
}
}
@@ -38,13 +38,13 @@ class Cache_Starred_Images extends Plugin {
Debug::log("caching media of starred articles for user " . $this->host->get_owner_uid() . "...");
$sth = $this->pdo->prepare("SELECT content, ttrss_entries.title,
- ttrss_user_entries.owner_uid, link, site_url, ttrss_entries.id, plugin_data
+ ttrss_user_entries.owner_uid, link, site_url, ttrss_entries.id, plugin_data
FROM ttrss_entries, ttrss_user_entries LEFT JOIN ttrss_feeds ON
(ttrss_user_entries.feed_id = ttrss_feeds.id)
WHERE ref_id = ttrss_entries.id AND
marked = true AND
site_url != '' AND
- ttrss_user_entries.owner_uid = ? AND
+ ttrss_user_entries.owner_uid = ? AND
plugin_data NOT LIKE '%starred_cache_images%'
ORDER BY ".Db::sql_random_function()." LIMIT 100");
@@ -59,7 +59,7 @@ class Cache_Starred_Images extends Plugin {
$success = $this->cache_article_images($line["content"], $line["site_url"], $line["owner_uid"], $line["id"]);
if ($success) {
- $plugin_data = "starred_cache_images,${line['owner_uid']}:" . $line["plugin_data"];
+ $plugin_data = "starred_cache_images," . $line["owner_uid"] . ":" . $line["plugin_data"];
$usth->execute([$plugin_data, $line['id']]);
}
@@ -69,9 +69,12 @@ class Cache_Starred_Images extends Plugin {
/* actual housekeeping */
- Debug::log("expiring " . $this->cache->getDir() . "...");
+ Debug::log("expiring " . $this->cache->get_dir() . "...");
- $files = glob($this->cache->getDir() . "/*.{png,mp4,status}", GLOB_BRACE);
+ $files = array_merge(
+ glob($this->cache->get_dir() . "/*.png"),
+ glob($this->cache->get_dir() . "/*.mp4"),
+ glob($this->cache->get_dir() . "/*.status"));
$last_article_id = 0;
$article_exists = 1;
@@ -98,14 +101,14 @@ class Cache_Starred_Images extends Plugin {
$local_filename = $article_id . "-" . sha1($enc["content_url"]);
if ($this->cache->exists($local_filename)) {
- $enc["content_url"] = $this->cache->getUrl($local_filename);
+ $enc["content_url"] = $this->cache->get_url($local_filename);
}
return $enc;
}
function hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes, $article_id) {
- $xpath = new DOMXpath($doc);
+ $xpath = new DOMXPath($doc);
if ($article_id) {
$entries = $xpath->query('(//img[@src])|(//video/source[@src])');
@@ -117,7 +120,7 @@ class Cache_Starred_Images extends Plugin {
$local_filename = $article_id . "-" . sha1($src);
if ($this->cache->exists($local_filename)) {
- $entry->setAttribute("src", $this->cache->getUrl($local_filename));
+ $entry->setAttribute("src", $this->cache->get_url($local_filename));
$entry->removeAttribute("srcset");
}
}
@@ -133,7 +136,7 @@ class Cache_Starred_Images extends Plugin {
if (!$this->cache->exists($local_filename)) {
Debug::log("cache_images: downloading: $url to $local_filename", Debug::$LOG_VERBOSE);
- $data = UrlHelper::fetch(["url" => $url, "max_size" => MAX_CACHE_FILE_SIZE]);
+ $data = UrlHelper::fetch(["url" => $url, "max_size" => Config::get(Config::MAX_CACHE_FILE_SIZE)]);
if ($data)
return $this->cache->put($local_filename, $data);;
@@ -151,37 +154,37 @@ class Cache_Starred_Images extends Plugin {
$status_filename = $article_id . "-" . sha1($site_url) . ".status";
/* housekeeping might run as a separate user, in this case status/media might not be writable */
- if (!$this->cache->isWritable($status_filename)) {
+ if (!$this->cache->is_writable($status_filename)) {
Debug::log("status not writable: $status_filename", Debug::$LOG_VERBOSE);
return false;
}
Debug::log("status: $status_filename", Debug::$LOG_VERBOSE);
- if ($this->cache->exists($status_filename))
- $status = json_decode($this->cache->get($status_filename), true);
- else
- $status = [];
+ if ($this->cache->exists($status_filename))
+ $status = json_decode($this->cache->get($status_filename), true);
+ else
+ $status = ["attempt" => 0];
- $status["attempt"] += 1;
+ $status["attempt"] += 1;
- // only allow several download attempts for article
- if ($status["attempt"] > $this->max_cache_attempts) {
- Debug::log("too many attempts for $site_url", Debug::$LOG_VERBOSE);
- return false;
- }
+ // only allow several download attempts for article
+ if ($status["attempt"] > $this->max_cache_attempts) {
+ Debug::log("too many attempts for $site_url", Debug::$LOG_VERBOSE);
+ return false;
+ }
- if (!$this->cache->put($status_filename, json_encode($status))) {
- user_error("unable to write status file: $status_filename", E_USER_WARNING);
- return false;
- }
+ if (!$this->cache->put($status_filename, json_encode($status))) {
+ user_error("unable to write status file: $status_filename", E_USER_WARNING);
+ return false;
+ }
$doc = new DOMDocument();
$has_images = false;
$success = false;
- if (@$doc->loadHTML('<?xml encoding="UTF-8">' . $content)) {
+ if (@$doc->loadHTML('<?xml encoding="UTF-8">' . $content)) {
$xpath = new DOMXPath($doc);
$entries = $xpath->query('(//img[@src])|(//video/source[@src])');
@@ -203,11 +206,11 @@ class Cache_Starred_Images extends Plugin {
$esth = $this->pdo->prepare("SELECT content_url FROM ttrss_enclosures WHERE post_id = ? AND
(content_type LIKE '%image%' OR content_type LIKE '%video%')");
- if ($esth->execute([$article_id])) {
- while ($enc = $esth->fetch()) {
+ if ($esth->execute([$article_id])) {
+ while ($enc = $esth->fetch()) {
- $has_images = true;
- $url = rewrite_relative_url($site_url, $enc["content_url"]);
+ $has_images = true;
+ $url = rewrite_relative_url($site_url, $enc["content_url"]);
if ($this->cache_url($article_id, $url)) {
$success = true;
diff --git a/plugins/close_button/init.php b/plugins/close_button/init.php
index a2ba89478..4f33d1af0 100644
--- a/plugins/close_button/init.php
+++ b/plugins/close_button/init.php
@@ -15,7 +15,7 @@ class Close_Button extends Plugin {
}
function get_css() {
- return "i.icon-close-article { color : red; }";
+ return ".post .header .buttons i.material-icons.icon-close-article { color : red; }";
}
function hook_article_button($line) {
diff --git a/plugins/mail/init.php b/plugins/mail/init.php
index 40d147fc9..467f8294a 100644
--- a/plugins/mail/init.php
+++ b/plugins/mail/init.php
@@ -15,10 +15,15 @@ class Mail extends Plugin {
$host->add_hook($host::HOOK_ARTICLE_BUTTON, $this);
$host->add_hook($host::HOOK_PREFS_TAB, $this);
+ $host->add_hook($host::HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM, $this);
}
function get_js() {
- return file_get_contents(dirname(__FILE__) . "/mail.js");
+ return file_get_contents(__DIR__ . "/mail.js");
+ }
+
+ function hook_headline_toolbar_select_menu_item($feed_id, $is_cat) {
+ return "<div dojoType='dijit.MenuItem' onclick='Plugins.Mail.send()'>".__('Forward by email')."</div>";
}
function save() {
@@ -32,42 +37,38 @@ class Mail extends Plugin {
function hook_prefs_tab($args) {
if ($args != "prefPrefs") return;
- print "<div dojoType=\"dijit.layout.AccordionPane\"
- title=\"<i class='material-icons'>mail</i> ".__('Mail plugin')."\">";
+ $addresslist = $this->host->get($this, "addresslist");
- print "<p>" . __("You can set predefined email addressed here (comma-separated list):") . "</p>";
+ ?>
- print "<form dojoType=\"dijit.form.Form\">";
+ <div dojoType="dijit.layout.AccordionPane"
+ title="<i class='material-icons'>mail</i> <?= __('Mail plugin') ?>">
- print "<script type=\"dojo/method\" event=\"onSubmit\" args=\"evt\">
- evt.preventDefault();
- if (this.validate()) {
- console.log(dojo.objectToQuery(this.getValues()));
- new Ajax.Request('backend.php', {
- parameters: dojo.objectToQuery(this.getValues()),
- onComplete: function(transport) {
- Notify.info(transport.responseText);
- }
- });
- //this.reset();
- }
- </script>";
+ <form dojoType="dijit.form.Form">
+ <?= \Controls\pluginhandler_tags($this, "save") ?>
- print_hidden("op", "pluginhandler");
- print_hidden("method", "save");
- print_hidden("plugin", "mail");
+ <script type="dojo/method" event="onSubmit" args="evt">
+ evt.preventDefault();
+ if (this.validate()) {
+ Notify.progress('Saving data...', true);
+ xhr.post("backend.php", this.getValues(), (reply) => {
+ Notify.info(reply);
+ })
+ }
+ </script>
- $addresslist = $this->host->get($this, "addresslist");
+ <header><?= __("You can set predefined email addressed here (comma-separated list):") ?></header>
- print "<textarea dojoType=\"dijit.form.SimpleTextarea\" style='font-size : 12px; width : 50%' rows=\"3\"
- name='addresslist'>$addresslist</textarea>";
+ <textarea dojoType="dijit.form.SimpleTextarea" style='font-size : 12px; width : 50%' rows="3"
+ name='addresslist'><?= $addresslist ?></textarea>
- print "<p><button dojoType=\"dijit.form.Button\" type=\"submit\">".
- __("Save")."</button>";
+ <hr/>
- print "</form>";
+ <?= \Controls\submit_tag(__("Save")) ?>
- print "</div>";
+ </form>
+ </div>
+ <?php
}
function hook_article_button($line) {
@@ -78,12 +79,9 @@ class Mail extends Plugin {
function emailArticle() {
- $ids = explode(",", $_REQUEST['param']);
+ $ids = explode(",", clean($_REQUEST['ids']));
$ids_qmarks = arr_qmarks($ids);
- print_hidden("op", "pluginhandler");
- print_hidden("plugin", "mail");
- print_hidden("method", "sendEmail");
$sth = $this->pdo->prepare("SELECT email, full_name FROM ttrss_users WHERE
id = ?");
@@ -100,9 +98,6 @@ class Mail extends Plugin {
if (!$user_name)
$user_name = $_SESSION['name'];
- print_hidden("from_email", "$user_email");
- print_hidden("from_name", "$user_name");
-
$tpl = new Templator();
$tpl->readTemplateFromFile("email_article_template.txt");
@@ -143,46 +138,56 @@ class Mail extends Plugin {
$content = "";
$tpl->generateOutputToString($content);
- print "<table width='100%'><tr><td>";
-
$addresslist = explode(",", $this->host->get($this, "addresslist"));
- print __('To:');
-
- print "</td><td>";
-
-/* print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"true\"
- style=\"width : 30em;\"
- name=\"destination\" id=\"emailArticleDlg_destination\">"; */
-
- print_select("destination", "", $addresslist, 'style="width: 30em" dojoType="dijit.form.ComboBox"');
-
-/* print "<div class=\"autocomplete\" id=\"emailArticleDlg_dst_choices\"
- style=\"z-index: 30; display : none\"></div>"; */
-
- print "</td></tr><tr><td>";
-
- print __('Subject:');
-
- print "</td><td>";
-
- print "<input dojoType='dijit.form.ValidationTextBox' required='true'
- style='width : 30em;' name='subject' value=\"$subject\" id='subject'>";
-
- print "</td></tr>";
-
- print "<tr><td colspan='2'><textarea dojoType='dijit.form.SimpleTextarea'
- style='height : 200px; font-size : 12px; width : 98%' rows=\"20\"
- name='content'>$content</textarea>";
-
- print "</td></tr></table>";
-
- print "<footer>";
- print "<button dojoType='dijit.form.Button' onclick=\"dijit.byId('emailArticleDlg').execute()\">".__('Send e-mail')."</button> ";
- print "<button dojoType='dijit.form.Button' onclick=\"dijit.byId('emailArticleDlg').hide()\">".__('Cancel')."</button>";
- print "</footer>";
-
- //return;
+ ?>
+
+ <form dojoType='dijit.form.Form'>
+
+ <?= \Controls\pluginhandler_tags($this, "sendemail") ?>
+
+ <?= \Controls\hidden_tag("from_email", $user_email) ?>
+ <?= \Controls\hidden_tag("from_name", $user_name) ?>
+
+ <script type='dojo/method' event='onSubmit' args='evt'>
+ evt.preventDefault();
+ if (this.validate()) {
+ xhr.json("backend.php", this.getValues(), (reply) => {
+ if (reply && reply.error)
+ Notify.error(reply.error);
+ else
+ this.hide();
+ });
+ }
+ </script>
+
+ <section>
+ <fieldset class='narrow'>
+ <label><?= __('To:') ?></label>
+ <?= \Controls\select_tag("destination", "", $addresslist,
+ ["style" => "width: 380px", "required" => 1, "dojoType" => "dijit.form.ComboBox"]) ?>
+ </fieldset>
+ </section>
+
+ <section>
+ <fieldset class='narrow'>
+ <label><?= __('Subject:') ?></label>
+ <input dojoType='dijit.form.ValidationTextBox' required='true'
+ style='width : 380px' name='subject' value="<?= htmlspecialchars($subject) ?>" id='subject'>
+ </fieldset>
+ </section>
+
+ <textarea dojoType='dijit.form.SimpleTextarea'
+ style='height : 200px; font-size : 12px; width : 98%' rows="20"
+ name='content'><?= $content ?></textarea>
+
+ <footer>
+ <?= \Controls\submit_tag(__('Send email')) ?>
+ <?= \Controls\cancel_dialog_tag(__('Cancel')) ?>
+ </footer>
+
+ </form>
+ <?php
}
function sendEmail() {
@@ -223,20 +228,6 @@ class Mail extends Plugin {
print json_encode($reply);
}
- /* function completeEmails() {
- $search = $_REQUEST["search"];
-
- print "<ul>";
-
- foreach ($_SESSION['stored_emails'] as $email) {
- if (strpos($email, $search) !== false) {
- print "<li>$email</li>";
- }
- }
-
- print "</ul>";
- } */
-
function api_version() {
return 2;
}
diff --git a/plugins/mail/mail.js b/plugins/mail/mail.js
index 5ddc0dc41..d2bafe0e9 100644
--- a/plugins/mail/mail.js
+++ b/plugins/mail/mail.js
@@ -1,4 +1,4 @@
-/* global Plugins, Headlines, xhrJson, Notify, fox, __ */
+/* global Plugins, Headlines, dojo, App, xhr, Notify, fox, __ */
Plugins.Mail = {
send: function(id) {
@@ -13,14 +13,11 @@ Plugins.Mail = {
id = ids.toString();
}
- const query = "backend.php?op=pluginhandler&plugin=mail&method=emailArticle&param=" + encodeURIComponent(id);
-
const dialog = new fox.SingleUseDialog({
- id: "emailArticleDlg",
title: __("Forward article by email"),
execute: function () {
if (this.validate()) {
- xhrJson("backend.php", this.attr('value'), (reply) => {
+ xhr.json("backend.php", this.attr('value'), (reply) => {
if (reply) {
const error = reply['error'];
@@ -35,16 +32,16 @@ Plugins.Mail = {
});
}
},
- href: query
+ content: __("Loading, please wait...")
});
- /* var tmph = dojo.connect(dialog, 'onLoad', function() {
- dojo.disconnect(tmph);
+ const tmph = dojo.connect(dialog, 'onShow', function () {
+ dojo.disconnect(tmph);
- new Ajax.Autocompleter('emailArticleDlg_destination', 'emailArticleDlg_dst_choices',
- "backend.php?op=pluginhandler&plugin=mail&method=completeEmails",
- { tokens: '', paramName: "search" });
- }); */
+ xhr.post("backend.php", App.getPhArgs("mail", "emailArticle", {ids: id}), (reply) => {
+ dialog.attr('content', reply);
+ });
+ });
dialog.show();
},
diff --git a/plugins/mail/mail.png b/plugins/mail/mail.png
deleted file mode 100644
index 7348aed77..000000000
--- a/plugins/mail/mail.png
+++ /dev/null
Binary files differ
diff --git a/plugins/mailto/init.js b/plugins/mailto/init.js
index ae68bf49b..4a9557249 100644
--- a/plugins/mailto/init.js
+++ b/plugins/mailto/init.js
@@ -1,4 +1,4 @@
-/* global Plugins, Headlines, fox, __ */
+/* global Plugins, Headlines, xhr, dojo, fox, __ */
Plugins.Mailto = {
send: function (id) {
@@ -13,12 +13,19 @@ Plugins.Mailto = {
id = ids.toString();
}
- const query = "backend.php?op=pluginhandler&plugin=mailto&method=emailArticle&param=" + encodeURIComponent(id);
-
const dialog = new fox.SingleUseDialog({
- id: "emailArticleDlg",
- title: __("Forward article by email"),
- href: query});
+ title: __("Forward article by email (mailto:)"),
+ content: __("Loading, please wait...")
+ });
+
+ const tmph = dojo.connect(dialog, 'onShow', function () {
+ dojo.disconnect(tmph);
+
+ xhr.post("backend.php", App.getPhArgs("mailto", "emailArticle", {ids: id}), (reply) => {
+ dialog.attr('content', reply);
+ });
+ });
+
dialog.show();
}
diff --git a/plugins/mailto/init.php b/plugins/mailto/init.php
index 390984b71..c34b400ce 100644
--- a/plugins/mailto/init.php
+++ b/plugins/mailto/init.php
@@ -12,21 +12,26 @@ class MailTo extends Plugin {
$this->host = $host;
$host->add_hook($host::HOOK_ARTICLE_BUTTON, $this);
+ $host->add_hook($host::HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM, $this);
+ }
+
+ function hook_headline_toolbar_select_menu_item($feed_id, $is_cat) {
+ return "<div dojoType='dijit.MenuItem' onclick='Plugins.Mailto.send()'>".__('Forward by email (mailto:)')."</div>";
}
function get_js() {
- return file_get_contents(dirname(__FILE__) . "/init.js");
+ return file_get_contents(__DIR__ . "/init.js");
}
function hook_article_button($line) {
return "<i class='material-icons' style=\"cursor : pointer\"
onclick=\"Plugins.Mailto.send(".$line["id"].")\"
- title='".__('Forward by email')."'>mail_outline</i>";
+ title='".__('Forward by email (mailto:)')."'>mail_outline</i>";
}
function emailArticle() {
- $ids = explode(",", $_REQUEST['param']);
+ $ids = explode(",", clean($_REQUEST['ids']));
$ids_qmarks = arr_qmarks($ids);
$tpl = new Templator();
@@ -37,7 +42,6 @@ class MailTo extends Plugin {
//$tpl->setVariable('USER_EMAIL', $user_email, true);
$tpl->setVariable('TTRSS_HOST', $_SERVER["HTTP_HOST"], true);
-
$sth = $this->pdo->prepare("SELECT DISTINCT link, content, title
FROM ttrss_user_entries, ttrss_entries WHERE id = ref_id AND
id IN ($ids_qmarks) AND owner_uid = ?");
@@ -65,25 +69,23 @@ class MailTo extends Plugin {
$content = "";
$tpl->generateOutputToString($content);
- $mailto_link = htmlspecialchars("mailto:?subject=".rawurlencode($subject).
- "&body=".rawurlencode($content));
-
- print __("Clicking the following link to invoke your mail client:");
-
- print "<div class='panel text-center'>";
- print "<a target=\"_blank\" href=\"$mailto_link\">".
- __("Forward selected article(s) by email.")."</a>";
- print "</div>";
+ $mailto_link = "mailto:?subject=".rawurlencode($subject)."&body=".rawurlencode($content);
- print __("You should be able to edit the message before sending in your mail client.");
+ ?>
- print "<p>";
+ <section>
+ <div class='panel text-center'>
+ <a target="_blank" href="<?= htmlspecialchars($mailto_link) ?>">
+ <?= __("Click to open your mail client") ?>
+ </a>
+ </div>
+ </section>
- print "<footer class='text-center'>";
- print "<button dojoType='dijit.form.Button' onclick=\"dijit.byId('emailArticleDlg').hide()\">".__('Close this dialog')."</button>";
- print "</footer>";
+ <footer class='text-center'>
+ <?= \Controls\submit_tag(__('Close this dialog')) ?>
+ </footer>
- //return;
+ <?php
}
function api_version() {
diff --git a/plugins/mailto/mail.png b/plugins/mailto/mail.png
deleted file mode 100644
index 2c49f78a6..000000000
--- a/plugins/mailto/mail.png
+++ /dev/null
Binary files differ
diff --git a/plugins/note/init.php b/plugins/note/init.php
index 3c2ca0075..52f7be3eb 100644
--- a/plugins/note/init.php
+++ b/plugins/note/init.php
@@ -17,7 +17,7 @@ class Note extends Plugin {
}
function get_js() {
- return file_get_contents(dirname(__FILE__) . "/note.js");
+ return file_get_contents(__DIR__ . "/note.js");
}
@@ -27,48 +27,42 @@ class Note extends Plugin {
}
function edit() {
- $param = $_REQUEST['param'];
+ $id = clean($_REQUEST['id']);
$sth = $this->pdo->prepare("SELECT note FROM ttrss_user_entries WHERE
ref_id = ? AND owner_uid = ?");
- $sth->execute([$param, $_SESSION['uid']]);
+ $sth->execute([$id, $_SESSION['uid']]);
if ($row = $sth->fetch()) {
$note = $row['note'];
- print_hidden("id", "$param");
- print_hidden("op", "pluginhandler");
- print_hidden("method", "setNote");
- print_hidden("plugin", "note");
+ print \Controls\hidden_tag("id", $id);
+ print \Controls\pluginhandler_tags($this, "setnote");
- print "<textarea dojoType='dijit.form.SimpleTextarea'
+ ?>
+ <textarea dojoType='dijit.form.SimpleTextarea'
style='font-size : 12px; width : 98%; height: 100px;'
- name='note'>$note</textarea>";
-
+ name='note'><?= $note ?></textarea>
+ <?php
}
-
- print "<footer class='text-center'>";
- print "<button dojoType=\"dijit.form.Button\"
- onclick=\"dijit.byId('editNoteDlg').execute()\">".__('Save')."</button> ";
- print "<button dojoType=\"dijit.form.Button\"
- onclick=\"dijit.byId('editNoteDlg').hide()\">".__('Cancel')."</button>";
- print "</footer>";
-
+ ?>
+ <footer class='text-center'>
+ <?= \Controls\submit_tag(__('Save')) ?>
+ <?= \Controls\cancel_dialog_tag(__('Cancel')) ?>
+ </footer>
+ <?php
}
function setNote() {
- $id = $_REQUEST["id"];
- $note = trim(strip_tags($_REQUEST["note"]));
+ $id = (int)clean($_REQUEST["id"]);
+ $note = clean($_REQUEST["note"]);
$sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET note = ?
WHERE ref_id = ? AND owner_uid = ?");
$sth->execute([$note, $id, $_SESSION['uid']]);
- $formatted_note = Article::format_article_note($id, $note);
-
- print json_encode(array("note" => $formatted_note,
- "raw_length" => mb_strlen($note)));
+ print json_encode(["id" => $id, "note" => $note]);
}
function api_version() {
diff --git a/plugins/note/note.js b/plugins/note/note.js
index ab2ed9208..a46acb355 100644
--- a/plugins/note/note.js
+++ b/plugins/note/note.js
@@ -1,36 +1,39 @@
-/* global Plugins, xhrJson, Notify, fox, __ */
+/* global dojo, Plugins, xhr, App, Notify, fox, __ */
Plugins.Note = {
edit: function(id) {
- const query = "backend.php?op=pluginhandler&plugin=note&method=edit&param=" + encodeURIComponent(id);
-
const dialog = new fox.SingleUseDialog({
- id: "editNoteDlg",
title: __("Edit article note"),
execute: function () {
if (this.validate()) {
Notify.progress("Saving article note...", true);
- xhrJson("backend.php", this.attr('value'), (reply) => {
+ xhr.json("backend.php", this.attr('value'), (reply) => {
Notify.close();
dialog.hide();
if (reply) {
- const elem = $("POSTNOTE-" + id);
-
- if (elem) {
- elem.innerHTML = reply.note;
+ App.findAll(`div[data-note-for="${reply.id}"]`).forEach((elem) => {
+ elem.querySelector(".body").innerHTML = reply.note;
- if (reply.raw_length != 0)
- Element.show(elem);
+ if (reply.note)
+ elem.show();
else
- Element.hide(elem);
- }
+ elem.hide();
+ });
}
});
}
},
- href: query,
+ content: __("Loading, please wait...")
+ });
+
+ const tmph = dojo.connect(dialog, 'onShow', function () {
+ dojo.disconnect(tmph);
+
+ xhr.post("backend.php", App.getPhArgs("note", "edit", {id: id}), (reply) => {
+ dialog.attr('content', reply);
+ });
});
dialog.show();
diff --git a/plugins/nsfw/init.js b/plugins/nsfw/init.js
index adb6d43c0..4bc2443e8 100644
--- a/plugins/nsfw/init.js
+++ b/plugins/nsfw/init.js
@@ -1,7 +1,12 @@
-function nsfwShow(elem) {
- let content = elem.parentNode.getElementsBySelector("div.nswf.content")[0];
+/* global Plugins */
- if (content) {
- Element.toggle(content);
+Plugins.NSFW = {
+ toggle: function(elem) {
+ const content = elem.domNode.parentNode.querySelector(".nswf.content");
+
+ if (content) {
+ Element.toggle(content);
+ }
}
}
+
diff --git a/plugins/nsfw/init.php b/plugins/nsfw/init.php
index 02344eb14..7c5b8d00f 100644
--- a/plugins/nsfw/init.php
+++ b/plugins/nsfw/init.php
@@ -19,7 +19,7 @@ class NSFW extends Plugin {
}
function get_js() {
- return file_get_contents(dirname(__FILE__) . "/init.js");
+ return file_get_contents(__DIR__ . "/init.js");
}
function hook_render_article($article) {
@@ -27,74 +27,60 @@ class NSFW extends Plugin {
$a_tags = array_map("trim", explode(",", $article["tag_cache"]));
if (count(array_intersect($tags, $a_tags)) > 0) {
- $article["content"] = "<div class='nswf wrapper'><button onclick=\"nsfwShow(this)\">".__("Not work safe (click to toggle)")."</button>
- <div class='nswf content' style='display : none'>".$article["content"]."</div></div>";
+ $article["content"] = "<div class='nswf wrapper'>".
+ \Controls\button_tag(__("Not work safe (click to toggle)"), '', ['onclick' => 'Plugins.NSFW.toggle(this)']).
+ "<div class='nswf content' style='display : none'>".$article["content"]."</div>
+ </div>";
}
return $article;
}
function hook_render_article_cdm($article) {
- $tags = array_map("trim", explode(",", $this->host->get($this, "tags")));
- $a_tags = array_map("trim", explode(",", $article["tag_cache"]));
-
- if (count(array_intersect($tags, $a_tags)) > 0) {
- $article["content"] = "<div class='nswf wrapper'><button onclick=\"nsfwShow(this)\">".__("Not work safe (click to toggle)")."</button>
- <div class='nswf content' style='display : none'>".$article["content"]."</div></div>";
- }
-
- return $article;
+ return $this->hook_render_article($article);
}
function hook_prefs_tab($args) {
if ($args != "prefPrefs") return;
- print "<div dojoType=\"dijit.layout.AccordionPane\"
- title=\"<i class='material-icons'>extension</i> ".__("NSFW Plugin")."\">";
-
- print "<br/>";
-
$tags = $this->host->get($this, "tags");
- print "<form dojoType=\"dijit.form.Form\">";
-
- print "<script type=\"dojo/method\" event=\"onSubmit\" args=\"evt\">
- evt.preventDefault();
- if (this.validate()) {
- new Ajax.Request('backend.php', {
- parameters: dojo.objectToQuery(this.getValues()),
- onComplete: function(transport) {
- Notify.info(transport.responseText);
- }
- });
- //this.reset();
- }
- </script>";
-
- print_hidden("op", "pluginhandler");
- print_hidden("method", "save");
- print_hidden("plugin", "nsfw");
+ ?>
+ <div dojoType="dijit.layout.AccordionPane"
+ title="<i class='material-icons'>extension</i> <?= __("NSFW Plugin") ?>">
+ <form dojoType="dijit.form.Form">
- print "<table width=\"100%\" class=\"prefPrefsList\">";
+ <?= \Controls\pluginhandler_tags($this, "save") ?>
- print "<tr><td width=\"40%\">".__("Tags to consider NSFW (comma-separated)")."</td>";
- print "<td class=\"prefValue\"><input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\" name=\"tags\" value=\"$tags\"></td></tr>";
+ <script type="dojo/method" event="onSubmit" args="evt">
+ evt.preventDefault();
+ if (this.validate()) {
+ Notify.progress('Saving data...', true);
+ xhr.post("backend.php", this.getValues(), (reply) => {
+ Notify.info(reply);
+ })
+ }
+ </script>
- print "</table>";
+ <header><?= __("Tags to consider NSFW (comma-separated):") ?></header>
- print "<p><button dojoType=\"dijit.form.Button\" type=\"submit\">".
- __("Save")."</button>";
+ <fieldset>
+ <textarea dojoType='dijit.form.SimpleTextarea' rows='4'
+ style='width: 500px; font-size : 12px;'
+ name='tags'><?= $tags ?></textarea>
+ </fieldset>
- print "</form>";
+ <hr/>
- print "</div>"; #pane
+ <?= \Controls\submit_tag(__("Save")) ?>
+ </form>
+ </div>
+ <?php
}
function save() {
- $tags = explode(",", $_POST["tags"]);
- $tags = array_map("trim", $tags);
- $tags = array_map("mb_strtolower", $tags);
- $tags = join(", ", $tags);
+ $tags = implode(", ",
+ FeedItem_Common::normalize_categories(explode(",", $_POST["tags"] ?? "")));
$this->host->set($this, "tags", $tags);
diff --git a/plugins/share/init.php b/plugins/share/init.php
index 0794f5125..37799fba6 100644
--- a/plugins/share/init.php
+++ b/plugins/share/init.php
@@ -16,19 +16,22 @@ 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(dirname(__FILE__) . "/share.js");
+ return file_get_contents(__DIR__ . "/share.js");
}
function get_css() {
- return file_get_contents(dirname(__FILE__) . "/share.css");
+ return file_get_contents(__DIR__ . "/share.css");
}
function get_prefs_js() {
- return file_get_contents(dirname(__FILE__) . "/share_prefs.js");
+ return file_get_contents(__DIR__ . "/share_prefs.js");
}
-
function unshare() {
$id = $_REQUEST['id'];
@@ -36,32 +39,30 @@ class Share extends Plugin {
AND owner_uid = ?");
$sth->execute([$id, $_SESSION['uid']]);
- print "OK";
+ print __("Article unshared");
}
function hook_prefs_tab_section($id) {
if ($id == "prefFeedsPublishedGenerated") {
+ ?>
+ <hr/>
- print "<h3>" . __("You can disable all articles shared by unique URLs here.") . "</h3>";
-
- print "<button class='alt-danger' dojoType='dijit.form.Button' onclick=\"return Plugins.Share.clearKeys()\">".
- __('Unshare all articles')."</button> ";
-
- print "</p>";
+ <h2><?= __("You can disable all articles shared by unique URLs here.") ?></h2>
+ <button class='alt-danger' dojoType='dijit.form.Button' onclick="return Plugins.Share.clearKeys()">
+ <?= __('Unshare all articles') ?></button>
+ <?php
}
}
- // Silent
function clearArticleKeys() {
$sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET uuid = '' WHERE
owner_uid = ?");
$sth->execute([$_SESSION['uid']]);
- return;
+ print __("Shared URLs cleared.");
}
-
function newkey() {
$id = $_REQUEST['id'];
$uuid = uniqid_short();
@@ -70,26 +71,169 @@ class Share extends Plugin {
AND owner_uid = ?");
$sth->execute([$uuid, $id, $_SESSION['uid']]);
- print json_encode(array("link" => $uuid));
+ print json_encode(["link" => $uuid]);
}
function hook_article_button($line) {
- $img_class = $line['uuid'] ? "shared" : "";
+ $icon_class = !empty($line['uuid']) ? "is-shared" : "";
- return "<i id='SHARE-IMG-".$line['int_id']."' class='material-icons icon-share $img_class'
+ return "<i class='material-icons icon-share share-icon-".$line['int_id']." $icon_class'
style='cursor : pointer' onclick=\"Plugins.Share.shareArticle(".$line['int_id'].")\"
title='".__('Share by URL')."'>link</i>";
}
- function shareArticle() {
- $param = $_REQUEST['param'];
+ 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"];
+
+ $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]);
+
+ 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);
+
+ $enclosures = Article::_get_enclosures($line["id"]);
+ list ($og_image, $og_stream) = Article::_get_image($enclosures, $line['content'], $line["site_url"]);
+
+ $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']);
+
+ ob_start();
+
+ ?>
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
+ <title><?= $line["title"] ?></title>
+ <?= javascript_tag("js/common.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'>
+
+ <meta property='og:title' content="<?= htmlspecialchars($content_decoded) ?>">
+ <meta property='og:description' content="<?= htmlspecialchars(
+ truncate_string(
+ preg_replace("/[\r\n\t]/", "",
+ preg_replace("/ {1,}/", " ",
+ strip_tags($content_decoded)
+ )
+ ), 500, "...")) ?>">
+ </head>
+
+ <?php if ($og_image) { ?>
+ <meta property='og:image' content="<?= htmlspecialchars($og_image) ?>">
+ <?php } ?>
+
+ <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'>
+ <div><?= $line['author'] ?></div>
+ <div><?= $parsed_updated ?></div>
+ </div>
+ </div>
+
+ <div class='content' lang="<?= $line['lang'] ? $line['lang'] : "en" ?>">
+ <?= $line["content"] ?>
+ </div>
+ </div>
+ </body>
+ </html>
+ <?php
+
+ $rv = ob_get_contents();
+ ob_end_clean();
+
+ PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_FORMAT_ARTICLE,
+ function ($result) use (&$rv) {
+ $rv = $result;
+ },
+ $rv, $line);
+
+ print $rv;
+ }
+ }
+
+ function shareDialog() {
+ $id = (int)clean($_REQUEST['id'] ?? 0);
$sth = $this->pdo->prepare("SELECT uuid FROM ttrss_user_entries WHERE int_id = ?
AND owner_uid = ?");
- $sth->execute([$param, $_SESSION['uid']]);
+ $sth->execute([$id, $_SESSION['uid']]);
if ($row = $sth->fetch()) {
-
$uuid = $row['uuid'];
if (!$uuid) {
@@ -97,42 +241,34 @@ class Share extends Plugin {
$sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET uuid = ? WHERE int_id = ?
AND owner_uid = ?");
- $sth->execute([$uuid, $param, $_SESSION['uid']]);
+ $sth->execute([$uuid, $id, $_SESSION['uid']]);
}
- print "<header>" . __("You can share this article by the following unique URL:") . "</header>";
+ $url_path = $this->host->get_public_method_url($this, "get", ["key" => $uuid]);
+ ?>
- $url_path = get_self_url_prefix();
- $url_path .= "/public.php?op=share&key=$uuid";
+ <header><?= __("You can share this article by the following unique URL:") ?></header>
- print "<section>
+ <section>
<div class='panel text-center'>
- <a id='gen_article_url' href='$url_path' target='_blank' rel='noopener noreferrer'>$url_path</a>
+ <a class='target-url' href="<?= htmlspecialchars($url_path) ?>"
+ target='_blank' rel='noopener noreferrer'><?= htmlspecialchars($url_path) ?></a>
</div>
- </section>";
-
- /* if (!label_find_id(__('Shared'), $_SESSION["uid"]))
- label_create(__('Shared'), $_SESSION["uid"]);
-
- label_add_article($ref_id, __('Shared'), $_SESSION['uid']); */
+ </section>
+ <?php
} else {
- print "Article not found.";
+ print format_error(__("Article not found."));
}
- print "<footer class='text-center'>";
-
- print "<button dojoType='dijit.form.Button' onclick=\"return App.dialogOf(this).unshare()\">".
- __('Unshare article')."</button>";
-
- print "<button dojoType='dijit.form.Button' onclick=\"return App.dialogOf(this).newurl()\">".
- __('Generate new URL')."</button>";
-
- print "<button dojoType='dijit.form.Button' type='submit' class='alt-primary'>".
- __('Close this window')."</button>";
-
- print "</footer>";
+ ?>
+ <footer class='text-center'>
+ <?= \Controls\button_tag(__('Unshare article'), '', ['class' => 'alt-danger', 'onclick' => "App.dialogOf(this).unshare()"]) ?>
+ <?= \Controls\button_tag(__('Generate new URL'), '', ['onclick' => "App.dialogOf(this).newurl()"]) ?>
+ <?= \Controls\submit_tag(__("Close this window")) ?>
+ </footer>
+ <?php
}
function api_version() {
diff --git a/plugins/share/share.css b/plugins/share/share.css
index 00bad68dd..ac9247a54 100644
--- a/plugins/share/share.css
+++ b/plugins/share/share.css
@@ -1,3 +1,3 @@
-i.icon-share.shared {
+i.material-icons.icon-share.is-shared {
color : #0a0;
} \ No newline at end of file
diff --git a/plugins/share/share.js b/plugins/share/share.js
index 3fc42d654..1be9db682 100644
--- a/plugins/share/share.js
+++ b/plugins/share/share.js
@@ -1,9 +1,7 @@
-/* global Plugins, xhrJson, Notify, fox, xhrPost, __ */
+/* global dojo, Plugins, App, Notify, fox, xhr, __ */
Plugins.Share = {
shareArticle: function(id) {
- const query = "backend.php?op=pluginhandler&plugin=share&method=shareArticle&param=" + encodeURIComponent(id);
-
const dialog = new fox.SingleUseDialog({
id: "shareArticleDlg",
title: __("Share article by URL"),
@@ -12,25 +10,23 @@ Plugins.Share = {
Notify.progress("Trying to change URL...", true);
- const query = {op: "pluginhandler", plugin: "share", method: "newkey", id: id};
-
- xhrJson("backend.php", query, (reply) => {
+ xhr.json("backend.php", App.getPhArgs("share", "newkey", {id: id}), (reply) => {
if (reply) {
const new_link = reply.link;
- const e = $('gen_article_url');
+ const target = dialog.domNode.querySelector(".target-url");
- if (new_link) {
+ if (new_link && target) {
- e.innerHTML = e.innerHTML.replace(/\&amp;key=.*$/,
+ target.innerHTML = target.innerHTML.replace(/&amp;key=.*$/,
"&amp;key=" + new_link);
- e.href = e.href.replace(/\&key=.*$/,
+ target.href = target.href.replace(/&key=.*$/,
"&key=" + new_link);
- new Effect.Highlight(e);
+ const icon = document.querySelector(".share-icon-" + id);
- const img = $("SHARE-IMG-" + id);
- img.addClassName("shared");
+ if (icon)
+ icon.addClassName("is-shared");
Notify.close();
@@ -44,32 +40,35 @@ Plugins.Share = {
},
unshare: function () {
if (confirm(__("Remove sharing for this article?"))) {
+ xhr.post("backend.php", App.getPhArgs("share", "unshare", {id: id}), (reply) => {
+ Notify.info(reply);
- const query = {op: "pluginhandler", plugin: "share", method: "unshare", id: id};
-
- xhrPost("backend.php", query, () => {
- try {
- const img = $("SHARE-IMG-" + id);
+ const icon = document.querySelector(".share-icon-" + id);
- if (img) {
- img.removeClassName("shared");
- img.up("div[id*=RROW]").removeClassName("shared");
- }
+ if (icon)
+ icon.removeClassName("is-shared");
- dialog.hide();
- } catch (e) {
- console.error(e);
- }
+ dialog.hide();
});
}
},
- href: query
+ content: __("Loading, please wait...")
});
- dialog.show();
+ const tmph = dojo.connect(dialog, 'onShow', function () {
+ dojo.disconnect(tmph);
- const img = $("SHARE-IMG-" + id);
- img.addClassName("shared");
+ xhr.post("backend.php", App.getPhArgs("share", "shareDialog", {id: id}), (reply) => {
+ dialog.attr('content', reply)
+
+ const icon = document.querySelector(".share-icon-" + id);
+
+ if (icon)
+ icon.addClassName("is-shared");
+ });
+ });
+
+ dialog.show();
}
}
diff --git a/plugins/share/share_prefs.js b/plugins/share/share_prefs.js
index 071a6667c..d974af618 100644
--- a/plugins/share/share_prefs.js
+++ b/plugins/share/share_prefs.js
@@ -1,12 +1,12 @@
+/* global Plugins, Notify, xhr, App */
+
Plugins.Share = {
clearKeys: function() {
if (confirm(__("This will invalidate all previously shared article URLs. Continue?"))) {
Notify.progress("Clearing URLs...");
- const query = {op: "pluginhandler", plugin: "share", method: "clearArticleKeys"};
-
- xhrPost("backend.php", query, () => {
- Notify.info("Shared URLs cleared.");
+ xhr.post("backend.php", App.getPhArgs("share", "clearArticleKeys"), (reply) => {
+ Notify.info(reply);
});
}
diff --git a/plugins/shorten_expanded/init.js b/plugins/shorten_expanded/init.js
index 30bfac6ba..0abc8c129 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 = {
@@ -5,8 +7,8 @@ Plugins.Shorten_Expanded = {
const row = $(id);
if (row) {
- const content = row.select(".content-shrink-wrap")[0];
- const link = row.select(".expand-prompt")[0];
+ const content = row.querySelector(".content-shrink-wrap");
+ const link = row.querySelector(".expand-prompt");
if (content) content.removeClassName("content-shrink-wrap");
if (link) Element.hide(link);
@@ -22,26 +24,26 @@ 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");
- if (c_inner && c_inter &&
- row.offsetHeight >= _shorten_expanded_threshold * window.innerHeight) {
+ //console.log('shorten', row.offsetHeight, 'vs', _shorten_expanded_threshold * window.innerHeight);
- let tmp = document.createElement("div");
+ if (content && row.offsetHeight >= _shorten_expanded_threshold * window.innerHeight) {
- c_inter.select("> *:not([class*='attachments'])").each(function(p) {
- p.parentNode.removeChild(p);
- tmp.appendChild(p);
- });
+ const attachments = row.querySelector(".attachments-inline"); // optional
- c_inner.innerHTML = `<div class="content-shrink-wrap">
- ${c_inner.innerHTML}
- ${tmp.innerHTML}</div>
+ 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>`;
- dojo.parser.parse(c_inner);
+ if (attachments)
+ attachments.innerHTML = "";
+
+ dojo.parser.parse(content);
}
}
}, 150);
diff --git a/plugins/toggle_sidebar/init.php b/plugins/toggle_sidebar/init.php
index f8ec35a91..19ca960e2 100644
--- a/plugins/toggle_sidebar/init.php
+++ b/plugins/toggle_sidebar/init.php
@@ -24,7 +24,7 @@ class Toggle_Sidebar extends Plugin {
<button dojoType="dijit.form.Button" onclick="Plugins.Toggle_Sidebar.toggle(this)">
<i class="material-icons toggle-sidebar-label"
- title="<?php echo __('Toggle sidebar') ?>">chevron_left</i>
+ title="<?= __('Toggle sidebar') ?>">chevron_left</i>
</button>
<?php
diff --git a/plugins/vf_shared/init.php b/plugins/vf_shared/init.php
index 8c38cbf32..1112f6f2f 100644
--- a/plugins/vf_shared/init.php
+++ b/plugins/vf_shared/init.php
@@ -60,7 +60,7 @@ class VF_Shared extends Plugin {
"override_vfeed" => "ttrss_feeds.title AS feed_title,"
);
- $qfh_ret = Feeds::queryFeedHeadlines($params);
+ $qfh_ret = Feeds::_get_headlines($params);
$qfh_ret[1] = __("Shared articles");
return $qfh_ret;