From f2d3cba2316a39e3d27e2e93e52562e72e7bd99d Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 12 Feb 2021 21:20:04 +0300 Subject: add HTTP_ACCEPT_LANGUAGE handling for php8 --- include/functions.php | 78 +++++++++++++------ include/sessions.php | 1 - lib/accept-to-gettext.php | 186 ---------------------------------------------- 3 files changed, 55 insertions(+), 210 deletions(-) delete mode 100644 lib/accept-to-gettext.php diff --git a/include/functions.php b/include/functions.php index f870f3382..6362adbbe 100644 --- a/include/functions.php +++ b/include/functions.php @@ -91,14 +91,8 @@ define('SUBSTRING_FOR_DATE', 'SUBSTRING'); } - /** - * Return available translations names. - * - * @access public - * @return array A array of available translations. - */ function get_translations() { - $tr = array( + $t = array( "auto" => __("Detect automatically"), "ar_SA" => "العربيّة (Arabic)", "bg_BG" => "Bulgarian", @@ -129,38 +123,76 @@ "fi_FI" => "Suomi", "tr_TR" => "Türkçe"); - return $tr; + return $t; } - require_once "lib/accept-to-gettext.php"; require_once "lib/gettext/gettext.inc.php"; function startup_gettext() { - # Get locale from Accept-Language header - if (version_compare(PHP_VERSION, '8.0.0', '<')) { - $lang = al2gt(array_keys(get_translations()), "text/html"); - } else { - $lang = ""; // FIXME: do something with accept-to-gettext.php - } + $selected_locale = ""; + + // https://www.codingwithjesse.com/blog/use-accept-language-header/ + if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { + $valid_langs = []; + $translations = array_keys(get_translations()); + + array_shift($translations); // remove "auto" + + // full locale first + foreach ($translations as $t) { + $lang = strtolower(str_replace("_", "-", (string)$t)); + $valid_langs[$lang] = $t; + + $lang = substr($lang, 0, 2); + if (!isset($valid_langs[$lang])) + $valid_langs[$lang] = $t; + } - if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) { - $lang = _TRANSLATION_OVERRIDE_DEFAULT; + // break up string into pieces (languages and q factors) + preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', + $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse); + + if (count($lang_parse[1])) { + // create a list like "en" => 0.8 + $langs = array_combine($lang_parse[1], $lang_parse[4]); + + if (is_array($langs)) { + // set default to 1 for any without q factor + foreach ($langs as $lang => $val) { + if ($val === '') $langs[$lang] = 1; + } + + // sort list based on value + arsort($langs, SORT_NUMERIC); + + foreach (array_keys($langs) as $lang) { + $lang = strtolower($lang); + + foreach ($valid_langs as $vlang => $vlocale) { + if ($vlang == $lang) { + $selected_locale = $vlocale; + break 2; + } + } + } + } + } } if (!empty($_SESSION["uid"]) && get_schema_version() >= 120) { - $pref_lang = get_pref("USER_LANGUAGE", $_SESSION["uid"]); + $pref_locale = get_pref("USER_LANGUAGE", $_SESSION["uid"]); - if ($pref_lang && $pref_lang != 'auto') { - $lang = $pref_lang; + if (!empty($pref_locale) && $pref_locale != 'auto') { + $selected_locale = $pref_locale; } } - if ($lang) { + if ($selected_locale) { if (defined('LC_MESSAGES')) { - _setlocale(LC_MESSAGES, $lang); + _setlocale(LC_MESSAGES, $selected_locale); } else if (defined('LC_ALL')) { - _setlocale(LC_ALL, $lang); + _setlocale(LC_ALL, $selected_locale); } _bindtextdomain("messages", "locale"); diff --git a/include/sessions.php b/include/sessions.php index d7dde782e..3119a4e07 100644 --- a/include/sessions.php +++ b/include/sessions.php @@ -5,7 +5,6 @@ require_once "classes/db.php"; require_once "autoload.php"; require_once "errorhandler.php"; - require_once "lib/accept-to-gettext.php"; require_once "lib/gettext/gettext.inc.php"; $session_expire = min(2147483647 - time() - 1, max(SESSION_COOKIE_LIFETIME, 86400)); diff --git a/lib/accept-to-gettext.php b/lib/accept-to-gettext.php deleted file mode 100644 index c86a62b2e..000000000 --- a/lib/accept-to-gettext.php +++ /dev/null @@ -1,186 +0,0 @@ - - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Usage: - * - * $locale=al2gt(, - * ); - * setlocale('LC_ALL', $locale); // or 'LC_MESSAGES', or whatever... - * - * Example: - * - * $langs=array('nl_BE.ISO-8859-15','nl_BE.UTF-8','en_US.UTF-8','en_GB.UTF-8'); - * $locale=al2gt($langs, 'text/html'); - * setlocale('LC_ALL', $locale); - * - * Note that this will send out header information (to be - * RFC2616-compliant), so it must be called before anything is sent to - * the user. - * - * Assumptions made: - * * Charset encodings are written the same way as the Accept-Charset - * HTTP header specifies them (RFC2616), except that they're parsed - * case-insensitive. - * * Country codes and language codes are the same in both gettext and - * the Accept-Language syntax (except for the case differences, which - * are dealt with easily). If not, some input may be ignored. - * * The provided gettext-strings are fully qualified; i.e., no "en_US"; - * always "en_US.ISO-8859-15" or "en_US.UTF-8", or whichever has been - * used. "en.ISO-8859-15" is OK, though. - * * The language is more important than the charset; i.e., if the - * following is given: - * - * Accept-Language: nl-be, nl;q=0.8, en-us;q=0.5, en;q=0.3 - * Accept-Charset: ISO-8859-15, utf-8;q=0.5 - * - * And the supplied parameter contains (amongst others) nl_BE.UTF-8 - * and nl.ISO-8859-15, then nl_BE.UTF-8 will be picked. - * - * $Log: accept-to-gettext.inc,v $ - * Revision 1.1.1.1 2003/11/19 19:31:15 wouter - * * moved to new CVS repo after death of the old - * * Fixed code to apply a default to both Accept-Charset and - * Accept-Language if none of those headers are supplied; patch from - * Dominic Chambers - * - * Revision 1.2 2003/08/14 10:23:59 wouter - * Removed little error in Content-Type header syntaxis. - * - * 2007-04-01 - * add '@' before use of arrays, to avoid PHP warnings. - */ - -/* not really important, this one; perhaps I could've put it inline with - * the rest. */ -function find_match($curlscore,$curcscore,$curgtlang,$langval,$charval, - $gtlang) -{ - if($curlscore < $langval) { - $curlscore=$langval; - $curcscore=$charval; - $curgtlang=$gtlang; - } else if ($curlscore == $langval) { - if($curcscore < $charval) { - $curcscore=$charval; - $curgtlang=$gtlang; - } - } - return array($curlscore, $curcscore, $curgtlang); -} - -function al2gt($gettextlangs, $mime) { - /* default to "everything is acceptable", as RFC2616 specifies */ - $acceptLang=(($_SERVER["HTTP_ACCEPT_LANGUAGE"] == '') ? '*' : - $_SERVER["HTTP_ACCEPT_LANGUAGE"]); - $acceptChar=(($_SERVER["HTTP_ACCEPT_CHARSET"] == '') ? '*' : - $_SERVER["HTTP_ACCEPT_CHARSET"]); - $alparts=@preg_split("/,/",$acceptLang); - $acparts=@preg_split("/,/",$acceptChar); - - /* Parse the contents of the Accept-Language header.*/ - foreach($alparts as $part) { - $part=trim($part); - if(preg_match("/;/", $part)) { - $lang=@preg_split("/;/",$part); - $score=@preg_split("/=/",$lang[1]); - $alscores[$lang[0]]=$score[1]; - } else { - $alscores[$part]=1; - } - } - - /* Do the same for the Accept-Charset header. */ - - /* RFC2616: ``If no "*" is present in an Accept-Charset field, then - * all character sets not explicitly mentioned get a quality value of - * 0, except for ISO-8859-1, which gets a quality value of 1 if not - * explicitly mentioned.'' - * - * Making it 2 for the time being, so that we - * can distinguish between "not specified" and "specified as 1" later - * on. */ - $acscores["ISO-8859-1"]=2; - - foreach($acparts as $part) { - $part=trim($part); - if(preg_match("/;/", $part)) { - $cs=@preg_split("/;/",$part); - $score=@preg_split("/=/",$cs[1]); - $acscores[strtoupper($cs[0])]=$score[1]; - } else { - $acscores[strtoupper($part)]=1; - } - } - if($acscores["ISO-8859-1"]==2) { - $acscores["ISO-8859-1"]=(isset($acscores["*"])?$acscores["*"]:1); - } - - /* - * Loop through the available languages/encodings, and pick the one - * with the highest score, excluding the ones with a charset the user - * did not include. - */ - $curlscore=0; - $curcscore=0; - $curgtlang=NULL; - foreach($gettextlangs as $gtlang) { - - $tmp1=preg_replace("/\_/","-",$gtlang); - $tmp2=@preg_split("/\./",$tmp1); - $allang=strtolower($tmp2[0]); - $gtcs=strtoupper($tmp2[1]); - $noct=@preg_split("/-/",$allang); - - $testvals=array( - array(@$alscores[$allang], @$acscores[$gtcs]), - array(@$alscores[$noct[0]], @$acscores[$gtcs]), - array(@$alscores[$allang], @$acscores["*"]), - array(@$alscores[$noct[0]], @$acscores["*"]), - array(@$alscores["*"], @$acscores[$gtcs]), - array(@$alscores["*"], @$acscores["*"])); - - $found=FALSE; - foreach($testvals as $tval) { - if(!$found && isset($tval[0]) && isset($tval[1])) { - $arr=find_match($curlscore, $curcscore, $curgtlang, $tval[0], - $tval[1], $gtlang); - $curlscore=$arr[0]; - $curcscore=$arr[1]; - $curgtlang=$arr[2]; - $found=TRUE; - } - } - } - - /* We must re-parse the gettext-string now, since we may have found it - * through a "*" qualifier.*/ - - $gtparts=@preg_split("/\./",$curgtlang); - $tmp=strtolower($gtparts[0]); - $lang=preg_replace("/\_/", "-", $tmp); - $charset=$gtparts[1]; - - header("Content-Language: $lang"); - header("Content-Type: $mime; charset=$charset"); - - return $curgtlang; -} - -?> -- cgit v1.2.3 From 119a4226d812918733a815a896cfed8380188c15 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 12 Feb 2021 21:21:23 +0300 Subject: validate_csrf: remove warning --- include/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/functions.php b/include/functions.php index 6362adbbe..eaf7d8243 100644 --- a/include/functions.php +++ b/include/functions.php @@ -310,7 +310,7 @@ } function validate_csrf($csrf_token) { - return isset($csrf_token) && hash_equals($_SESSION['csrf_token'], $csrf_token); + return isset($csrf_token) && hash_equals($_SESSION['csrf_token'] ?? "", $csrf_token); } function truncate_string($str, $max_len, $suffix = '…') { -- cgit v1.2.3 From e6624cf631f772f2a1eac9412e7a96d6545e91e6 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 12 Feb 2021 21:24:49 +0300 Subject: fix a few more session-related warnings --- backend.php | 2 +- classes/handler/protected.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend.php b/backend.php index dec79f46f..2ea396987 100644 --- a/backend.php +++ b/backend.php @@ -46,7 +46,7 @@ UserHelper::authenticate( "admin", null); } - if ($_SESSION["uid"]) { + if (!empty($_SESSION["uid"])) { if (!validate_session()) { header("Content-Type: text/json"); print error_json(6); diff --git a/classes/handler/protected.php b/classes/handler/protected.php index 765b17480..8e9e5ca1d 100644 --- a/classes/handler/protected.php +++ b/classes/handler/protected.php @@ -2,6 +2,6 @@ class Handler_Protected extends Handler { function before($method) { - return parent::before($method) && $_SESSION['uid']; + return parent::before($method) && !empty($_SESSION['uid']); } } -- cgit v1.2.3 From 6af83e3881b3f38104027275913f7fc55251d020 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 12 Feb 2021 21:43:38 +0300 Subject: drop ENABLE_GZIP_OUTPUT; system prefs: load php info only if needed --- api/index.php | 8 +------- backend.php | 4 ---- classes/pref/system.php | 20 +++++++++++++------- config.php-dist | 7 ------- include/sanity_config.php | 4 ++-- js/PrefHelpers.js | 7 +++++++ public.php | 4 ---- 7 files changed, 23 insertions(+), 31 deletions(-) diff --git a/api/index.php b/api/index.php index 77552af46..9e998df84 100644 --- a/api/index.php +++ b/api/index.php @@ -22,13 +22,7 @@ ini_set('session.use_cookies', 0); ini_set("session.gc_maxlifetime", 86400); - if (defined('ENABLE_GZIP_OUTPUT') && ENABLE_GZIP_OUTPUT && - function_exists("ob_gzhandler")) { - - ob_start("ob_gzhandler"); - } else { - ob_start(); - } + ob_start(); $input = file_get_contents("php://input"); diff --git a/backend.php b/backend.php index 2ea396987..030676dcb 100644 --- a/backend.php +++ b/backend.php @@ -38,10 +38,6 @@ header("Content-Type: text/json; charset=utf-8"); - if (ENABLE_GZIP_OUTPUT && function_exists("ob_gzhandler")) { - ob_start("ob_gzhandler"); - } - if (SINGLE_USER_MODE) { UserHelper::authenticate( "admin", null); } diff --git a/classes/pref/system.php b/classes/pref/system.php index d91339698..a7512915a 100644 --- a/classes/pref/system.php +++ b/classes/pref/system.php @@ -25,6 +25,15 @@ class Pref_System extends Handler_Protected { $this->pdo->query("DELETE FROM ttrss_error_log"); } + function getphpinfo() { + ob_start(); + phpinfo(); + $info = ob_get_contents(); + ob_end_clean(); + + print preg_replace( '%^.*(.*).*$%ms','$1', $info); + } + private function log_viewer(int $page, int $severity) { $errno_values = []; @@ -167,14 +176,11 @@ class Pref_System extends Handler_Protected { print "
"; - ob_start(); - phpinfo(); - $info = ob_get_contents(); - ob_end_clean(); + print ""; - print "
"; - print preg_replace( '%^.*(.*).*$%ms','$1', $info); - print "
"; + print "
" . __("Loading, please wait...") . "
"; print "
"; # accordion pane diff --git a/config.php-dist b/config.php-dist index cd0ee0078..2ee1c719d 100644 --- a/config.php-dist +++ b/config.php-dist @@ -122,13 +122,6 @@ define('CHECK_FOR_UPDATES', true); // Check for updates automatically if running Git version - define('ENABLE_GZIP_OUTPUT', false); - // Selectively gzip output to improve wire performance. This requires - // PHP Zlib extension on the server. - // Enabling this can break tt-rss in several httpd/php configurations, - // if you experience weird errors and tt-rss failing to start, blank pages - // after login, or content encoding errors, disable it. - define('PLUGINS', 'auth_internal, note'); // Comma-separated list of plugins to load automatically for all users. // System plugins have to be specified here. Please enable at least one diff --git a/include/sanity_config.php b/include/sanity_config.php index 7aa4f6b0f..5059ee83b 100644 --- a/include/sanity_config.php +++ b/include/sanity_config.php @@ -1,3 +1,3 @@ - +$required_defines = array( 'DB_TYPE', 'DB_HOST', 'DB_USER', 'DB_NAME', 'DB_PASS', 'MYSQL_CHARSET', 'SELF_URL_PATH', 'SINGLE_USER_MODE', 'SIMPLE_UPDATE_MODE', 'PHP_EXECUTABLE', 'LOCK_DIRECTORY', 'CACHE_DIR', 'ICONS_DIR', 'ICONS_URL', 'AUTH_AUTO_CREATE', 'AUTH_AUTO_LOGIN', 'FORCE_ARTICLE_PURGE', 'SESSION_COOKIE_LIFETIME', 'SMTP_FROM_NAME', 'SMTP_FROM_ADDRESS', 'DIGEST_SUBJECT', 'CHECK_FOR_UPDATES', 'PLUGINS', 'LOG_DESTINATION', 'CONFIG_VERSION'); ?> diff --git a/js/PrefHelpers.js b/js/PrefHelpers.js index 5bb76d179..3b9e985a6 100644 --- a/js/PrefHelpers.js +++ b/js/PrefHelpers.js @@ -51,6 +51,13 @@ const Helpers = { return false; }, + System: { + getPHPInfo: function(widget) { + xhrPost("backend.php", {op: 'pref-system', method: 'getphpinfo'}, (transport) => { + widget.attr('content', transport.responseText); + }); + } + }, EventLog: { log_page: 0, refresh: function() { diff --git a/public.php b/public.php index 36308e25e..3e4a9e023 100644 --- a/public.php +++ b/public.php @@ -16,10 +16,6 @@ if (!init_plugins()) return; - if (ENABLE_GZIP_OUTPUT && function_exists("ob_gzhandler")) { - ob_start("ob_gzhandler"); - } - $method = $_REQUEST["op"]; $override = PluginHost::getInstance()->lookup_handler("public", $method); -- cgit v1.2.3 From 481bd7610059cfa09282a03e1a9c8dbc213f4819 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 12 Feb 2021 21:51:32 +0300 Subject: pref helpers: move some methods to their own sections --- classes/pref/feeds.php | 2 +- classes/pref/prefs.php | 8 +- js/PrefHelpers.js | 270 +++++++++++++++++++++++++------------------------ 3 files changed, 143 insertions(+), 137 deletions(-) diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index 47e5689ec..ff9e69336 100755 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -1394,7 +1394,7 @@ class Pref_Feeds extends Handler_Protected { print " - "; PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefFeedsPublishedGenerated"); diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index d40dc87c0..cfe63ce85 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -715,7 +715,7 @@ class Pref_Prefs extends Handler_Protected { print ""; print " "; + onclick=\"Helpers.Prefs.customizeCSS()\">" . __('Customize') . ""; print " "; @@ -830,10 +830,10 @@ class Pref_Prefs extends Handler_Protected { "; - print " "; - print ""; print " "; @@ -968,7 +968,7 @@ class Pref_Prefs extends Handler_Protected { if (count($tmppluginhost->get_all($plugin)) > 0) { if (in_array($name, $system_enabled) || in_array($name, $user_enabled)) { print " "; } } diff --git a/js/PrefHelpers.js b/js/PrefHelpers.js index 3b9e985a6..b09beb995 100644 --- a/js/PrefHelpers.js +++ b/js/PrefHelpers.js @@ -40,16 +40,18 @@ const Helpers = { } }, }, - clearFeedAccessKeys: function() { - if (confirm(__("This will invalidate all previously generated feed URLs. Continue?"))) { - Notify.progress("Clearing URLs..."); + Feeds: { + clearFeedAccessKeys: function() { + if (confirm(__("This will invalidate all previously generated feed URLs. Continue?"))) { + Notify.progress("Clearing URLs..."); - xhrPost("backend.php", {op: "pref-feeds", method: "clearKeys"}, () => { - Notify.info("Generated URLs cleared."); - }); - } + xhrPost("backend.php", {op: "pref-feeds", method: "clearKeys"}, () => { + Notify.info("Generated URLs cleared."); + }); + } - return false; + return false; + }, }, System: { getPHPInfo: function(widget) { @@ -90,151 +92,155 @@ const Helpers = { } }, }, - editProfiles: function() { - const dialog = new fox.SingleUseDialog({ - id: "profileEditDlg", - title: __("Settings Profiles"), - getSelectedProfiles: function () { - return Tables.getSelected("pref-profiles-list"); - }, - removeSelected: function () { - const sel_rows = this.getSelectedProfiles(); - - if (sel_rows.length > 0) { - if (confirm(__("Remove selected profiles? Active and default profiles will not be removed."))) { - Notify.progress("Removing selected profiles...", true); - - const query = { - op: "rpc", method: "remprofiles", - ids: sel_rows.toString() - }; + Profiles: { + edit: function() { + const dialog = new fox.SingleUseDialog({ + id: "profileEditDlg", + title: __("Settings Profiles"), + getSelectedProfiles: function () { + return Tables.getSelected("pref-profiles-list"); + }, + removeSelected: function () { + const sel_rows = this.getSelectedProfiles(); + + if (sel_rows.length > 0) { + if (confirm(__("Remove selected profiles? Active and default profiles will not be removed."))) { + Notify.progress("Removing selected profiles...", true); + + const query = { + op: "rpc", method: "remprofiles", + ids: sel_rows.toString() + }; + + xhrPost("backend.php", query, () => { + Notify.close(); + dialog.refresh(); + }); + } + + } else { + alert(__("No profiles selected.")); + } + }, + addProfile: function () { + if (this.validate()) { + Notify.progress("Creating profile...", true); + + const query = {op: "rpc", method: "addprofile", title: dialog.attr('value').newprofile}; xhrPost("backend.php", query, () => { Notify.close(); dialog.refresh(); }); - } - - } else { - alert(__("No profiles selected.")); - } - }, - addProfile: function () { - if (this.validate()) { - Notify.progress("Creating profile...", true); - - const query = {op: "rpc", method: "addprofile", title: dialog.attr('value').newprofile}; - xhrPost("backend.php", query, () => { - Notify.close(); - dialog.refresh(); + } + }, + refresh: function() { + xhrPost("backend.php", {op: 'pref-prefs', method: 'editPrefProfiles'}, (transport) => { + dialog.attr('content', transport.responseText); }); + }, + execute: function () { + const sel_rows = this.getSelectedProfiles(); - } - }, - refresh: function() { - xhrPost("backend.php", {op: 'pref-prefs', method: 'editPrefProfiles'}, (transport) => { - dialog.attr('content', transport.responseText); - }); - }, - execute: function () { - const sel_rows = this.getSelectedProfiles(); + if (sel_rows.length == 1) { + if (confirm(__("Activate selected profile?"))) { + Notify.progress("Loading, please wait..."); - if (sel_rows.length == 1) { - if (confirm(__("Activate selected profile?"))) { - Notify.progress("Loading, please wait..."); + xhrPost("backend.php", {op: "rpc", method: "setprofile", id: sel_rows.toString()}, () => { + window.location.reload(); + }); + } - xhrPost("backend.php", {op: "rpc", method: "setprofile", id: sel_rows.toString()}, () => { - window.location.reload(); - }); + } else { + alert(__("Please choose a profile to activate.")); } + }, + content: "" + }); - } else { - alert(__("Please choose a profile to activate.")); - } - }, - content: "" - }); - - dialog.refresh(); - dialog.show(); + dialog.refresh(); + dialog.show(); + }, }, - customizeCSS: function() { - xhrJson("backend.php", {op: "pref-prefs", method: "customizeCSS"}, (reply) => { + Prefs: { + customizeCSS: function() { + xhrJson("backend.php", {op: "pref-prefs", method: "customizeCSS"}, (reply) => { + + const dialog = new fox.SingleUseDialog({ + title: __("Customize stylesheet"), + apply: function() { + xhrPost("backend.php", this.attr('value'), () => { + new Effect.Appear("css_edit_apply_msg"); + $("user_css_style").innerText = this.attr('value'); + }); + }, + execute: function () { + Notify.progress('Saving data...', true); - const dialog = new fox.SingleUseDialog({ - title: __("Customize stylesheet"), - apply: function() { - xhrPost("backend.php", this.attr('value'), () => { - new Effect.Appear("css_edit_apply_msg"); - $("user_css_style").innerText = this.attr('value'); - }); - }, - execute: function () { - Notify.progress('Saving data...', true); + xhrPost("backend.php", this.attr('value'), () => { + window.location.reload(); + }); + }, + content: ` +
+ ${__("You can override colors, fonts and layout of your currently selected theme with custom CSS declarations here.")} +
- xhrPost("backend.php", this.attr('value'), () => { - window.location.reload(); - }); - }, - content: ` -
- ${__("You can override colors, fonts and layout of your currently selected theme with custom CSS declarations here.")} -
- - ${App.FormFields.hidden('op', 'rpc')} - ${App.FormFields.hidden('method', 'setpref')} - ${App.FormFields.hidden('key', 'USER_STYLESHEET')} - - "; } + function get_metadata_by_id() { + $id = clean($_REQUEST['id']); + + $sth = $this->pdo->prepare("SELECT link, title FROM ttrss_entries, ttrss_user_entries + WHERE ref_id = ? AND ref_id = id AND owner_uid = ?"); + $sth->execute([$id, $_SESSION['uid']]); + + if ($row = $sth->fetch()) { + $link = $row['link']; + $title = $row['title']; + + echo json_encode(["link" => $link, "title" => $title]); + } + } + static function get_article_enclosures($id) { $pdo = Db::pdo(); diff --git a/classes/rpc.php b/classes/rpc.php index f8af1d660..f6b57775d 100755 --- a/classes/rpc.php +++ b/classes/rpc.php @@ -382,23 +382,6 @@ class RPC extends Handler_Protected { $sth->execute(array_merge($ids, [$_SESSION['uid']])); } - function getlinktitlebyid() { - $id = clean($_REQUEST['id']); - - $sth = $this->pdo->prepare("SELECT link, title FROM ttrss_entries, ttrss_user_entries - WHERE ref_id = ? AND ref_id = id AND owner_uid = ?"); - $sth->execute([$id, $_SESSION['uid']]); - - if ($row = $sth->fetch()) { - $link = $row['link']; - $title = $row['title']; - - echo json_encode(array("link" => $link, "title" => $title)); - } else { - echo json_encode(array("error" => "ARTICLE_NOT_FOUND")); - } - } - function log() { $msg = clean($_REQUEST['msg']); $file = basename(clean($_REQUEST['file'])); diff --git a/js/Article.js b/js/Article.js index 61368dfed..f8b0415b9 100644 --- a/js/Article.js +++ b/js/Article.js @@ -123,11 +123,13 @@ const Article = { Article.setActive(0); }, displayUrl: function (id) { - const query = {op: "rpc", method: "getlinktitlebyid", id: id}; + const query = {op: "article", method: "get_metadata_by_id", id: id}; xhrJson("backend.php", query, (reply) => { if (reply && reply.link) { prompt(__("Article URL:"), reply.link); + } else { + alert(__("No URL could be displayed for this article.")); } }); }, diff --git a/js/CommonFilters.js b/js/CommonFilters.js index 802cf478d..15403b8c4 100644 --- a/js/CommonFilters.js +++ b/js/CommonFilters.js @@ -332,7 +332,7 @@ const Filters = { } else { - const query = {op: "rpc", method: "getlinktitlebyid", id: Article.getActive()}; + const query = {op: "article", method: "get_metadata_by_id", id: Article.getActive()}; xhrPost("backend.php", query, (transport) => { const reply = JSON.parse(transport.responseText); -- cgit v1.2.3 From e7924c6dacef405a6e20b41d078f4a90a210cb51 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sat, 13 Feb 2021 13:17:34 +0300 Subject: label editor: use client dialog --- classes/pref/labels.php | 68 ++------------------ js/App.js | 4 +- js/PrefLabelTree.js | 167 ++++++++++++++++++++++++++++++++---------------- 3 files changed, 120 insertions(+), 119 deletions(-) diff --git a/classes/pref/labels.php b/classes/pref/labels.php index a787ce388..6102ab8dd 100644 --- a/classes/pref/labels.php +++ b/classes/pref/labels.php @@ -10,72 +10,14 @@ class Pref_Labels extends Handler_Protected { function edit() { $label_id = clean($_REQUEST['id']); - $sth = $this->pdo->prepare("SELECT * FROM ttrss_labels2 WHERE + $sth = $this->pdo->prepare("SELECT id, caption, fg_color, bg_color FROM ttrss_labels2 WHERE id = ? AND owner_uid = ?"); $sth->execute([$label_id, $_SESSION['uid']]); - if ($line = $sth->fetch()) { - - print_hidden("id", "$label_id"); - print_hidden("op", "pref-labels"); - print_hidden("method", "save"); - - print "
"; - - print "
".__("Caption")."
"; - - print "
"; - - $fg_color = $line['fg_color']; - $bg_color = $line['bg_color'] ? $line['bg_color'] : '#fff7d5'; - - print ""; - - print "
"; - - print "
" . __("Colors") . "
"; - print "
"; - - print ""; - print ""; - print "
".__("Foreground:")."".__("Background:")."
"; - - print ""; - print ""; - - print "
- -
"; - - print "
"; - - print "
- -
"; - - print "
"; - print "
"; - - print "
"; - print ""; - print ""; - print "
"; - - print "
"; + if ($line = $sth->fetch(PDO::FETCH_ASSOC)) { + print json_encode($line); + } else { + print json_encode(["error" => "LABEL_NOT_FOUND"]); } } diff --git a/js/App.js b/js/App.js index 514a6d4b7..4646145ea 100644 --- a/js/App.js +++ b/js/App.js @@ -18,8 +18,8 @@ const App = { is_prefs: false, LABEL_BASE_INDEX: -1024, FormFields: { - hidden: function(name, value) { - return `` + hidden: function(name, value, id = "") { + return `` } }, Scrollable: { diff --git a/js/PrefLabelTree.js b/js/PrefLabelTree.js index 73f375f2d..88e88b669 100644 --- a/js/PrefLabelTree.js +++ b/js/PrefLabelTree.js @@ -1,5 +1,5 @@ /* eslint-disable prefer-rest-params */ -/* global __, define, lib, dijit, dojo, xhrPost, Notify, fox */ +/* global __, define, lib, dijit, dojo, xhrPost, Notify, fox, App */ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dijit/form/DropDownButton"], function (declare, domConstruct) { @@ -61,70 +61,129 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dijit/f }); }, editLabel: function(id) { - const dialog = new fox.SingleUseDialog({ - id: "labelEditDlg", - title: __("Label Editor"), - style: "width: 650px", - setLabelColor: function (id, fg, bg) { - - let kind = ''; - let color = ''; - - if (fg && bg) { - kind = 'both'; - } else if (fg) { - kind = 'fg'; - color = fg; - } else if (bg) { - kind = 'bg'; - color = bg; - } - - const e = $("icon-label-" + id); - - if (e) { - if (bg) e.style.color = bg; - } + xhrJson("backend.php", {op: "pref-labels", method: "edit", id: id}, (reply) => { - const query = { - op: "pref-labels", method: "colorset", kind: kind, - ids: id, fg: fg, bg: bg, color: color - }; + console.log(reply); - xhrPost("backend.php", query, () => { - const tree = dijit.byId("filterTree"); - if (tree) tree.reload(); // maybe there's labels in there - }); + const fg_color = reply['fg_color']; + const bg_color = reply['bg_color'] ? reply['bg_color'] : '#fff7d5'; + + const dialog = new fox.SingleUseDialog({ + id: "labelEditDlg", + title: __("Label Editor"), + style: "width: 650px", + setLabelColor: function (id, fg, bg) { + + let kind = ''; + let color = ''; - }, - execute: function () { - if (this.validate()) { - const caption = this.attr('value').caption; - const fg_color = this.attr('value').fg_color; - const bg_color = this.attr('value').bg_color; + if (fg && bg) { + kind = 'both'; + } else if (fg) { + kind = 'fg'; + color = fg; + } else if (bg) { + kind = 'bg'; + color = bg; + } - dijit.byId('labelTree').setNameById(id, caption); - this.setLabelColor(id, fg_color, bg_color); - this.hide(); + const e = $("icon-label-" + id); - xhrPost("backend.php", this.attr('value'), () => { + if (e) { + if (bg) e.style.color = bg; + } + + const query = { + op: "pref-labels", method: "colorset", kind: kind, + ids: id, fg: fg, bg: bg, color: color + }; + + xhrPost("backend.php", query, () => { const tree = dijit.byId("filterTree"); if (tree) tree.reload(); // maybe there's labels in there }); - } - }, - content: __("Loading, please wait...") - }); - const tmph = dojo.connect(dialog, 'onShow', function () { - dojo.disconnect(tmph); + }, + execute: function () { + if (this.validate()) { + const caption = this.attr('value').caption; + const fg_color = this.attr('value').fg_color; + const bg_color = this.attr('value').bg_color; + + dijit.byId('labelTree').setNameById(id, caption); + this.setLabelColor(id, fg_color, bg_color); + this.hide(); + + xhrPost("backend.php", this.attr('value'), () => { + const tree = dijit.byId("filterTree"); + if (tree) tree.reload(); // maybe there's labels in there + }); + } + }, + content: ` +
+ +
${__("Caption")}
+
+ +
+ + ${App.FormFields.hidden('id', id)} + ${App.FormFields.hidden('op', 'pref-labels')} + ${App.FormFields.hidden('method', 'save')} + + ${App.FormFields.hidden('fg_color', fg_color, 'labelEdit_fgColor')} + ${App.FormFields.hidden('bg_color', bg_color, 'labelEdit_bgColor')} + +
${__("Colors")}
+
+ + + + + + + + + +
${__("Foreground:")}${__("Background:")}
+
+ +
+
+
+ +
+
+
+ +
+ + +
+ +
+ ` + }); - xhrPost("backend.php", {op: "pref-labels", method: "edit", id: id}, (transport) => { - dialog.attr('content', transport.responseText); - }) - }); + dialog.show(); - dialog.show(); + }); }, resetColors: function() { const labels = this.getSelectedLabels(); -- cgit v1.2.3 From 46f6d7c11a85f78ffaad649b7f591effff52706f Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sat, 13 Feb 2021 13:26:17 +0300 Subject: pref-labels/index: cleanup --- classes/pref/labels.php | 115 +++++++++++++++++++++++------------------------- 1 file changed, 56 insertions(+), 59 deletions(-) diff --git a/classes/pref/labels.php b/classes/pref/labels.php index 6102ab8dd..acaabb233 100644 --- a/classes/pref/labels.php +++ b/classes/pref/labels.php @@ -190,65 +190,62 @@ class Pref_Labels extends Handler_Protected { } function index() { - - print "
"; - print "
"; - print "
"; - - print "
". - "" . __('Select').""; - print "
"; - print "
".__('All')."
"; - print "
".__('None')."
"; - print "
"; - - print" "; - - print " "; - - print ""; - - - print "
"; #toolbar - print "
"; #pane - print "
"; - - print "
- ". - __("Loading, please wait...")."
"; - - print "
-
-
+ ?> +
+
+
+
+ +
+
+
+
+
+ + + + + + + +
+
+ +
+
+ +
+ +
+
+ +
+
+ +
+ + +
+
+ run_hooks(PluginHost::HOOK_PREFS_TAB, "prefLabels") ?>
-
- - -
"; - - print "
"; #pane - - PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB, "prefLabels"); - - print "
"; #container - + Date: Sat, 13 Feb 2021 13:37:57 +0300 Subject: pref-labels index: use cleaner markup --- classes/pref/system.php | 56 +++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/classes/pref/system.php b/classes/pref/system.php index a7512915a..763440d78 100644 --- a/classes/pref/system.php +++ b/classes/pref/system.php @@ -150,43 +150,35 @@ class Pref_System extends Handler_Protected { } print ""; + print "
"; + print ""; } function index() { $severity = (int) ($_REQUEST["severity"] ?? E_USER_WARNING); $page = (int) ($_REQUEST["page"] ?? 0); - - print "
"; - print "
"; - - if (LOG_DESTINATION == "sql") { - - $this->log_viewer($page, $severity); - - } else { - print_notice("Please set LOG_DESTINATION to 'sql' in config.php to enable database logging."); - } - - print "
"; # content pane - print "
"; # container - print ""; # accordion pane - - print "
"; - - print ""; - - print "
" . __("Loading, please wait...") . "
"; - - print "
"; # accordion pane - - PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB, "prefSystem"); - - print ""; #container + ?> +
+
'> + log_viewer($page, $severity); + } else { + print_notice("Please set LOG_DESTINATION to 'sql' in config.php to enable database logging."); + } + ?> +
+ +
'> + +
+
+ + run_hooks(PluginHost::HOOK_PREFS_TAB, "prefSystem") ?> +
+ Date: Sat, 13 Feb 2021 13:50:53 +0300 Subject: prefs system: markup cleanup --- classes/pref/labels.php | 2 +- classes/pref/system.php | 164 ++++++++++++++++++++++++------------------------ 2 files changed, 84 insertions(+), 82 deletions(-) diff --git a/classes/pref/labels.php b/classes/pref/labels.php index acaabb233..92acabd9e 100644 --- a/classes/pref/labels.php +++ b/classes/pref/labels.php @@ -139,7 +139,7 @@ class Pref_Labels extends Handler_Protected { $sth->execute([$caption, $old_caption, $_SESSION['uid']]); - print clean($_REQUEST["value"]); + print clean($_REQUEST["caption"]); } else { print $old_caption; } diff --git a/classes/pref/system.php b/classes/pref/system.php index 763440d78..994e024c7 100644 --- a/classes/pref/system.php +++ b/classes/pref/system.php @@ -31,7 +31,7 @@ class Pref_System extends Handler_Protected { $info = ob_get_contents(); ob_end_clean(); - print preg_replace( '%^.*(.*).*$%ms','$1', $info); + print preg_replace( '%^.*(.*).*$%ms','$1', (string)$info); } private function log_viewer(int $page, int $severity) { @@ -71,87 +71,89 @@ class Pref_System extends Handler_Protected { $total_pages = 0; } - print "
"; - - print "
"; - - print ""; - - $prev_page_disabled = $page <= 0 ? "disabled" : ""; - - print ""; - - print ""; - - $next_page_disabled = $page >= $total_pages ? "disabled" : ""; - - print ""; - - print ""; - - print "
"; - - print __("Severity:") . " "; - print_select_hash("severity", $severity, - [ - E_USER_ERROR => __("Errors"), - E_USER_WARNING => __("Warnings"), - E_USER_NOTICE => __("Everything") - ], 'dojoType="fox.form.Select" onchange="Helpers.EventLog.refresh()"'); - - print "
"; # pull-right - - print "
"; # toolbar - - print '
'; - - print ""; - - print " - - - - - - "; - - $sth = $this->pdo->prepare("SELECT - errno, errstr, filename, lineno, created_at, login, context - FROM - ttrss_error_log LEFT JOIN ttrss_users ON (owner_uid = ttrss_users.id) - WHERE - $errno_filter_qpart - ORDER BY - ttrss_error_log.id DESC - LIMIT $limit OFFSET $offset"); - - $sth->execute($errno_values); - - while ($line = $sth->fetch()) { - print ""; - - foreach ($line as $k => $v) { - $line[$k] = htmlspecialchars($v); - } - - print ""; - print ""; - print ""; - print ""; - - print ""; - - print ""; - } + ?> +
+
+ + + + + + + + + + + +
+ + + __("Errors"), + E_USER_WARNING => __("Warnings"), + E_USER_NOTICE => __("Everything") + ], 'dojoType="fox.form.Select" onchange="Helpers.EventLog.refresh()"') ?> +
+
- print "
".__("Error")."".__("Filename")."".__("Message")."".__("User")."".__("Date")."
" . Logger::$errornames[$line["errno"]] . " (" . $line["errno"] . ")" . $line["filename"] . ":" . $line["lineno"] . "" . $line["errstr"] . "\n" . $line["context"] . "" . - TimeHelper::make_local_datetime($line["created_at"], false) . "
"; - print "
"; - print "
"; +
+ + + + + + + + + + + + pdo->prepare("SELECT + errno, errstr, filename, lineno, created_at, login, context + FROM + ttrss_error_log LEFT JOIN ttrss_users ON (owner_uid = ttrss_users.id) + WHERE + $errno_filter_qpart + ORDER BY + ttrss_error_log.id DESC + LIMIT $limit OFFSET $offset"); + + $sth->execute($errno_values); + + while ($line = $sth->fetch()) { + foreach ($line as $k => $v) { $line[$k] = htmlspecialchars($v); } + ?> + + + + + + + + +
+ + + +
+
+ + Date: Sat, 13 Feb 2021 14:05:25 +0300 Subject: pref filters index: markup cleanup --- classes/pref/filters.php | 133 ++++++++++++++++++++++------------------------- 1 file changed, 61 insertions(+), 72 deletions(-) diff --git a/classes/pref/filters.php b/classes/pref/filters.php index a24a05b05..c80a10c64 100755 --- a/classes/pref/filters.php +++ b/classes/pref/filters.php @@ -710,81 +710,70 @@ class Pref_Filters extends Handler_Protected { $filter_search = ($_SESSION["prefs_filter_search"] ?? ""); } - print "
"; - print "
"; - print "
"; - - print "
- - -
"; - - print "
". - "" . __('Select').""; - print "
"; - print "
".__('All')."
"; - print "
".__('None')."
"; - print "
"; - - print " "; - - print " "; - - print " "; - - print " "; - - - print " "; - - print "
"; # toolbar - print "
"; # toolbar-frame - print "
"; + ?> +
+
+
+ +
+ + +
- print "
- ". - __("Loading, please wait...")."
"; +
+ +
+
+
+
+
- print "
-
-
+ + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + +
+
+ run_hooks(PluginHost::HOOK_PREFS_TAB, "prefFilters") ?>
-
- - - -
"; - - print "
"; #pane - - PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB, "prefFilters"); - - print "
"; #container - + Date: Sat, 13 Feb 2021 16:07:52 +0300 Subject: minor fixes re: previous --- classes/pref/filters.php | 2 +- classes/pref/system.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/pref/filters.php b/classes/pref/filters.php index c80a10c64..c83299678 100755 --- a/classes/pref/filters.php +++ b/classes/pref/filters.php @@ -717,7 +717,7 @@ class Pref_Filters extends Handler_Protected {
+ value="">
diff --git a/classes/pref/system.php b/classes/pref/system.php index 994e024c7..72e15e4f3 100644 --- a/classes/pref/system.php +++ b/classes/pref/system.php @@ -146,7 +146,7 @@ class Pref_System extends Handler_Protected { - + -- cgit v1.2.3 From 17413078a72e1298c6dc8953c40e8d83ce38c49c Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sat, 13 Feb 2021 18:32:02 +0300 Subject: pref feeds: index cleanup, split into several methods, use tabs to maximize space for feed tree, persist feed tree state --- classes/pref/feeds.php | 333 +++++++++++++++++++++++++---------------------- classes/pref/filters.php | 6 - classes/pref/labels.php | 7 - js/PrefFeedTree.js | 37 +++++- plugins/share/init.php | 6 +- themes/compact.css | 15 +-- themes/compact_night.css | 15 +-- themes/light.css | 15 +-- themes/light/prefs.less | 14 +- themes/night.css | 15 +-- themes/night_blue.css | 15 +-- 11 files changed, 235 insertions(+), 243 deletions(-) diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index ff9e69336..d8495a59c 100755 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -1197,12 +1197,7 @@ class Pref_Feeds extends Handler_Protected { $opml->opml_import($_SESSION["uid"]); } - function index() { - - print "
"; - print "
rss_feed ".__('Feeds')."\">"; - + private function index_feeds() { $sth = $this->pdo->prepare("SELECT COUNT(id) AS num_errors FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ?"); $sth->execute([$_SESSION['uid']]); @@ -1214,16 +1209,15 @@ class Pref_Feeds extends Handler_Protected { } if ($num_errors > 0) { - $error_button = ""; + $error_button = ""; } else { $error_button = ""; } - $inactive_button = ""; @@ -1235,175 +1229,202 @@ class Pref_Feeds extends Handler_Protected { $feed_search = $_SESSION["prefs_feed_search"] ?? ""; } - print '
'; - - print "
"; #toolbar - - print "
- - -
"; - - print "
". - "" . __('Select').""; - print "
"; - print "
".__('All')."
"; - print "
".__('None')."
"; - print "
"; - - print "
". - "" . __('Feeds').""; - print "
"; - print "
".__('Subscribe to feed')."
"; - print "
".__('Edit selected feeds')."
"; - print "
".__('Reset sort order')."
"; - print "
".__('Batch subscribe')."
"; - print "
" - .__('Unsubscribe')."
"; - print "
"; - - if (get_pref('ENABLE_FEED_CATS')) { - print "
". - "" . __('Categories').""; - print "
"; - print "
".__('Add category')."
"; - print "
".__('Reset sort order')."
"; - print "
".__('Remove selected')."
"; - print "
"; - - } - - print $error_button; - print $inactive_button; - - print "
"; # toolbar - - //print '
'; - print '
'; - - print "
- ". - __("Loading, please wait...")."
"; - - $auto_expand = $feed_search != "" ? "true" : "false"; - - print "
-
-
+ ?> + +
+
+
+ + +
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+
+
+
+
+
+
+ + +
+ +
+
+
+
+
+
+ + + +
+
+
+
+ +
+
+ +
" + persist="true" + model="feedModel" + openOnClick="false"> + + +
+
-
- - -
"; - -# print "
-# ".__('Hint: you can drag feeds and categories around.')." -#
"; + '; - print '
'; - - print "
"; # feeds pane + } - print "
"; + private function index_opml() { + ?> - print "

" . __("Using OPML you can export and import your feeds, filters, labels and Tiny Tiny RSS settings.") . "

"; +

- print_notice("Only main settings profile can be migrated using OPML."); + - print "
-
"; # pane - - print "
share ".__('Published & shared articles / Generated feeds')."\">"; - - print "

" . __('Published articles can be subscribed by anyone who knows the following URL:') . "

"; - + private function index_shared() { $rss_url = htmlspecialchars(get_self_url_prefix() . - "/public.php?op=rss&id=-2&view-mode=all_articles");; + "/public.php?op=rss&id=-2&view-mode=all_articles"); + ?> - print " - "; +

- PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefFeedsPublishedGenerated"); + - print "
"; #pane + - PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB, "prefFeeds"); + run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefFeedsPublishedGenerated"); + } - print "
"; #container + function index() { + ?> + +
+
+ index_feeds() ?> +
+ +
+ index_opml() ?> +
+ +
+ index_shared() ?> +
+ + run_hooks(PluginHost::HOOK_PREFS_TAB, "prefFeeds"); + $plugin_data = trim((string)ob_get_contents()); + ob_end_clean(); + ?> + + +
+ +
+ +
+
+ +
+
-
- -
@@ -758,9 +755,6 @@ class Pref_Filters extends Handler_Protected {
-
diff --git a/js/PrefFeedTree.js b/js/PrefFeedTree.js index e17b8744d..e0a2dd932 100644 --- a/js/PrefFeedTree.js +++ b/js/PrefFeedTree.js @@ -209,6 +209,13 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_b return false; }, + checkErrorFeeds: function() { + xhrJson("backend.php", {op: "pref-feeds", method: "feedsWithErrors"}, (reply) => { + if (reply.length > 0) { + Element.show(dijit.byId("pref_feeds_errors_btn").domNode); + } + }); + }, checkInactiveFeeds: function() { xhrJson("backend.php", {op: "pref-feeds", method: "inactivefeeds"}, (reply) => { if (reply.length > 0) { diff --git a/themes/compact.css b/themes/compact.css index f923b2ee1..36c5aec9f 100644 --- a/themes/compact.css +++ b/themes/compact.css @@ -1456,7 +1456,7 @@ body.ttrss_prefs #feedsTab { body.ttrss_prefs .dijitDialog #pref-profiles-list .dijitInlineEditBoxDisplayMode { padding: 0px; } -body.ttrss_prefs #errorButton { +body.ttrss_prefs #pref_feeds_errors_btn { color: red; } body.ttrss_prefs .user-css-editor { diff --git a/themes/compact_night.css b/themes/compact_night.css index e512e8176..e39b7020a 100644 --- a/themes/compact_night.css +++ b/themes/compact_night.css @@ -1456,7 +1456,7 @@ body.ttrss_prefs #feedsTab { body.ttrss_prefs .dijitDialog #pref-profiles-list .dijitInlineEditBoxDisplayMode { padding: 0px; } -body.ttrss_prefs #errorButton { +body.ttrss_prefs #pref_feeds_errors_btn { color: red; } body.ttrss_prefs .user-css-editor { diff --git a/themes/light.css b/themes/light.css index a19467c41..b6c487b66 100644 --- a/themes/light.css +++ b/themes/light.css @@ -1456,7 +1456,7 @@ body.ttrss_prefs #feedsTab { body.ttrss_prefs .dijitDialog #pref-profiles-list .dijitInlineEditBoxDisplayMode { padding: 0px; } -body.ttrss_prefs #errorButton { +body.ttrss_prefs #pref_feeds_errors_btn { color: red; } body.ttrss_prefs .user-css-editor { diff --git a/themes/light/prefs.less b/themes/light/prefs.less index 0206916ae..ec3006ce5 100644 --- a/themes/light/prefs.less +++ b/themes/light/prefs.less @@ -65,7 +65,7 @@ body.ttrss_prefs { padding : 0px; } - #errorButton { + #pref_feeds_errors_btn { color : red; } diff --git a/themes/night.css b/themes/night.css index e2bd50142..5941fd8e1 100644 --- a/themes/night.css +++ b/themes/night.css @@ -1457,7 +1457,7 @@ body.ttrss_prefs #feedsTab { body.ttrss_prefs .dijitDialog #pref-profiles-list .dijitInlineEditBoxDisplayMode { padding: 0px; } -body.ttrss_prefs #errorButton { +body.ttrss_prefs #pref_feeds_errors_btn { color: red; } body.ttrss_prefs .user-css-editor { diff --git a/themes/night_blue.css b/themes/night_blue.css index 93027e8be..99bb191a2 100644 --- a/themes/night_blue.css +++ b/themes/night_blue.css @@ -1457,7 +1457,7 @@ body.ttrss_prefs #feedsTab { body.ttrss_prefs .dijitDialog #pref-profiles-list .dijitInlineEditBoxDisplayMode { padding: 0px; } -body.ttrss_prefs #errorButton { +body.ttrss_prefs #pref_feeds_errors_btn { color: red; } body.ttrss_prefs .user-css-editor { -- cgit v1.2.3 From 15fd23c374eb32c50733de1906065f552afbfbc4 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 14 Feb 2021 09:15:51 +0300 Subject: use shortcut echo syntax for php templates --- classes/feeds.php | 10 +++--- classes/handler/public.php | 44 +++++++++++------------ classes/pref/feeds.php | 74 +++++++++++++++++++------------------- classes/pref/filters.php | 20 +++++------ classes/pref/labels.php | 12 +++---- classes/pref/system.php | 42 +++++++++++----------- include/login_form.php | 34 +++++++++--------- index.php | 78 ++++++++++++++++++++--------------------- plugins/af_redditimgur/init.php | 4 +-- plugins/auth_internal/init.php | 18 +++++----- plugins/toggle_sidebar/init.php | 2 +- prefs.php | 28 +++++++-------- 12 files changed, 183 insertions(+), 183 deletions(-) diff --git a/classes/feeds.php b/classes/feeds.php index 031a671ae..e6bd1459d 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -838,14 +838,14 @@ class Feeds extends Handler_Protected {
-

Feed Debugger: getFeedTitle($feed_id) ?>

+

Feed Debugger: getFeedTitle($feed_id) ?>

- + - +
- +
- +
diff --git a/classes/handler/public.php b/classes/handler/public.php index db8a924ad..481145606 100755 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -519,7 +519,7 @@ class Handler_Public extends Handler { - <?php echo __("Share with Tiny Tiny RSS") ?> + <?= __("Share with Tiny Tiny RSS") ?>
- - + +
- - + +
- +
- +
- - - + + +
@@ -635,24 +635,24 @@ class Handler_Public extends Handler { -
+
- + " /> + required="1" value="" />
- + "/> + value=""/>

@@ -660,7 +660,7 @@ class Handler_Public extends Handler {
- +
@@ -781,7 +781,7 @@ class Handler_Public extends Handler { };
-

+

- +
- + Database Updater - +
-

+

diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index 72a8344ad..6b5df0289 100755 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -1223,52 +1223,52 @@ class Pref_Feeds extends Handler_Protected {
+ value=""> +
- +
+ dojoType="dijit.MenuItem">
+ dojoType="dijit.MenuItem">
- +
+ dojoType="dijit.MenuItem">
+ dojoType="dijit.MenuItem">
+ dojoType="dijit.MenuItem">
+ dojoType="dijit.MenuItem">
-
+
- +
+ dojoType="dijit.MenuItem">
+ dojoType="dijit.MenuItem">
+ dojoType="dijit.MenuItem">
- - + +
" + autoExpand="" persist="true" model="feedModel" openOnClick="false"> @@ -1311,19 +1311,19 @@ class Pref_Feeds extends Handler_Protected { private function index_opml() { ?> -

+

-
+ + + + + diff --git a/classes/pref/labels.php b/classes/pref/labels.php index 22a2dddea..d182a0995 100644 --- a/classes/pref/labels.php +++ b/classes/pref/labels.php @@ -195,23 +195,23 @@ class Pref_Labels extends Handler_Protected {
- +
+ dojoType='dijit.MenuItem'>
+ dojoType='dijit.MenuItem'>
+ + + diff --git a/classes/pref/system.php b/classes/pref/system.php index 72e15e4f3..1adddf116 100644 --- a/classes/pref/system.php +++ b/classes/pref/system.php @@ -76,30 +76,30 @@ class Pref_System extends Handler_Protected {
- -
- + - - - - - + + + + + - + - - - + + + - + @@ -162,7 +162,7 @@ class Pref_System extends Handler_Protected { $page = (int) ($_REQUEST["page"] ?? 0); ?>
-
'> +
'> log_viewer($page, $severity); @@ -172,11 +172,11 @@ class Pref_System extends Handler_Protected { ?>
-
'> +
'> -
+
run_hooks(PluginHost::HOOK_PREFS_TAB, "prefSystem") ?> diff --git a/include/login_form.php b/include/login_form.php index aec305b13..aa6a72260 100755 --- a/include/login_form.php +++ b/include/login_form.php @@ -92,29 +92,29 @@
-

+

-
- +
- + " /> + required="1" value="" />
- + "/> + value=""/>
- +
- +
@@ -143,11 +143,11 @@ +
- +
@@ -155,11 +155,11 @@ +
- +
0) { ?> @@ -167,7 +167,7 @@ @@ -177,7 +177,7 @@
- +
@@ -185,7 +185,7 @@
diff --git a/index.php b/index.php index fa23570ff..8bfca1af2 100644 --- a/index.php +++ b/index.php @@ -47,7 +47,7 @@ } ?> @@ -68,7 +68,7 @@ @@ -50,7 +50,7 @@ "; + private function index_auth_personal() { $sth = $this->pdo->prepare("SELECT email,full_name,otp_enabled, access_level FROM ttrss_users @@ -311,179 +285,214 @@ class Pref_Prefs extends Handler_Protected { $full_name = htmlspecialchars($row["full_name"]); $otp_enabled = sql_bool_to_bool($row["otp_enabled"]); - print "
"; - print ""; - print ""; - print "
"; - - print "
"; - print ""; - print ""; - print "
"; - - if (!SINGLE_USER_MODE && !empty($_SESSION["hide_hello"])) { + ?> +
- $access_level = $row["access_level"]; - print "
"; - print ""; - print $access_level_names[$access_level]; - print "
"; - } - - print_hidden("op", "pref-prefs"); - print_hidden("method", "changeemail"); - - print "
"; + + - print ""; - - print "
"; + + +
+ + +
+ +
+ + +
+ + +
+ + +
+ + +
+ + + + get_plugin($_SESSION["auth_module"]); } else { $authenticator = false; } - print "
"; + $otp_enabled = $this->is_otp_enabled(); if ($authenticator && method_exists($authenticator, "change_password")) { + ?> - print ""; + - print "
"; + - print ""; + const warn = $('default_pass_warning'); + if (warn) Element.hide(warn); + } - if ($otp_enabled) { - print_notice(__("Changing your current password will disable OTP.")); - } + new Effect.Appear('pwd_change_infobox'); + } + }); + this.reset(); + } + - print "
"; - print ""; - print ""; - print "
"; + - print "
"; - print ""; - print ""; - print "
"; +
+ + +
- print "
"; - print ""; - print ""; - print "
"; +
+ + +
- print_hidden("op", "pref-prefs"); - print_hidden("method", "changepassword"); +
+ + +
- print "
"; +
- print ""; + +
- print ""; + %s) does not provide an ability to set passwords.", $_SESSION["auth_module"])); } + } - print "
"; # content pane + private function index_auth_app_passwords() { + print_notice("You can create separate passwords for API clients. Using one is required if you enable OTP."); + ?> - print "
"; +
+ appPasswordList() ?> +
- print_notice("You can create separate passwords for API clients. Using one is required if you enable OTP."); +
- print "
"; - $this->appPasswordList(); - print "
"; + + + - print "
"; + " . - __('Generate new password') . " "; + private function is_otp_enabled() { + $sth = $this->pdo->prepare("SELECT otp_enabled FROM ttrss_users + WHERE id = ?"); + $sth->execute([$_SESSION["uid"]]); - print ""; + if ($row = $sth->fetch()) { + return sql_bool_to_bool($row["otp_enabled"]); + } - print "
"; # content pane + return false; + } - print "
"; + private function index_auth_2fa() { + $otp_enabled = $this->is_otp_enabled(); if ($_SESSION["auth_module"] == "auth_internal") { - if ($otp_enabled) { - print_warning("One time passwords are currently enabled. Enter your current password below to disable."); + ?> + +
+ + + + - print ""; - - print ""; - - print "
"; - print ""; - print ""; - print "
"; +
+ + +
- print_hidden("op", "pref-prefs"); - print_hidden("method", "otpdisable"); +
- print "
"; + - print ""; +
- print ""; + " . __("Scan the following code by the Authenticator application or copy the key manually") . ""; - $csrf_token_hash = sha1($_SESSION["csrf_token"]); print "otp qr-code"; } else { @@ -500,105 +508,86 @@ class Pref_Prefs extends Handler_Protected { print "

" . __("Use the following OTP key with a compatible Authenticator application") . "

"; } - print "
"; - $otp_secret = $this->otpsecret(); + ?> + + + + + + +
+ + +
+ + - print "
"; - print ""; - print ""; - print "
"; - - print_hidden("op", "pref-prefs"); - print_hidden("method", "otpenable"); - - print ""; - - print "
"; - print ""; - print ""; - print "
"; +
+ + +
- print "
"; - print ""; - print ""; - print "
"; +
+ + +
- print "
"; - print ""; +
- print "
"; + + + auth_internal authentication module."); } + } - print "
"; # content pane - - print "
"; # tab container - - PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefPrefsAuth"); - - print "
"; #pane - - print "
settings ".__('Preferences')."\">"; - - print "
"; - - print ""; - - print '
'; - - print '
'; + private function index_auth() { + ?> +
+
+ index_auth_personal() ?> +
+
+ index_auth_password() ?> +
+
+ index_auth_app_passwords() ?> +
+
+ index_auth_2fa() ?> +
+
+ initialize_user_prefs($_SESSION["uid"], $profile); } else { $this->initialize_user_prefs($_SESSION["uid"]); @@ -660,7 +649,9 @@ class Pref_Prefs extends Handler_Protected { continue; } - if (isset($prefs_available[$pref_name]) &&$item = $prefs_available[$pref_name]) { + if (isset($prefs_available[$pref_name])) { + + $item = $prefs_available[$pref_name]; print "
"; @@ -735,16 +726,16 @@ class Pref_Prefs extends Handler_Protected { array_push($listed_boolean_prefs, $pref_name); - $checked = ($value == "true") ? "checked=\"checked\"" : ""; + $is_checked = ($value == "true") ? "checked=\"checked\"" : ""; if ($pref_name == "PURGE_UNREAD_ARTICLES" && FORCE_ARTICLE_PURGE != 0) { - $disabled = "disabled=\"1\""; - $checked = "checked=\"checked\""; + $is_disabled = "disabled=\"1\""; + $is_checked = "checked=\"checked\""; } else { - $disabled = ""; + $is_disabled = ""; } - print ""; } else if (in_array($pref_name, ['FRESH_ARTICLE_MAX_AGE', @@ -753,19 +744,19 @@ class Pref_Prefs extends Handler_Protected { $regexp = ($type_name == 'integer') ? 'regexp="^\d*$"' : ''; if ($pref_name == "PURGE_OLD_DAYS" && FORCE_ARTICLE_PURGE != 0) { - $disabled = "disabled='1'"; + $is_disabled = "disabled='1'"; $value = FORCE_ARTICLE_PURGE; } else { - $disabled = ""; + $is_disabled = ""; } if ($type_name == 'integer') print ""; else print ""; } else if ($pref_name == "SSL_CERT_SERIAL") { @@ -808,204 +799,246 @@ class Pref_Prefs extends Handler_Protected { } } } + print_hidden("boolean_prefs", htmlspecialchars(join(",", $listed_boolean_prefs))); + } - $listed_boolean_prefs = htmlspecialchars(join(",", $listed_boolean_prefs)); - - print_hidden("boolean_prefs", "$listed_boolean_prefs"); - - PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefPrefsPrefsInside"); - - print '
'; # inside pane - print '
'; - - print_hidden("op", "pref-prefs"); - print_hidden("method", "saveconfig"); + private function index_prefs() { + ?> + + + + + +
+
+ index_prefs_list() ?> + run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefPrefsPrefsInside") ?> +
+
+ +
+ +
+
+ +
+
+
+ + + + + + run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefPrefsPrefsOutside") ?> +
-
"; - - print " "; - - print ""; - - print " "; - - PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefPrefsPrefsOutside"); - - print ""; - print '
'; # inner pane - print '
'; # border container - - print "
"; #pane - - print "
extension ".__('Plugins')."\">"; - - print "
"; - - print ""; - - print_hidden("op", "pref-prefs"); - print_hidden("method", "setplugins"); - - print '
'; - print '
'; - - if (ini_get("open_basedir") && function_exists("curl_init") && !defined("NO_CURL")) { - print_warning("Your PHP configuration has open_basedir restrictions enabled. Some plugins relying on CURL for functionality may not work correctly."); - } - - if ($_SESSION["safe_mode"]) { - print_error("You have logged in using safe mode, no user plugins will be actually enabled until you login again."); - } - - $feed_handler_whitelist = [ "Af_Comics" ]; - - $feed_handlers = array_merge( - PluginHost::getInstance()->get_hooks(PluginHost::HOOK_FEED_FETCHED), - PluginHost::getInstance()->get_hooks(PluginHost::HOOK_FEED_PARSED), - PluginHost::getInstance()->get_hooks(PluginHost::HOOK_FETCH_FEED)); - - $feed_handlers = array_filter($feed_handlers, function($plugin) use ($feed_handler_whitelist) { - return in_array(get_class($plugin), $feed_handler_whitelist) === false; }); - - if (count($feed_handlers) > 0) { - print_error( - T_sprintf("The following plugins use per-feed content hooks. This may cause excessive data usage and origin server load resulting in a ban of your instance: %s" , - implode(", ", array_map(function($plugin) { return get_class($plugin); }, $feed_handlers)) - ) . " (".__("More info...").")" - ); - } + + ".__("System plugins").""; + private function index_plugins_system() { print_notice("System plugins are enabled in config.php for all users."); - $system_enabled = array_map("trim", explode(",", PLUGINS)); - $user_enabled = array_map("trim", explode(",", get_pref("_ENABLED_PLUGINS"))); + $system_enabled = array_map("trim", explode(",", (string)PLUGINS)); $tmppluginhost = new PluginHost(); $tmppluginhost->load_all($tmppluginhost::KIND_ALL, $_SESSION["uid"], true); - //$tmppluginhost->load_data(true); foreach ($tmppluginhost->get_plugins() as $name => $plugin) { $about = $plugin->about(); if ($about[3] ?? false) { - if (in_array($name, $system_enabled)) { - $checked = "checked='1'"; - } else { - $checked = ""; - } - - print "
- - "; - - if ($about[4] ?? false) { - print ""; - } - - print "
". - htmlspecialchars(T_sprintf("v%.2f, by %s", $about[0], $about[2])). - "
"; - - print "
"; - + $is_checked = in_array($name, $system_enabled) ? "checked" : ""; + ?> +
+ + + + + + + +
+ +
+
+ ".__("User plugins").""; + private function index_plugins_user() { + $system_enabled = array_map("trim", explode(",", (string)PLUGINS)); + $user_enabled = array_map("trim", explode(",", get_pref("_ENABLED_PLUGINS"))); + + $tmppluginhost = new PluginHost(); + $tmppluginhost->load_all($tmppluginhost::KIND_ALL, $_SESSION["uid"], true); foreach ($tmppluginhost->get_plugins() as $name => $plugin) { $about = $plugin->about(); if (empty($about[3]) || $about[3] == false) { - $checked = ""; - $disabled = ""; + $is_checked = ""; + $is_disabled = ""; if (in_array($name, $system_enabled)) { - $checked = "checked='1'"; - $disabled = "disabled='1'"; + $is_checked = "checked='1'"; + $is_disabled = "disabled='1'"; } else if (in_array($name, $user_enabled)) { - $checked = "checked='1'"; + $is_checked = "checked='1'"; } - print "
- - "; - - if (count($tmppluginhost->get_all($plugin)) > 0) { - if (in_array($name, $system_enabled) || in_array($name, $user_enabled)) { - print " "; - } - } + ?> + +
+ + + + get_all($plugin)) > 0) { + if (in_array($name, $system_enabled) || in_array($name, $user_enabled)) { ?> + + + + + + + +
+ +
+ +
+ - open_in_new ".__("More info...").""; + private function index_plugins() { + ?> +
+ - print "
". - htmlspecialchars(T_sprintf("v%.2f, by %s", $about[0], $about[2])). - "
"; + + - print "
"; - } - } +
+
+ "; #content-pane - print '
'; + $feed_handler_whitelist = [ "Af_Comics" ]; - print ""; + $feed_handlers = array_merge( + PluginHost::getInstance()->get_hooks(PluginHost::HOOK_FEED_FETCHED), + PluginHost::getInstance()->get_hooks(PluginHost::HOOK_FEED_PARSED), + PluginHost::getInstance()->get_hooks(PluginHost::HOOK_FETCH_FEED)); - print ""; - print "
"; #pane + $feed_handlers = array_filter($feed_handlers, function($plugin) use ($feed_handler_whitelist) { + return in_array(get_class($plugin), $feed_handler_whitelist) === false; }); - print "
"; #pane - print "
"; #border-container + if (count($feed_handlers) > 0) { + print_error( + T_sprintf("The following plugins use per-feed content hooks. This may cause excessive data usage and origin server load resulting in a ban of your instance: %s" , + implode(", ", array_map(function($plugin) { return get_class($plugin); }, $feed_handlers)) + ) . " (".__("More info...").")" + ); + } + ?> - print ""; +

+ + index_plugins_system() ?> - PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB, "prefPrefs"); +

- print "
"; #container + index_plugins_user() ?> +
+
+ + +
+
+ + +
+
+ index_auth() ?> +
+
+ index_prefs() ?> +
+
+ index_plugins() ?> +
+ run_hooks(PluginHost::HOOK_PREFS_TAB, "prefPrefs") ?> +
+ Date: Sun, 14 Feb 2021 11:39:26 +0300 Subject: appPasswordList: markup cleanup --- classes/pref/prefs.php | 90 ++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index a37ae99b5..ec7fbf607 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -1317,53 +1317,51 @@ class Pref_Prefs extends Handler_Protected { } private function appPasswordList() { - print "
"; - print "
" . - "" . __('Select') . ""; - print "
"; - print "
" . __('All') . "
"; - print "
" . __('None') . "
"; - print "
"; - print "
"; #toolbar - - print "
"; - print ""; - print ""; - print ""; - print ""; - print ""; - print ""; - print ""; - - $sth = $this->pdo->prepare("SELECT id, title, created, last_used - FROM ttrss_app_passwords WHERE owner_uid = ?"); - $sth->execute([$_SESSION['uid']]); - - while ($row = $sth->fetch()) { - - $row_id = $row["id"]; - - print ""; - - print ""; - print ""; - - print ""; - - print ""; - - print ""; - } + ?> +
+
+ +
+
+
+
+
+
- print "
".__("Description")."".__("Created")."".__("Last used")."
- " . htmlspecialchars($row["title"]) . ""; - print TimeHelper::make_local_datetime($row['created'], false); - print ""; - print TimeHelper::make_local_datetime($row['last_used'], false); - print "
"; - print "
"; +
+ + + + + + + + pdo->prepare("SELECT id, title, created, last_used + FROM ttrss_app_passwords WHERE owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); + + while ($row = $sth->fetch()) { ?> + '> + + + + + + +
+ + + + + + + +
+
+ Date: Sun, 14 Feb 2021 12:25:41 +0300 Subject: pref prefs: load secondary tabs when needed --- classes/pref/prefs.php | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index ec7fbf607..19f5221eb 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -564,7 +564,7 @@ class Pref_Prefs extends Handler_Protected { } } - private function index_auth() { + function index_auth() { ?>
@@ -953,7 +953,7 @@ class Pref_Prefs extends Handler_Protected { } } - private function index_plugins() { + function index_plugins() { ?>
+
index_prefs() ?>
- index_plugins() ?> + +
run_hooks(PluginHost::HOOK_PREFS_TAB, "prefPrefs") ?>
-- cgit v1.2.3 From 1c7e4782aa426dd1a003948756c51cf9d61f2163 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 14 Feb 2021 12:29:08 +0300 Subject: prefs system: load phpinfo using inline method --- classes/pref/prefs.php | 2 -- classes/pref/system.php | 13 +++++++++---- js/PrefHelpers.js | 6 +----- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index 19f5221eb..6e4deb223 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -1032,7 +1032,6 @@ class Pref_Prefs extends Handler_Protected { if (this.domNode.querySelector('.loading')) window.setTimeout(() => { xhrPost("backend.php", {op: 'pref-prefs', method: 'index_auth'}, (transport) => { - console.log(this); this.attr('content', transport.responseText); }); }, 100); @@ -1047,7 +1046,6 @@ class Pref_Prefs extends Handler_Protected { if (this.domNode.querySelector('.loading')) window.setTimeout(() => { xhrPost("backend.php", {op: 'pref-prefs', method: 'index_plugins'}, (transport) => { - console.log(this); this.attr('content', transport.responseText); }); }, 200); diff --git a/classes/pref/system.php b/classes/pref/system.php index 1adddf116..14df6f8d1 100644 --- a/classes/pref/system.php +++ b/classes/pref/system.php @@ -173,10 +173,15 @@ class Pref_System extends Handler_Protected {
'> - -
+ +
run_hooks(PluginHost::HOOK_PREFS_TAB, "prefSystem") ?> diff --git a/js/PrefHelpers.js b/js/PrefHelpers.js index b09beb995..7a3d38d02 100644 --- a/js/PrefHelpers.js +++ b/js/PrefHelpers.js @@ -54,11 +54,7 @@ const Helpers = { }, }, System: { - getPHPInfo: function(widget) { - xhrPost("backend.php", {op: 'pref-system', method: 'getphpinfo'}, (transport) => { - widget.attr('content', transport.responseText); - }); - } + // }, EventLog: { log_page: 0, -- cgit v1.2.3 From 2547ece0cacb7080060ad3bc32b879fee6b52230 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 14 Feb 2021 14:59:22 +0300 Subject: pref-users: cleanup index --- classes/pref/users.php | 214 +++++++++++++++++++++---------------------------- 1 file changed, 93 insertions(+), 121 deletions(-) diff --git a/classes/pref/users.php b/classes/pref/users.php index 5c622a9b1..0454a1292 100644 --- a/classes/pref/users.php +++ b/classes/pref/users.php @@ -251,12 +251,8 @@ class Pref_Users extends Handler_Protected { print T_sprintf("Added user %s with password %s", $login, $tmp_user_pwd); - $this->initialize_user($new_uid); - } else { - print T_sprintf("Could not create user %s", $login); - } } else { print T_sprintf("User %s already exists.", $login); @@ -303,10 +299,6 @@ class Pref_Users extends Handler_Protected { global $access_level_names; - print "
"; - print "
"; - print "
"; - $user_search = clean($_REQUEST["search"] ?? ""); if (array_key_exists("search", $_REQUEST)) { @@ -315,137 +307,117 @@ class Pref_Users extends Handler_Protected { $user_search = ($_SESSION["prefs_user_search"] ?? ""); } - print "
- - -
"; - $sort = clean($_REQUEST["sort"] ?? ""); if (!$sort || $sort == "undefined") { $sort = "login"; } - print "
". - "" . __('Select').""; - print "
"; - print "
".__('All')."
"; - print "
".__('None')."
"; - print "
"; - - print ""; - - print " - - - "; - - PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefUsersToolbar"); - - print "
"; #toolbar - print "
"; #pane - print "
"; - $sort = $this->validate_field($sort, ["login", "access_level", "created", "num_feeds", "created", "last_login"], "login"); if ($sort != "login") $sort = "$sort DESC"; - $sth = $this->pdo->prepare("SELECT - tu.id, - login,access_level,email, - ".SUBSTRING_FOR_DATE."(last_login,1,16) as last_login, - ".SUBSTRING_FOR_DATE."(created,1,16) as created, - (SELECT COUNT(id) FROM ttrss_feeds WHERE owner_uid = tu.id) AS num_feeds - FROM - ttrss_users tu - WHERE - (:search = '' OR login LIKE :search) AND tu.id > 0 - ORDER BY $sort"); - $sth->execute([":search" => $user_search ? "%$user_search%" : ""]); - - print ""; - - print " - - - - - - "; - - $lnum = 0; - - while ($line = $sth->fetch()) { - - $uid = $line["id"]; - - print ""; - - $line["login"] = htmlspecialchars($line["login"]); - $line["created"] = TimeHelper::make_local_datetime($line["created"], false); - $line["last_login"] = TimeHelper::make_local_datetime($line["last_login"], false); - - print ""; - - print ""; - - print ""; - print ""; - print ""; - print ""; - - print ""; - - ++$lnum; - } - - print "
 ".__('Login')."".__('Access Level')."".__('Subscribed feeds')."".__('Registered')."".__('Last login')."
person " . $line["login"] . "" . $access_level_names[$line["access_level"]] . "" . $line["num_feeds"] . "" . $line["created"] . "" . $line["last_login"] . "
"; - - if ($lnum == 0) { - if (!$user_search) { - print_warning(__('No users defined.')); - } else { - print_warning(__('No matching users found.')); - } - } - - print "
"; #pane - - PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB, "prefUsers"); - - print "
"; #container - - } + ?> + +
+
+
+ +
+ + +
+ +
+ +
+
+
+
+
+ + + + + + + + + + run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefUsersToolbar") ?> + +
+
+
+ + + + + + + + + + + + + pdo->prepare("SELECT + tu.id, + login,access_level,email, + ".SUBSTRING_FOR_DATE."(last_login,1,16) as last_login, + ".SUBSTRING_FOR_DATE."(created,1,16) as created, + (SELECT COUNT(id) FROM ttrss_feeds WHERE owner_uid = tu.id) AS num_feeds + FROM + ttrss_users tu + WHERE + (:search = '' OR login LIKE :search) AND tu.id > 0 + ORDER BY $sort"); + $sth->execute([":search" => $user_search ? "%$user_search%" : ""]); + + while ($row = $sth->fetch()) { ?> + + + + + + + + + + + +
+ + person
+
+ run_hooks(PluginHost::HOOK_PREFS_TAB, "prefUsers") ?> +
+ prepare("insert into ttrss_feeds (owner_uid,title,feed_url) - values (?, 'Tiny Tiny RSS: Forum', - 'https://tt-rss.org/forum/rss.php')"); - $sth->execute([$uid]); - } - static function logout_user() { if (session_status() === PHP_SESSION_ACTIVE) session_destroy(); -- cgit v1.2.3 From a8cc43a0ff1cf6297577fae8536408287518baf4 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 14 Feb 2021 15:31:03 +0300 Subject: move logout_user() to UserHelper --- classes/api.php | 2 +- classes/handler/public.php | 2 +- classes/pref/users.php | 11 ----------- classes/userhelper.php | 14 +++++++++++++- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/classes/api.php b/classes/api.php index 6a919be64..9299c34ea 100755 --- a/classes/api.php +++ b/classes/api.php @@ -81,7 +81,7 @@ class API extends Handler { } function logout() { - Pref_Users::logout_user(); + UserHelper::logout(); $this->wrap(self::STATUS_OK, array("status" => "OK")); } diff --git a/classes/handler/public.php b/classes/handler/public.php index 481145606..79f3a9e6c 100755 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -286,7 +286,7 @@ class Handler_Public extends Handler { function logout() { if (validate_csrf($_POST["csrf_token"])) { - Pref_Users::logout_user(); + UserHelper::logout(); header("Location: index.php"); } else { header("Content-Type: text/json"); diff --git a/classes/pref/users.php b/classes/pref/users.php index 0454a1292..24d28e62a 100644 --- a/classes/pref/users.php +++ b/classes/pref/users.php @@ -418,15 +418,4 @@ class Pref_Users extends Handler_Protected { return $default; } - static function logout_user() { - if (session_status() === PHP_SESSION_ACTIVE) - session_destroy(); - - if (isset($_COOKIE[session_name()])) { - setcookie(session_name(), '', time()-42000, '/'); - - } - session_commit(); - } - } diff --git a/classes/userhelper.php b/classes/userhelper.php index c9c4dd102..8eb97f5d0 100644 --- a/classes/userhelper.php +++ b/classes/userhelper.php @@ -105,7 +105,7 @@ class UserHelper { } if (empty($_SESSION["uid"])) { - Pref_Users::logout_user(); + UserHelper::logout(); Handler_Public::render_login_form(); exit; @@ -157,4 +157,16 @@ class UserHelper { return false; } + + static function logout() { + if (session_status() === PHP_SESSION_ACTIVE) + session_destroy(); + + if (isset($_COOKIE[session_name()])) { + setcookie(session_name(), '', time()-42000, '/'); + + } + session_commit(); + } + } -- cgit v1.2.3 From 0fbf10991237b3f91ee5c77349637d7197a22bdc Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 14 Feb 2021 15:38:45 +0300 Subject: * remove users/filters toolbar edit button (just click on it) * fix title of edit filter dialog always showing create filter --- classes/pref/filters.php | 2 -- classes/pref/users.php | 4 ---- js/CommonFilters.js | 2 +- js/PrefFilterTree.js | 17 ----------------- js/PrefUsers.js | 15 --------------- 5 files changed, 1 insertion(+), 39 deletions(-) diff --git a/classes/pref/filters.php b/classes/pref/filters.php index c898a8b67..1c264f642 100755 --- a/classes/pref/filters.php +++ b/classes/pref/filters.php @@ -736,8 +736,6 @@ class Pref_Filters extends Handler_Protected { - - - diff --git a/js/CommonFilters.js b/js/CommonFilters.js index 15403b8c4..06e0410c7 100644 --- a/js/CommonFilters.js +++ b/js/CommonFilters.js @@ -243,7 +243,7 @@ const Filters = { try { const dialog = new fox.SingleUseDialog({ id: "filterEditDlg", - title: __("Create Filter"), + title: id ? __("Edit Filter") : __("Create Filter"), test: function () { Filters.test(this.attr('value')); }, diff --git a/js/PrefFilterTree.js b/js/PrefFilterTree.js index abfdbb3b0..e7d4efdc1 100644 --- a/js/PrefFilterTree.js +++ b/js/PrefFilterTree.js @@ -119,23 +119,6 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree"], functio }); } }, - editSelectedFilter: function() { - const rows = this.getSelectedFilters(); - - if (rows.length == 0) { - alert(__("No filters selected.")); - return; - } - - if (rows.length > 1) { - alert(__("Please select only one filter.")); - return; - } - - Notify.close(); - - this.editFilter(rows[0]); - }, removeSelectedFilters: function() { const sel_rows = this.getSelectedFilters(); diff --git a/js/PrefUsers.js b/js/PrefUsers.js index 0a7e635fe..e5c281811 100644 --- a/js/PrefUsers.js +++ b/js/PrefUsers.js @@ -93,21 +93,6 @@ const Users = { alert(__("No users selected.")); } }, - editSelected: function() { - const rows = this.getSelection(); - - if (rows.length == 0) { - alert(__("No users selected.")); - return; - } - - if (rows.length > 1) { - alert(__("Please select one user.")); - return; - } - - this.edit(rows[0]); - }, getSelection :function() { return Tables.getSelected("users-list"); } -- cgit v1.2.3 From 33ea46c2bc5c91d7767f11c230a941cc635c0e67 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 14 Feb 2021 15:42:12 +0300 Subject: pref-users/add: remove unused variable --- classes/pref/users.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/classes/pref/users.php b/classes/pref/users.php index 7adb09ab2..9d9ea4d8e 100644 --- a/classes/pref/users.php +++ b/classes/pref/users.php @@ -246,8 +246,6 @@ class Pref_Users extends Handler_Protected { if ($new_uid = UserHelper::find_user_by_login($login)) { - $new_uid = $row['id']; - print T_sprintf("Added user %s with password %s", $login, $tmp_user_pwd); -- cgit v1.2.3 From 0b7377238a556708035b0cd51a9e58693fb648f6 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 14 Feb 2021 15:50:46 +0300 Subject: add Handler_Administrative --- classes/handler/administrative.php | 11 +++++++++++ classes/pref/system.php | 13 +------------ classes/pref/users.php | 13 +------------ 3 files changed, 13 insertions(+), 24 deletions(-) create mode 100644 classes/handler/administrative.php diff --git a/classes/handler/administrative.php b/classes/handler/administrative.php new file mode 100644 index 000000000..52dfed8b7 --- /dev/null +++ b/classes/handler/administrative.php @@ -0,0 +1,11 @@ += 10) { + return true; + } + } + return false; + } +} diff --git a/classes/pref/system.php b/classes/pref/system.php index 14df6f8d1..2a97ec6f0 100644 --- a/classes/pref/system.php +++ b/classes/pref/system.php @@ -1,20 +1,9 @@ Date: Sun, 14 Feb 2021 16:44:41 +0300 Subject: pref-users edit: use client dialog --- classes/pref/users.php | 112 +++++++------------------------------------------ js/App.js | 9 ++++ js/PrefUsers.js | 84 +++++++++++++++++++++++++++++++++++-- 3 files changed, 105 insertions(+), 100 deletions(-) diff --git a/classes/pref/users.php b/classes/pref/users.php index b34f85d88..bc125d0ce 100644 --- a/classes/pref/users.php +++ b/classes/pref/users.php @@ -1,7 +1,7 @@ "; + $id = (int)clean($_REQUEST["id"]); - print '
-
'; - - //print ""; - - $id = (int) clean($_REQUEST["id"]); - - print_hidden("id", "$id"); - print_hidden("op", "pref-users"); - print_hidden("method", "editSave"); - - $sth = $this->pdo->prepare("SELECT * FROM ttrss_users WHERE id = ?"); + $sth = $this->pdo->prepare("SELECT id, login, access_level, email FROM ttrss_users WHERE id = ?"); $sth->execute([$id]); - if ($row = $sth->fetch()) { - - $login = $row["login"]; - $access_level = $row["access_level"]; - $email = $row["email"]; - - $sel_disabled = ($id == $_SESSION["uid"] || $login == "admin") ? "disabled" : ""; - - print "
".__("User")."
"; - print "
"; - - if ($sel_disabled) { - print_hidden("login", "$login"); - } - - print "
"; - print ""; - print ""; - print "
"; - - print "
"; - - print "
".__("Authentication")."
"; - print "
"; - - print "
"; - - print " "; - - if (!$sel_disabled) { - print_select_hash("access_level", $access_level, $access_level_names, - "dojoType=\"fox.form.Select\" $sel_disabled"); - } else { - print_select_hash("", $access_level, $access_level_names, - "dojoType=\"fox.form.Select\" $sel_disabled"); - print_hidden("access_level", "$access_level"); - } - - print "
"; - print "
"; - - print " "; - print ""; - - print "
"; - - print "
"; - - print "
".__("Options")."
"; - print "
"; - - print "
"; - print " "; - print ""; - print "
"; - - print "
"; - - print ""; - + if ($row = $sth->fetch(PDO::FETCH_ASSOC)) { + print json_encode([ + "user" => $row, + "access_level_names" => $access_level_names + ]); + } else { + print json_encode(["error" => "USER_NOT_FOUND"]); } - - print '
'; #tab - print "
"; - - print '
'; - print '
'; - - print "
- - -
"; - - print ""; - - return; } function userdetails() { @@ -186,6 +100,12 @@ class Pref_Users extends Handler_Administrative { $email = clean($_REQUEST["email"]); $password = clean($_REQUEST["password"]); + // no blank usernames + if (!$login) return; + + // forbid renaming admin + if ($uid == 1) $login = "admin"; + if ($password) { $salt = substr(bin2hex(get_random_bytes(125)), 0, 250); $pwd_hash = encrypt_password($password, $salt, true); diff --git a/js/App.js b/js/App.js index 4646145ea..1e6e5fdb1 100644 --- a/js/App.js +++ b/js/App.js @@ -20,6 +20,15 @@ const App = { FormFields: { hidden: function(name, value, id = "") { return `` + }, + select_hash: function(name, value, values, attributes) { + return ` + + ` } }, Scrollable: { diff --git a/js/PrefUsers.js b/js/PrefUsers.js index e5c281811..1fe4db150 100644 --- a/js/PrefUsers.js +++ b/js/PrefUsers.js @@ -1,7 +1,7 @@ 'use strict' /* global __ */ -/* global xhrPost, dojo, dijit, Notify, Tables, fox */ +/* global xhrPost, xhrJson, dojo, dijit, Notify, Tables, App, fox */ const Users = { reload: function(sort) { @@ -27,7 +27,10 @@ const Users = { } }, edit: function(id) { - xhrPost('backend.php', {op: 'pref-users', method: 'edit', id: id}, (transport) => { + xhrJson('backend.php', {op: 'pref-users', method: 'edit', id: id}, (reply) => { + const user = reply.user; + const is_disabled = (user.id == 1) ? "disabled='disabled'" : ''; + const dialog = new fox.SingleUseDialog({ id: "userEditDlg", title: __("User Editor"), @@ -35,13 +38,86 @@ const Users = { if (this.validate()) { Notify.progress("Saving data...", true); - xhrPost("backend.php", dojo.formToObject("user_edit_form"), (/* transport */) => { + xhrPost("backend.php", this.attr('value'), () => { dialog.hide(); Users.reload(); }); } }, - content: transport.responseText + content: ` +
+ + ${App.FormFields.hidden('id', user.id.toString())} + ${App.FormFields.hidden('op', 'pref-users')} + ${App.FormFields.hidden('method', 'editSave')} + +
+
+ +
${__("User")}
+ +
+
+ + + + ${is_disabled ? App.FormFields.hidden("login", user.login) : ''} +
+
+ +
${__("Authentication")}
+ +
+
+ + ${App.FormFields.select_hash("access_level", + user.access_level, reply.access_level_names, is_disabled)} + + ${is_disabled ? App.FormFields.hidden("access_level", + user.access_level.toString()) : ''} +
+
+ + +
+
+ +
${__("Options")}
+ +
+
+ + +
+
+
+
+ + ${__("Loading, please wait...")} +
+
+ +
+ + +
+
+ ` }); dialog.show(); -- cgit v1.2.3 From ff6031d3c914fa2c7ac514243394fa70a56c6bd7 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 14 Feb 2021 18:59:09 +0300 Subject: remove old-style markup from exception dialog --- js/App.js | 44 +++++++++++++++++++++++--------------------- themes/compact.css | 15 +++------------ themes/compact_night.css | 15 +++------------ themes/light.css | 15 +++------------ themes/light/tt-rss.less | 28 +++++++--------------------- themes/night.css | 15 +++------------ themes/night_blue.css | 15 +++------------ 7 files changed, 45 insertions(+), 102 deletions(-) diff --git a/js/App.js b/js/App.js index 1e6e5fdb1..0a72d1b12 100644 --- a/js/App.js +++ b/js/App.js @@ -542,29 +542,31 @@ const App = { } try { - let stack_msg = ""; - - if (error.stack) - stack_msg += `
Stack trace:
- `; - - if (params.info) - stack_msg += `
Additional information:
- `; - - const content = `
-

${message}

- ${stack_msg} -
- -
-
`; - const dialog = new fox.SingleUseDialog({ - id: "exceptionDlg", title: params.title || __("Unhandled exception"), - content: content + content: ` +
+

${message}

+ +
${__('Stack trace')}
+
+ +
+ + ${params && params.info ? + ` +
${__('Additional information')}
+
+ +
+ ` : ''} +
+
+ +
+
` }); dialog.show(); diff --git a/themes/compact.css b/themes/compact.css index 36c5aec9f..24380d428 100644 --- a/themes/compact.css +++ b/themes/compact.css @@ -526,22 +526,13 @@ body.ttrss_main #feed_browser_spinner { height: 18px; width: 18px; } -body.ttrss_main #exceptionDlg .dijitDialogTitleBar { - background: red; - color: white; -} -body.ttrss_main #exceptionDlg .dijitDialogPaneContent { - background: #fcc; -} -body.ttrss_main #exceptionDlg .error-contents .message { +body.ttrss_main .exception-contents h3 { color: red; } -body.ttrss_main #exceptionDlg .error-contents textarea { +body.ttrss_main .exception-contents textarea { width: 99%; height: 200px; -} -body.ttrss_main #exceptionDlg .error-contents .dlgButtons { - text-align: center; + font-size: 11px; } body.ttrss_main #content-wrap { padding: 0px; diff --git a/themes/compact_night.css b/themes/compact_night.css index e39b7020a..a366404a4 100644 --- a/themes/compact_night.css +++ b/themes/compact_night.css @@ -526,22 +526,13 @@ body.ttrss_main #feed_browser_spinner { height: 18px; width: 18px; } -body.ttrss_main #exceptionDlg .dijitDialogTitleBar { - background: red; - color: white; -} -body.ttrss_main #exceptionDlg .dijitDialogPaneContent { - background: #fcc; -} -body.ttrss_main #exceptionDlg .error-contents .message { +body.ttrss_main .exception-contents h3 { color: red; } -body.ttrss_main #exceptionDlg .error-contents textarea { +body.ttrss_main .exception-contents textarea { width: 99%; height: 200px; -} -body.ttrss_main #exceptionDlg .error-contents .dlgButtons { - text-align: center; + font-size: 11px; } body.ttrss_main #content-wrap { padding: 0px; diff --git a/themes/light.css b/themes/light.css index b6c487b66..d3f5d7978 100644 --- a/themes/light.css +++ b/themes/light.css @@ -526,22 +526,13 @@ body.ttrss_main #feed_browser_spinner { height: 18px; width: 18px; } -body.ttrss_main #exceptionDlg .dijitDialogTitleBar { - background: red; - color: white; -} -body.ttrss_main #exceptionDlg .dijitDialogPaneContent { - background: #fcc; -} -body.ttrss_main #exceptionDlg .error-contents .message { +body.ttrss_main .exception-contents h3 { color: red; } -body.ttrss_main #exceptionDlg .error-contents textarea { +body.ttrss_main .exception-contents textarea { width: 99%; height: 200px; -} -body.ttrss_main #exceptionDlg .error-contents .dlgButtons { - text-align: center; + font-size: 11px; } body.ttrss_main #content-wrap { padding: 0px; diff --git a/themes/light/tt-rss.less b/themes/light/tt-rss.less index 65ec33bc3..d13ffff3e 100644 --- a/themes/light/tt-rss.less +++ b/themes/light/tt-rss.less @@ -609,28 +609,14 @@ body.ttrss_main { width : 18px; } - #exceptionDlg { - .dijitDialogTitleBar { - background : red; - color : white; - } - - .dijitDialogPaneContent { - background : #fcc; + .exception-contents { + h3 { + color : red; } - - .error-contents { - .message { - color : red; - } - - textarea { - width : 99%; - height : 200px; - } - .dlgButtons { - text-align : center; - } + textarea { + width : 99%; + height : 200px; + font-size : 11px; } } diff --git a/themes/night.css b/themes/night.css index 5941fd8e1..87a68a3c0 100644 --- a/themes/night.css +++ b/themes/night.css @@ -527,22 +527,13 @@ body.ttrss_main #feed_browser_spinner { height: 18px; width: 18px; } -body.ttrss_main #exceptionDlg .dijitDialogTitleBar { - background: red; - color: white; -} -body.ttrss_main #exceptionDlg .dijitDialogPaneContent { - background: #fcc; -} -body.ttrss_main #exceptionDlg .error-contents .message { +body.ttrss_main .exception-contents h3 { color: red; } -body.ttrss_main #exceptionDlg .error-contents textarea { +body.ttrss_main .exception-contents textarea { width: 99%; height: 200px; -} -body.ttrss_main #exceptionDlg .error-contents .dlgButtons { - text-align: center; + font-size: 11px; } body.ttrss_main #content-wrap { padding: 0px; diff --git a/themes/night_blue.css b/themes/night_blue.css index 99bb191a2..02a66656b 100644 --- a/themes/night_blue.css +++ b/themes/night_blue.css @@ -527,22 +527,13 @@ body.ttrss_main #feed_browser_spinner { height: 18px; width: 18px; } -body.ttrss_main #exceptionDlg .dijitDialogTitleBar { - background: red; - color: white; -} -body.ttrss_main #exceptionDlg .dijitDialogPaneContent { - background: #fcc; -} -body.ttrss_main #exceptionDlg .error-contents .message { +body.ttrss_main .exception-contents h3 { color: red; } -body.ttrss_main #exceptionDlg .error-contents textarea { +body.ttrss_main .exception-contents textarea { width: 99%; height: 200px; -} -body.ttrss_main #exceptionDlg .error-contents .dlgButtons { - text-align: center; + font-size: 11px; } body.ttrss_main #content-wrap { padding: 0px; -- cgit v1.2.3 From 37a81ba594c2b848b8ecec805527ee8766f1ed14 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 14 Feb 2021 19:19:25 +0300 Subject: SingleUseDialog: destroy existing widget with same id on create --- js/SingleUseDialog.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/js/SingleUseDialog.js b/js/SingleUseDialog.js index 944f24c6f..2de6f83ff 100644 --- a/js/SingleUseDialog.js +++ b/js/SingleUseDialog.js @@ -1,6 +1,17 @@ +/* eslint-disable prefer-rest-params */ /* global dijit, define */ define(["dojo/_base/declare", "dijit/Dialog"], function (declare) { return declare("fox.SingleUseDialog", dijit.Dialog, { + create: function(params) { + const extant = dijit.byId(params.id); + + if (extant) { + console.warn('SingleUseDialog: destroying existing widget:', params.id, '=', extant) + extant.destroyRecursive(); + } + + return this.inherited(arguments); + }, onHide: function() { this.destroyRecursive(); } -- cgit v1.2.3 From 68e2ccb35477d9270f598a3e1750782fc81bfb16 Mon Sep 17 00:00:00 2001 From: Joseph Date: Sun, 14 Feb 2021 17:31:01 +0000 Subject: Lazy load image attachments --- classes/article.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/article.php b/classes/article.php index 7f5311668..5ea936985 100755 --- a/classes/article.php +++ b/classes/article.php @@ -443,7 +443,7 @@ class Article extends Handler_Protected { $encsize .= ' height="' . intval($entry['height']) . '"'; if ($entry['width'] > 0) $encsize .= ' width="' . intval($entry['width']) . '"'; - $rv .= "

\"".htmlspecialchars($entry["filename"])."\"

"; -- cgit v1.2.3 From a2e688fcb2d463a5db700ebd013c783e3a8f4971 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 14 Feb 2021 22:17:13 +0300 Subject: render headline-specific toolbar on the client --- classes/feeds.php | 123 +++++++++-------------------------------------- classes/pref/feeds.php | 22 ++++++--- index.php | 4 +- js/CommonDialogs.js | 13 ++--- js/Headlines.js | 61 +++++++++++++++++++++-- plugins/mail/init.php | 7 ++- plugins/mailto/init.php | 5 ++ themes/compact.css | 5 +- themes/compact_night.css | 5 +- themes/light.css | 5 +- themes/light/tt-rss.less | 4 +- themes/night.css | 5 +- themes/night_blue.css | 5 +- 13 files changed, 136 insertions(+), 128 deletions(-) diff --git a/classes/feeds.php b/classes/feeds.php index e6bd1459d..0a3e77a1a 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -16,103 +16,6 @@ class Feeds extends Handler_Protected { return array_search($method, $csrf_ignored) !== false; } - private function format_headline_subtoolbar($feed_site_url, $feed_title, - $feed_id, $is_cat, $search, - $error, $feed_last_updated) { - - $cat_q = $is_cat ? "&is_cat=$is_cat" : ""; - - if ($search) { - $search_q = "&q=$search"; - } else { - $search_q = ""; - } - - $reply = ""; - - $rss_link = htmlspecialchars(get_self_url_prefix() . - "/public.php?op=rss&id=${feed_id}${cat_q}${search_q}"); - - $reply .= ""; - - $reply .= " - rss_feed"; - - $reply .= ""; - - if ($feed_site_url) { - $last_updated = T_sprintf("Last updated: %s", $feed_last_updated); - - $reply .= "". - truncate_string(strip_tags($feed_title), 30).""; - } else { - $reply .= strip_tags($feed_title); - } - - if ($error) - $reply .= " error"; - - $reply .= ""; - $reply .= ""; - $reply .= ""; - - $reply .= ""; - $reply .= ""; - $reply .= " "; - - $reply .= "
- ".__("Select...")." -
-
".__('All')."
-
".__('Unread')."
-
".__('Invert')."
-
".__('None')."
-
-
".__('Toggle unread')."
-
".__('Toggle starred')."
-
".__('Toggle published')."
-
-
".__('Mark as read')."
-
".__('Set score')."
"; - - // TODO: move to mail plugin - if (PluginHost::getInstance()->get_plugin("mail")) { - $reply .= "
".__('Forward by email')."
"; - } - - // TODO: move to mailto plugin - if (PluginHost::getInstance()->get_plugin("mailto")) { - $reply .= "
".__('Forward by email')."
"; - } - - PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM, - function ($result) use (&$reply) { - $reply .= $result; - }, - $feed_id, $is_cat); - - if ($feed_id == 0 && !$is_cat) { - $reply .= "
-
".__('Delete permanently')."
"; - } - - $reply .= "
"; /* menu */ - - $reply .= "
"; /* dropdown */ - - PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_HEADLINE_TOOLBAR_BUTTON, - function ($result) use (&$reply) { - $reply .= $result; - }, - $feed_id, $is_cat); - - $reply .= "
"; - - return $reply; - } - private function format_headlines_list($feed, $method, $view_mode, $limit, $cat_view, $offset, $override_order = false, $include_children = false, $check_first_id = false, $skip_first_id_check = false, $order_by = false) { @@ -222,10 +125,28 @@ class Feeds extends Handler_Protected { $reply['search_query'] = [$search, $search_language]; $reply['vfeed_group_enabled'] = $vfeed_group_enabled; - $reply['toolbar'] = $this->format_headline_subtoolbar($feed_site_url, - $feed_title, - $feed, $cat_view, $search, - $last_error, $last_updated); + $plugin_menu_items = ""; + PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM, + function ($result) use (&$plugin_menu_items) { + $plugin_menu_items .= $result; + }, + $feed, $cat_view); + + $plugin_buttons = ""; + PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_HEADLINE_TOOLBAR_BUTTON, + function ($result) use (&$plugin_buttons) { + $plugin_buttons .= $result; + }, + $feed, $cat_view); + + $reply['toolbar'] = [ + 'site_url' => $feed_site_url, + 'title' => truncate_string(strip_tags($feed_title), 30), + 'error' => $last_error, + 'last_updated' => $last_updated, + 'plugin_menu_items' => $plugin_menu_items, + 'plugin_buttons' => $plugin_buttons, + ]; $reply['content'] = []; diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index 6b5df0289..e225949f2 100755 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -1358,14 +1358,12 @@ class Pref_Feeds extends Handler_Protected { } private function index_shared() { - $rss_url = htmlspecialchars(get_self_url_prefix() . - "/public.php?op=rss&id=-2&view-mode=all_articles"); ?>

@@ -1603,11 +1601,23 @@ class Pref_Feeds extends Handler_Protected { print json_encode(["link" => $new_key]); } - function getFeedKey() { + function getsharedurl() { $feed_id = clean($_REQUEST['id']); - $is_cat = clean($_REQUEST['is_cat']); + $is_cat = clean($_REQUEST['is_cat']) == "true"; + $search = clean($_REQUEST['search']); + + $link = get_self_url_prefix() . "/public.php?" . http_build_query([ + 'op' => 'rss', + 'id' => $feed_id, + 'is_cat' => (int)$is_cat, + 'q' => $search, + 'key' => Feeds::get_feed_access_key($feed_id, $is_cat, $_SESSION["uid"]) + ]); - print json_encode(["link" => Feeds::get_feed_access_key($feed_id, $is_cat, $_SESSION["uid"])]); + print json_encode([ + "title" => Feeds::getFeedTitle($feed_id, $is_cat), + "link" => $link + ]); } private function update_feed_access_key($feed_id, $is_cat, $owner_uid) { diff --git a/index.php b/index.php index 8bfca1af2..d53fb54a8 100644 --- a/index.php +++ b/index.php @@ -176,9 +176,9 @@ }); ?> -
+
- +
diff --git a/js/CommonDialogs.js b/js/CommonDialogs.js index 5a72f705b..e6b1822c2 100644 --- a/js/CommonDialogs.js +++ b/js/CommonDialogs.js @@ -433,24 +433,19 @@ const CommonDialogs = { } }); }, - generatedFeed: function(feed, is_cat, rss_url, feed_title) { + generatedFeed: function(feed, is_cat, search = "") { Notify.progress("Loading, please wait...", true); - xhrJson("backend.php", {op: "pref-feeds", method: "getFeedKey", id: feed, is_cat: is_cat}, (reply) => { + xhrJson("backend.php", {op: "pref-feeds", method: "getsharedurl", id: feed, is_cat: is_cat, search: search}, (reply) => { try { - if (!feed_title && typeof Feeds != "undefined") - feed_title = Feeds.getName(feed, is_cat); - - const secret_url = rss_url + "&key=" + encodeURIComponent(reply.link); - const dialog = new fox.SingleUseDialog({ title: __("Show as feed"), content: ` -
${__("%s can be accessed via the following secret URL:").replace("%s", feed_title)}
+
${__("%s can be accessed via the following secret URL:").replace("%s", App.escapeHtml(reply.title))}
diff --git a/js/Headlines.js b/js/Headlines.js index ea4c81a6a..954b6b45f 100755 --- a/js/Headlines.js +++ b/js/Headlines.js @@ -566,6 +566,58 @@ const Headlines = { } } }, + renderToolbar: function(headlines) { + + const tb = headlines['toolbar']; + const search_query = Feeds._search_query ? Feeds._search_query.query : ""; + const target = dijit.byId('toolbar-headlines'); + + target.attr('innerHTML', + ` + + + rss_feed + + ${tb.site_url ? + `${tb.title}` : + `${tb.title}`} + ${search_query ? + ` + (${__("Cancel search")}) + ` : ''} + ${tb.error ? `error` : ''} + + + + +
+ ${__("Select...")} +
+
${__('All')}
+
${__('Unread')}
+
${__('Invert')}
+
${__('None')}
+
+
${__('Toggle unread')}
+
${__('Toggle starred')}
+
${__('Toggle published')}
+
+
${__('Mark as read')}
+
${__('Set score')}
+ ${tb.plugin_menu_items} + ${headlines.id === 0 && !headlines.is_cat ? + ` +
+
${__('Delete permanently')}
+ ` : ''} +
+ ${tb.plugin_buttons} + + `); + + dojo.parser.parse(target.domNode); + }, onLoaded: function (transport, offset, append) { const reply = App.handleRpcJson(transport); @@ -613,9 +665,11 @@ const Headlines = { this.headlines = []; this.vgroup_last_feed = undefined; - dojo.html.set($("toolbar-headlines"), + /*dojo.html.set($("toolbar-headlines"), reply['headlines']['toolbar'], - {parseContent: true}); + {parseContent: true});*/ + + Headlines.renderToolbar(reply['headlines']); if (typeof reply['headlines']['content'] == 'string') { $("headlines-frame").innerHTML = reply['headlines']['content']; @@ -646,11 +700,12 @@ const Headlines = { hsp.innerHTML = "" + __("Click to open next unread feed.") + ""; + /* if (Feeds._search_query) { $("feed_title").innerHTML += "" + " (" + __("Cancel search") + ")" + ""; - } + } */ Headlines.updateCurrentUnread(); diff --git a/plugins/mail/init.php b/plugins/mail/init.php index 40d147fc9..829620ebc 100644 --- a/plugins/mail/init.php +++ b/plugins/mail/init.php @@ -15,12 +15,17 @@ 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"); } + function hook_headline_toolbar_select_menu_item($feed_id, $is_cat) { + return "
".__('Forward by email')."
"; + } + function save() { $addresslist = $_POST["addresslist"]; @@ -32,7 +37,7 @@ class Mail extends Plugin { function hook_prefs_tab($args) { if ($args != "prefPrefs") return; - print "
mail ".__('Mail plugin')."\">"; print "

" . __("You can set predefined email addressed here (comma-separated list):") . "

"; diff --git a/plugins/mailto/init.php b/plugins/mailto/init.php index 390984b71..3e24dcf29 100644 --- a/plugins/mailto/init.php +++ b/plugins/mailto/init.php @@ -12,6 +12,11 @@ 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 "
".__('Forward by email')."
"; } function get_js() { diff --git a/themes/compact.css b/themes/compact.css index 24380d428..6e4f59c84 100644 --- a/themes/compact.css +++ b/themes/compact.css @@ -704,6 +704,8 @@ body.ttrss_main #toolbar-frame #toolbar i { margin: 0 4px; } body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines { + font-size: 12px; + background: transparent; padding-right: 4px; flex-grow: 2; display: flex; @@ -713,7 +715,8 @@ body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left { display: flex; align-items: center; } -body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left #feed_title { +body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title, +body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .cancel_search { margin-left: 4px; } body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .right { diff --git a/themes/compact_night.css b/themes/compact_night.css index a366404a4..7dd4c0b0e 100644 --- a/themes/compact_night.css +++ b/themes/compact_night.css @@ -704,6 +704,8 @@ body.ttrss_main #toolbar-frame #toolbar i { margin: 0 4px; } body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines { + font-size: 12px; + background: transparent; padding-right: 4px; flex-grow: 2; display: flex; @@ -713,7 +715,8 @@ body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left { display: flex; align-items: center; } -body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left #feed_title { +body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title, +body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .cancel_search { margin-left: 4px; } body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .right { diff --git a/themes/light.css b/themes/light.css index d3f5d7978..6f70862d6 100644 --- a/themes/light.css +++ b/themes/light.css @@ -704,6 +704,8 @@ body.ttrss_main #toolbar-frame #toolbar i { margin: 0 4px; } body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines { + font-size: 12px; + background: transparent; padding-right: 4px; flex-grow: 2; display: flex; @@ -713,7 +715,8 @@ body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left { display: flex; align-items: center; } -body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left #feed_title { +body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title, +body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .cancel_search { margin-left: 4px; } body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .right { diff --git a/themes/light/tt-rss.less b/themes/light/tt-rss.less index d13ffff3e..4632997ba 100644 --- a/themes/light/tt-rss.less +++ b/themes/light/tt-rss.less @@ -820,6 +820,8 @@ body.ttrss_main { } #toolbar-headlines { + font-size : 12px; + background: transparent; padding-right : 4px; flex-grow : 2; display : flex; @@ -829,7 +831,7 @@ body.ttrss_main { display : flex; align-items : center; - #feed_title { + .feed_title, .cancel_search { margin-left : 4px; } } diff --git a/themes/night.css b/themes/night.css index 87a68a3c0..579f6dcdf 100644 --- a/themes/night.css +++ b/themes/night.css @@ -705,6 +705,8 @@ body.ttrss_main #toolbar-frame #toolbar i { margin: 0 4px; } body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines { + font-size: 12px; + background: transparent; padding-right: 4px; flex-grow: 2; display: flex; @@ -714,7 +716,8 @@ body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left { display: flex; align-items: center; } -body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left #feed_title { +body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title, +body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .cancel_search { margin-left: 4px; } body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .right { diff --git a/themes/night_blue.css b/themes/night_blue.css index 02a66656b..3697cff05 100644 --- a/themes/night_blue.css +++ b/themes/night_blue.css @@ -705,6 +705,8 @@ body.ttrss_main #toolbar-frame #toolbar i { margin: 0 4px; } body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines { + font-size: 12px; + background: transparent; padding-right: 4px; flex-grow: 2; display: flex; @@ -714,7 +716,8 @@ body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left { display: flex; align-items: center; } -body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left #feed_title { +body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title, +body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .cancel_search { margin-left: 4px; } body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .right { -- cgit v1.2.3 From 9f31381bb6340349c83c394a6ffa15909a2ba3d1 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Mon, 15 Feb 2021 07:46:24 +0300 Subject: renderToolbar: support empty data i.e. dashboard feed --- classes/feeds.php | 2 +- js/Headlines.js | 86 +++++++++++++++++++++++++++++-------------------------- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/classes/feeds.php b/classes/feeds.php index 0a3e77a1a..ba320b52f 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -141,7 +141,7 @@ class Feeds extends Handler_Protected { $reply['toolbar'] = [ 'site_url' => $feed_site_url, - 'title' => truncate_string(strip_tags($feed_title), 30), + 'title' => strip_tags($feed_title), 'error' => $last_error, 'last_updated' => $last_updated, 'plugin_menu_items' => $plugin_menu_items, diff --git a/js/Headlines.js b/js/Headlines.js index 954b6b45f..94541b974 100755 --- a/js/Headlines.js +++ b/js/Headlines.js @@ -572,49 +572,53 @@ const Headlines = { const search_query = Feeds._search_query ? Feeds._search_query.query : ""; const target = dijit.byId('toolbar-headlines'); - target.attr('innerHTML', - ` - - - rss_feed - - ${tb.site_url ? - `${tb.title}` : - `${tb.title}`} - ${search_query ? - ` - (${__("Cancel search")}) - ` : ''} - ${tb.error ? `error` : ''} - - - - -
- ${__("Select...")} -
-
${__('All')}
-
${__('Unread')}
-
${__('Invert')}
-
${__('None')}
-
-
${__('Toggle unread')}
-
${__('Toggle starred')}
-
${__('Toggle published')}
-
-
${__('Mark as read')}
-
${__('Set score')}
- ${tb.plugin_menu_items} - ${headlines.id === 0 && !headlines.is_cat ? + if (tb && typeof tb == 'object') { + target.attr('innerHTML', + ` + + + rss_feed + + ${tb.site_url ? + `${tb.title}` : + `${tb.title}`} + ${search_query ? ` -
-
${__('Delete permanently')}
+ (${__("Cancel search")}) ` : ''} -
- ${tb.plugin_buttons} - - `); + ${tb.error ? `error` : ''} + + + + +
+ ${__("Select...")} +
+
${__('All')}
+
${__('Unread')}
+
${__('Invert')}
+
${__('None')}
+
+
${__('Toggle unread')}
+
${__('Toggle starred')}
+
${__('Toggle published')}
+
+
${__('Mark as read')}
+
${__('Set score')}
+ ${tb.plugin_menu_items} + ${headlines.id === 0 && !headlines.is_cat ? + ` +
+
${__('Delete permanently')}
+ ` : ''} +
+ ${tb.plugin_buttons} + + `); + } else { + target.attr('innerHTML', ''); + } dojo.parser.parse(target.domNode); }, -- cgit v1.2.3 From 3c584376ca24e4bc5220c8dac4f1e238a245e7fe Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Mon, 15 Feb 2021 07:59:11 +0300 Subject: shared opml and feed dialogs: remove unique target element id, move associated methods into dialog --- js/CommonDialogs.js | 91 ++++++++++++++++++++++++++++++++++------------------- js/PrefHelpers.js | 25 --------------- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/js/CommonDialogs.js b/js/CommonDialogs.js index e6b1822c2..60c3e2a84 100644 --- a/js/CommonDialogs.js +++ b/js/CommonDialogs.js @@ -369,35 +369,6 @@ const CommonDialogs = { dialog.show(); }, - genUrlChangeKey: function(feed, is_cat) { - if (confirm(__("Generate new syndication address for this feed?"))) { - - Notify.progress("Trying to change address...", true); - - const query = {op: "pref-feeds", method: "regenFeedKey", id: feed, is_cat: is_cat}; - - xhrJson("backend.php", query, (reply) => { - const new_link = reply.link; - const e = $('gen_feed_url'); - - if (new_link) { - e.innerHTML = e.innerHTML.replace(/&key=.*$/, - "&key=" + new_link); - - e.href = e.href.replace(/&key=.*$/, - "&key=" + new_link); - - new Effect.Highlight(e); - - Notify.close(); - - } else { - Notify.error("Could not change feed URL."); - } - }); - } - return false; - }, publishedOPML: function() { Notify.progress("Loading, please wait...", true); @@ -406,15 +377,40 @@ const CommonDialogs = { try { const dialog = new fox.SingleUseDialog({ title: __("Public OPML URL"), + regenOPMLKey: function() { + if (confirm(__("Replace current OPML publishing address with a new one?"))) { + Notify.progress("Trying to change address...", true); + + xhrJson("backend.php", {op: "pref-feeds", method: "regenOPMLKey"}, (reply) => { + if (reply) { + const new_link = reply.link; + const target = this.domNode.querySelector('.generated_url'); + + if (new_link && target) { + target.href = new_link; + target.innerHTML = new_link; + + new Effect.Highlight(target); + + Notify.close(); + + } else { + Notify.error("Could not change feed URL."); + } + } + }); + } + return false; + }, content: `
${__("Your Public OPML URL is:")}
- -
"; + PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_ENCLOSURE_ENTRY, + function ($result) use (&$enc) { + $enc = $result; + }, + $enc, $id); + array_push($rv['entries'], $enc); } return $rv; @@ -613,7 +530,7 @@ class Article extends Handler_Protected { } } - static function get_article_enclosures($id) { + static function get_enclosures($id) { $pdo = Db::pdo(); @@ -625,7 +542,7 @@ class Article extends Handler_Protected { $cache = new DiskCache("images"); - while ($line = $sth->fetch()) { + while ($line = $sth->fetch(PDO::FETCH_ASSOC)) { if ($cache->exists(sha1($line["content_url"]))) { $line["content_url"] = $cache->getUrl(sha1($line["content_url"])); diff --git a/classes/feeds.php b/classes/feeds.php index ba320b52f..d49fe7698 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -283,8 +283,10 @@ class Feeds extends Handler_Protected { $this->mark_timestamp(" pre-enclosures"); - $line["enclosures"] = Article::format_article_enclosures($id, $line["always_display_enclosures"], - $line["content"], $line["hide_images"]); + $line["enclosures"] = Article::format_enclosures($id, + $line["always_display_enclosures"], + $line["content"], + $line["hide_images"]); $this->mark_timestamp(" enclosures"); diff --git a/classes/handler/public.php b/classes/handler/public.php index 79f3a9e6c..0613e9a28 100755 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -131,7 +131,7 @@ class Handler_Public extends Handler { $tpl->addBlock('category'); } - $enclosures = Article::get_article_enclosures($line["id"]); + $enclosures = Article::get_enclosures($line["id"]); if (count($enclosures) > 0) { foreach ($enclosures as $e) { @@ -218,7 +218,7 @@ class Handler_Public extends Handler { } } - $enclosures = Article::get_article_enclosures($line["id"]); + $enclosures = Article::get_enclosures($line["id"]); if (count($enclosures) > 0) { $article['enclosures'] = array(); @@ -356,9 +356,7 @@ class Handler_Public extends Handler { $line['content'] = DiskCache::rewriteUrls($line['content']); - $enclosures = Article::get_article_enclosures($line["id"]); - - header("Content-Type: text/html"); + header("Content-Type: text/html"); $rv .= " @@ -392,6 +390,7 @@ class Handler_Public extends Handler { $rv .= ""; + $enclosures = Article::get_enclosures($line["id"]); list ($og_image, $og_stream) = Article::get_article_image($enclosures, $line['content'], $line["site_url"]); if ($og_image) { @@ -436,10 +435,10 @@ class Handler_Public extends Handler { $rv .= $line["content"]; - $rv .= Article::format_article_enclosures($id, + /* $rv .= Article::format_article_enclosures($id, $line["always_display_enclosures"], $line["content"], - $line["hide_images"]); + $line["hide_images"]); */ $rv .= "
"; # content diff --git a/classes/rpc.php b/classes/rpc.php index f6b57775d..e0753a08e 100755 --- a/classes/rpc.php +++ b/classes/rpc.php @@ -192,9 +192,7 @@ class RPC extends Handler_Protected { } function sanityCheck() { - $_SESSION["hasAudio"] = clean($_REQUEST["hasAudio"]) === "true"; $_SESSION["hasSandbox"] = clean($_REQUEST["hasSandbox"]) === "true"; - $_SESSION["hasMp3"] = clean($_REQUEST["hasMp3"]) === "true"; $_SESSION["clientTzOffset"] = clean($_REQUEST["clientTzOffset"]); $reply = array(); diff --git a/include/controls.php b/include/controls.php index e6678db9a..f706931db 100755 --- a/include/controls.php +++ b/include/controls.php @@ -298,37 +298,6 @@ function print_error($msg) { return print format_error($msg); } -function format_inline_player($url, $ctype) { - - $entry = ""; - - $url = htmlspecialchars($url); - - if (strpos($ctype, "audio/") === 0) { - - $entry .= "
"; - - if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false || - $_SESSION["hasMp3"])) { - - $entry .= " "; - - } - - if ($entry) $entry .= "" . basename($url) . ""; - - $entry .= "
"; - - return $entry; - - } - - return ""; -} - function print_label_select($name, $value, $attributes = "") { $pdo = Db::pdo(); diff --git a/js/App.js b/js/App.js index 0a72d1b12..3a31438c5 100644 --- a/js/App.js +++ b/js/App.js @@ -586,6 +586,10 @@ const App = { isPrefs() { return this.is_prefs; }, + audioCanPlay: function(ctype) { + const a = document.createElement('audio'); + return a.canPlayType(ctype); + }, init: function(parser, is_prefs) { this.is_prefs = is_prefs; window.onerror = this.Error.onWindowError; @@ -604,17 +608,11 @@ const App = { this.initHotkeyActions(); this.enableCsrfSupport(); - const a = document.createElement('audio'); - const hasAudio = !!a.canPlayType; - const hasSandbox = "sandbox" in document.createElement("iframe"); - const hasMp3 = !!(a.canPlayType && a.canPlayType('audio/mpeg;').replace(/no/, '')); - const clientTzOffset = new Date().getTimezoneOffset() * 60; - const params = { - op: "rpc", method: "sanityCheck", hasAudio: hasAudio, - hasMp3: hasMp3, - clientTzOffset: clientTzOffset, - hasSandbox: hasSandbox + op: "rpc", + method: "sanityCheck", + clientTzOffset: new Date().getTimezoneOffset() * 60, + hasSandbox: "sandbox" in document.createElement("iframe") }; xhrPost("backend.php", params, (transport) => { diff --git a/js/Article.js b/js/Article.js index f8b0415b9..efe33bc99 100644 --- a/js/Article.js +++ b/js/Article.js @@ -140,6 +140,62 @@ const Article = { Headlines.toggleUnread(id, 0); }, + renderEnclosures: function (enclosures) { + + // enclosure list was handled by backend (HOOK_FORMAT_ENCLOSURES) + if (enclosures.formatted) + return enclosures.formatted; + + return ` + ${enclosures.can_inline ? + `
+ ${enclosures.entries.map((enc) => { + if (!enclosures.inline_text_only) { + if (enc.content_type && enc.content_type.indexOf("image/") != -1) { + return `

+ +

` + } else if (enc.content_type && enc.content_type.indexOf("audio/") != -1 && App.audioCanPlay(enc.content_type)) { + return `

+ +

+ `; + } else { + return `

+ ${App.escapeHtml(enc.content_url)} +

` + } + } else { + return `

+ ${App.escapeHtml(enc.content_url)} +

` + } + }).join("")} +
` : ''} + ${enclosures.entries.length > 0 ? + `
+ ${__('Attachments')} +
+ ${enclosures.entries.map((enc) => ` +
+ ${enc.title ? enc.title : enc.filename} +
+ `).join("")} +
+
` : ''} + ` + }, render: function (article) { App.cleanupMemory("content-insert"); @@ -241,7 +297,7 @@ const Article = {
${hl.note}
${hl.content} - ${hl.enclosures} + ${Article.renderEnclosures(hl.enclosures)}
`; diff --git a/js/Headlines.js b/js/Headlines.js index 94541b974..dadf79c1a 100755 --- a/js/Headlines.js +++ b/js/Headlines.js @@ -488,7 +488,7 @@ const Headlines = {
- ${hl.enclosures} + ${Article.renderEnclosures(hl.enclosures)}
"; - + ?> +
+ + +
+ 0) { - $article["content"] = "
-
"; + $article["content"] = "
". + \Controls\button_tag(__("Not work safe (click to toggle)"), '', ['onclick' => 'Plugins.NSFW.toggle(this)']). + " +
"; } 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"] = "
-
"; - } - - return $article; + return $this->hook_render_article($article); } function hook_prefs_tab($args) { if ($args != "prefPrefs") return; - print "
extension ".__("NSFW Plugin")."\">"; - - print "
"; - $tags = $this->host->get($this, "tags"); - print ""; - - print ""; - - print \Controls\hidden_tag("op", "pluginhandler"); - print \Controls\hidden_tag("method", "save"); - print \Controls\hidden_tag("plugin", "nsfw"); - - print ""; - - print ""; - print ""; + - print "
".__("Tags to consider NSFW (comma-separated)")."
"; +
- print "

"; +

+ +
- print ""; +
- print "
"; #pane + + +
+ host->set($this, "tags", $tags); diff --git a/plugins/share/init.php b/plugins/share/init.php index 42923ed8a..846e1f39c 100644 --- a/plugins/share/init.php +++ b/plugins/share/init.php @@ -41,14 +41,14 @@ class Share extends Plugin { function hook_prefs_tab_section($id) { if ($id == "prefFeedsPublishedGenerated") { + ?> +
- print "
"; - - print "

" . __("You can disable all articles shared by unique URLs here.") . "

"; - - print " "; +

+ + execute([$uuid, $param, $_SESSION['uid']]); } - print "
" . __("You can share this article by the following unique URL:") . "
"; + $url_path = htmlspecialchars(get_self_url_prefix() . "/public.php?op=share&key=$uuid"); - $url_path = get_self_url_prefix(); - $url_path .= "/public.php?op=share&key=$uuid"; + ?> - print "
-
- $url_path -
-
"; +
- /* if (!label_find_id(__('Shared'), $_SESSION["uid"])) - label_create(__('Shared'), $_SESSION["uid"]); - label_add_article($ref_id, __('Shared'), $_SESSION['uid']); */ +
+
+ +
+
+ "; - - print ""; - - print ""; - - print ""; - - print ""; + ?> +
+ 'alt-danger', 'onclick' => "App.dialogOf(this).unshare()"]) ?> + "App.dialogOf(this).newurl()"]) ?> + +
+ Date: Wed, 17 Feb 2021 08:52:39 +0300 Subject: share plugin: cleanup, fix icon not highlighting properly --- plugins/share/init.php | 37 ++++++++++++---------------- plugins/share/share.css | 2 +- plugins/share/share.js | 58 +++++++++++++++++++++++--------------------- plugins/share/share_prefs.js | 8 +++--- 4 files changed, 52 insertions(+), 53 deletions(-) diff --git a/plugins/share/init.php b/plugins/share/init.php index 846e1f39c..a569393fe 100644 --- a/plugins/share/init.php +++ b/plugins/share/init.php @@ -17,18 +17,17 @@ class Share extends Plugin { } 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,7 +35,7 @@ class Share extends Plugin { AND owner_uid = ?"); $sth->execute([$id, $_SESSION['uid']]); - print "OK"; + print __("Article unshared"); } function hook_prefs_tab_section($id) { @@ -52,16 +51,14 @@ class Share extends Plugin { } } - // 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 +67,25 @@ 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 "link"; } - function shareArticle() { - $param = $_REQUEST['param']; + 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,27 +93,26 @@ 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']]); } - $url_path = htmlspecialchars(get_self_url_prefix() . "/public.php?op=share&key=$uuid"); + $url_path = get_self_url_prefix() . "/public.php?op=share&key=$uuid"; ?>
-
- +
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..09fb145c9 100644 --- a/plugins/share/share.js +++ b/plugins/share/share.js @@ -1,9 +1,7 @@ -/* global Plugins, xhrJson, Notify, fox, xhrPost, __ */ +/* global dojo, Effect, Plugins, xhrJson, Notify, fox, xhrPost, __ */ Plugins.Share = { shareArticle: function(id) { - const query = "backend.php?op=pluginhandler&plugin=share&method=shareArticle¶m=" + encodeURIComponent(id); - const dialog = new fox.SingleUseDialog({ id: "shareArticleDlg", title: __("Share article by URL"), @@ -17,20 +15,23 @@ Plugins.Share = { xhrJson("backend.php", query, (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(/\&key=.*$/, + target.innerHTML = target.innerHTML.replace(/&key=.*$/, "&key=" + new_link); - e.href = e.href.replace(/\&key=.*$/, + target.href = target.href.replace(/&key=.*$/, "&key=" + new_link); - new Effect.Highlight(e); + // eslint-disable-next-line no-new + new Effect.Highlight(target); + + 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 +45,35 @@ Plugins.Share = { }, unshare: function () { if (confirm(__("Remove sharing for this article?"))) { + xhrPost("backend.php", {op: "pluginhandler", plugin: "share", method: "unshare", id: id}, (transport) => { + Notify.info(transport.responseText); - 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"); + xhrPost("backend.php", {op: "pluginhandler", plugin: "share", method: "shareDialog", id: id}, (transport) => { + dialog.attr('content', transport.responseText) + + 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..29c9aeaf8 100644 --- a/plugins/share/share_prefs.js +++ b/plugins/share/share_prefs.js @@ -1,12 +1,12 @@ +/* global Plugins, Notify, xhrPost */ + 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."); + xhrPost("backend.php", {op: "pluginhandler", plugin: "share", method: "clearArticleKeys"}, (transport) => { + Notify.info(transport.responseText); }); } -- cgit v1.2.3 From 273ada7353b185e20452d54a8206d5e0cef9e573 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 17 Feb 2021 09:59:14 +0300 Subject: * implement shortcut syntax for exposed plugin methods * move shared article rendering code to share plugin --- classes/handler/public.php | 162 ------------------------------------------- classes/pluginhost.php | 5 +- plugins/share/init.php | 169 ++++++++++++++++++++++++++++++++++++++++++++- public.php | 11 +++ 4 files changed, 180 insertions(+), 167 deletions(-) diff --git a/classes/handler/public.php b/classes/handler/public.php index b0bed5d1c..b810019c1 100755 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -294,168 +294,6 @@ class Handler_Public extends Handler { } } - function share() { - $uuid = clean($_REQUEST["key"]); - - if ($uuid) { - $sth = $this->pdo->prepare("SELECT ref_id, owner_uid - FROM ttrss_user_entries WHERE uuid = ?"); - $sth->execute([$uuid]); - - if ($row = $sth->fetch()) { - header("Content-Type: text/html"); - - $id = $row["ref_id"]; - $owner_uid = $row["owner_uid"]; - - print $this->format_article($id, $owner_uid); - - return; - } - } - - header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found"); - print "Article not found."; - } - - private function format_article($id, $owner_uid) { - - $pdo = Db::pdo(); - - $sth = $pdo->prepare("SELECT id,title,link,content,feed_id,comments,int_id,lang, - ".SUBSTRING_FOR_DATE."(updated,1,16) as updated, - (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url, - (SELECT title FROM ttrss_feeds WHERE id = feed_id) as feed_title, - (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images, - (SELECT always_display_enclosures FROM ttrss_feeds WHERE id = feed_id) as always_display_enclosures, - num_comments, - tag_cache, - author, - guid, - note - FROM ttrss_entries,ttrss_user_entries - WHERE id = ? AND ref_id = id AND owner_uid = ?"); - $sth->execute([$id, $owner_uid]); - - $rv = ''; - - if ($line = $sth->fetch()) { - - $line["tags"] = Article::_get_tags($id, $owner_uid, $line["tag_cache"]); - unset($line["tag_cache"]); - - $line["content"] = Sanitizer::sanitize($line["content"], - $line['hide_images'], - $owner_uid, $line["site_url"], false, $line["id"]); - - PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_RENDER_ARTICLE, - function ($result) use (&$line) { - $line = $result; - }, - $line); - - $line['content'] = DiskCache::rewrite_urls($line['content']); - - header("Content-Type: text/html"); - - $rv .= " - - - ".$line["title"]."". - javascript_tag("lib/prototype.js"). - javascript_tag("js/utility.js")." - - - "; - - $rv .= "\n"; - $rv .= "\n"; - - $rv .= ""; - - $enclosures = Article::_get_enclosures($line["id"]); - list ($og_image, $og_stream) = Article::_get_image($enclosures, $line['content'], $line["site_url"]); - - if ($og_image) { - $rv .= ""; - } - - $rv .= ""; - $rv .= "
"; - - if ($line["link"]) { - $rv .= "

" . $line["title"] . "

"; - } else { - $rv .= "

" . $line["title"] . "

"; - } - - $rv .= "
"; - - /* header */ - - $rv .= "
"; - $rv .= "
"; # row - - //$entry_author = $line["author"] ? " - " . $line["author"] : ""; - $parsed_updated = TimeHelper::make_local_datetime($line["updated"], true, - $owner_uid, true); - - $rv .= "
".$line['author']."
"; - $rv .= "
$parsed_updated
"; - - $rv .= "
"; # row - - $rv .= "
"; # header - - /* content */ - - $lang = $line['lang'] ? $line['lang'] : "en"; - $rv .= "
"; - - /* content body */ - - $rv .= $line["content"]; - - /* $rv .= Article::format_article_enclosures($id, - $line["always_display_enclosures"], - $line["content"], - $line["hide_images"]); */ - - $rv .= "
"; # content - - $rv .= "
"; # post - - } - - PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_FORMAT_ARTICLE, - function ($result) use (&$rv) { - $rv = $result; - }, - $rv, $line); - - return $rv; - - } - function rss() { $feed = clean($_REQUEST["id"]); $key = clean($_REQUEST["key"]); diff --git a/classes/pluginhost.php b/classes/pluginhost.php index 6f223ee11..097bf987c 100755 --- a/classes/pluginhost.php +++ b/classes/pluginhost.php @@ -18,6 +18,7 @@ class PluginHost { private static $instance; const API_VERSION = 2; + const PUBLIC_METHOD_DELIMITER = "--"; // Hooks marked with *1 are run in global context and available // to plugins loaded in config.php only @@ -617,9 +618,7 @@ class PluginHost { http_build_query( array_merge( [ - "op" => "pluginhandler", - "plugin" => strtolower(get_class($sender)), - "pmethod" => $method + "op" => strtolower(get_class($sender) . PluginHost::PUBLIC_METHOD_DELIMITER . $method), ], $params)); } else { diff --git a/plugins/share/init.php b/plugins/share/init.php index a569393fe..6b7b81a2d 100644 --- a/plugins/share/init.php +++ b/plugins/share/init.php @@ -16,6 +16,10 @@ class Share extends Plugin { $host->add_hook($host::HOOK_PREFS_TAB_SECTION, $this); } + function is_public_method($method) { + return $method == "get"; + } + function get_js() { return file_get_contents(__DIR__ . "/share.js"); } @@ -78,6 +82,168 @@ class Share extends Plugin { title='".__('Share by URL')."'>link"; } + function get() { + $uuid = clean($_REQUEST["key"] ?? ""); + + if ($uuid) { + $sth = $this->pdo->prepare("SELECT ref_id, owner_uid + FROM ttrss_user_entries WHERE uuid = ?"); + $sth->execute([$uuid]); + + if ($row = $sth->fetch()) { + header("Content-Type: text/html"); + + $id = $row["ref_id"]; + $owner_uid = $row["owner_uid"]; + + print $this->format_article($id, $owner_uid); + + return; + } + } + + header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found"); + print "Article not found."; + } + + private function format_article($id, $owner_uid) { + + $pdo = Db::pdo(); + + $sth = $pdo->prepare("SELECT id,title,link,content,feed_id,comments,int_id,lang, + ".SUBSTRING_FOR_DATE."(updated,1,16) as updated, + (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url, + (SELECT title FROM ttrss_feeds WHERE id = feed_id) as feed_title, + (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images, + (SELECT always_display_enclosures FROM ttrss_feeds WHERE id = feed_id) as always_display_enclosures, + num_comments, + tag_cache, + author, + guid, + note + FROM ttrss_entries,ttrss_user_entries + WHERE id = ? AND ref_id = id AND owner_uid = ?"); + $sth->execute([$id, $owner_uid]); + + $rv = ''; + + if ($line = $sth->fetch()) { + + $line["tags"] = Article::_get_tags($id, $owner_uid, $line["tag_cache"]); + unset($line["tag_cache"]); + + $line["content"] = Sanitizer::sanitize($line["content"], + $line['hide_images'], + $owner_uid, $line["site_url"], false, $line["id"]); + + PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_RENDER_ARTICLE, + function ($result) use (&$line) { + $line = $result; + }, + $line); + + $line['content'] = DiskCache::rewrite_urls($line['content']); + + header("Content-Type: text/html"); + + $rv .= " + + + ".$line["title"]."". + javascript_tag("lib/prototype.js"). + javascript_tag("js/utility.js")." + + + "; + + $rv .= "\n"; + $rv .= "\n"; + + $rv .= ""; + + $enclosures = Article::_get_enclosures($line["id"]); + list ($og_image, $og_stream) = Article::_get_image($enclosures, $line['content'], $line["site_url"]); + + if ($og_image) { + $rv .= ""; + } + + $rv .= ""; + $rv .= "
"; + + if ($line["link"]) { + $rv .= "

" . $line["title"] . "

"; + } else { + $rv .= "

" . $line["title"] . "

"; + } + + $rv .= "
"; + + /* header */ + + $rv .= "
"; + $rv .= "
"; # row + + //$entry_author = $line["author"] ? " - " . $line["author"] : ""; + $parsed_updated = TimeHelper::make_local_datetime($line["updated"], true, + $owner_uid, true); + + $rv .= "
".$line['author']."
"; + $rv .= "
$parsed_updated
"; + + $rv .= "
"; # row + + $rv .= "
"; # header + + /* content */ + + $lang = $line['lang'] ? $line['lang'] : "en"; + $rv .= "
"; + + /* content body */ + + $rv .= $line["content"]; + + /* $rv .= Article::format_article_enclosures($id, + $line["always_display_enclosures"], + $line["content"], + $line["hide_images"]); */ + + $rv .= "
"; # content + + $rv .= "
"; # post + + } + + PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_FORMAT_ARTICLE, + function ($result) use (&$rv) { + $rv = $result; + }, + $rv, $line); + + return $rv; + + } + function shareDialog() { $id = (int)clean($_REQUEST['id'] ?? 0); @@ -96,8 +262,7 @@ class Share extends Plugin { $sth->execute([$uuid, $id, $_SESSION['uid']]); } - $url_path = get_self_url_prefix() . "/public.php?op=share&key=$uuid"; - + $url_path = $this->host->get_public_method_url($this, "get", ["key" => $uuid]); ?>
diff --git a/public.php b/public.php index 59b5a499c..fadb2f14d 100644 --- a/public.php +++ b/public.php @@ -17,6 +17,17 @@ $method = (string)clean($_REQUEST["op"]); + // shortcut syntax for public (exposed) methods (?op=plugin--pmethod&...params) + if (strpos($method, PluginHost::PUBLIC_METHOD_DELIMITER) !== false) { + list ($plugin, $pmethod) = explode(PluginHost::PUBLIC_METHOD_DELIMITER, $method, 2); + + // TODO: better implementation that won't modify $_REQUEST + $_REQUEST["plugin"] = $plugin; + $_REQUEST["pmethod"] = $pmethod; + + $method = "pluginhandler"; + } + $override = PluginHost::getInstance()->lookup_handler("public", $method); if ($override) { -- cgit v1.2.3 From 4325c30a3f7574ed2b1cc3fcf41a08d92c0ccc49 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 17 Feb 2021 12:10:19 +0300 Subject: share: markup cleanup --- plugins/share/init.php | 148 ++++++++++++++++++++++--------------------------- 1 file changed, 65 insertions(+), 83 deletions(-) diff --git a/plugins/share/init.php b/plugins/share/init.php index 6b7b81a2d..4c47e29d3 100644 --- a/plugins/share/init.php +++ b/plugins/share/init.php @@ -96,7 +96,7 @@ class Share extends Plugin { $id = $row["ref_id"]; $owner_uid = $row["owner_uid"]; - print $this->format_article($id, $owner_uid); + $this->format_article($id, $owner_uid); return; } @@ -125,8 +125,6 @@ class Share extends Plugin { WHERE id = ? AND ref_id = id AND owner_uid = ?"); $sth->execute([$id, $owner_uid]); - $rv = ''; - if ($line = $sth->fetch()) { $line["tags"] = Article::_get_tags($id, $owner_uid, $line["tag_cache"]); @@ -142,106 +140,90 @@ class Share extends Plugin { }, $line); + $enclosures = Article::_get_enclosures($line["id"]); + list ($og_image, $og_stream) = Article::_get_image($enclosures, $line['content'], $line["site_url"]); + + $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']); - header("Content-Type: text/html"); + ob_start(); - $rv .= " - - - ".$line["title"]."". - javascript_tag("lib/prototype.js"). - javascript_tag("js/utility.js")." + ?> + + + + + <?= $line["title"] ?> + + - - "; - - $rv .= "\n"; - $rv .= " + + + + \n"; - - $rv .= ""; - - $enclosures = Article::_get_enclosures($line["id"]); - list ($og_image, $og_stream) = Article::_get_image($enclosures, $line['content'], $line["site_url"]); - - if ($og_image) { - $rv .= ""; - } - - $rv .= ""; - $rv .= "
"; - - if ($line["link"]) { - $rv .= "

" . $line["title"] . "

"; - } else { - $rv .= "

" . $line["title"] . "

"; - } - - $rv .= "
"; - - /* header */ - - $rv .= "
"; - $rv .= "
"; # row - - //$entry_author = $line["author"] ? " - " . $line["author"] : ""; - $parsed_updated = TimeHelper::make_local_datetime($line["updated"], true, - $owner_uid, true); - - $rv .= "
".$line['author']."
"; - $rv .= "
$parsed_updated
"; - - $rv .= "
"; # row - - $rv .= "
"; # header - - /* content */ - - $lang = $line['lang'] ? $line['lang'] : "en"; - $rv .= "
"; - - /* content body */ - - $rv .= $line["content"]; - - /* $rv .= Article::format_article_enclosures($id, - $line["always_display_enclosures"], - $line["content"], - $line["hide_images"]); */ - - $rv .= "
"; # content + ), 500, "...")) ?>"> + + + + + + + +
+ + +

+ "> +

+ +

+ + +
+
+
+
+
+
+
+ +
"> + +
+
+ + + "; # post + $rv = ob_get_contents(); + ob_end_clean(); - } - - PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_FORMAT_ARTICLE, + PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_FORMAT_ARTICLE, function ($result) use (&$rv) { $rv = $result; }, $rv, $line); - return $rv; - + print $rv; + } } function shareDialog() { -- cgit v1.2.3 From 9ac6741d2476944b77e8665e8ff9a9d811848908 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 17 Feb 2021 12:25:33 +0300 Subject: af_comics: markup cleanup --- plugins/af_comics/init.php | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) 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 "
photo ".__('Feeds supported by af_comics')."\">"; - - print "

" . __("The following comics are currently supported:") . "

"; - $comics = []; foreach ($this->filters as $f) { @@ -62,17 +57,22 @@ class Af_Comics extends Plugin { asort($comics); - print "
    "; - foreach ($comics as $comic) { - print "
  • $comic
  • "; - } - print "
"; + ?> +
- print_notice("To subscribe to GoComics use the comic's regular web page as the feed URL (e.g. for the Garfield comic use http://www.gocomics.com/garfield)."); +

- print_notice('Drop any updated filters into filters.local in plugin directory.'); +
    + +
  • + +
- print "
"; + Garfield comic use http://www.gocomics.com/garfield).") ?> + filters.local in plugin directory.') ?> +
+ Date: Wed, 17 Feb 2021 12:36:02 +0300 Subject: af_readability: cleanup markup --- plugins/af_readability/init.php | 132 ++++++++++++++++++++-------------------- 1 file changed, 65 insertions(+), 67 deletions(-) diff --git a/plugins/af_readability/init.php b/plugins/af_readability/init.php index b9c4e252e..84cfe869a 100755 --- a/plugins/af_readability/init.php +++ b/plugins/af_readability/init.php @@ -55,79 +55,77 @@ class Af_Readability extends Plugin { function hook_prefs_tab($args) { if ($args != "prefFeeds") return; - print "
extension ".__('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 { - - print "

" . __("Global settings") . "

"; - - print_notice("Enable for specific feeds in the feed editor."); - - print "
"; - - print ""; - - print \Controls\hidden_tag("op", "pluginhandler"); - print \Controls\hidden_tag("method", "save"); - print \Controls\hidden_tag("plugin", "af_readability"); - - $enable_share_anything = sql_bool_to_bool($this->host->get($this, "enable_share_anything")); - - print "
"; - print ""; - print "
"; + ?> +
- print "
"; +

- print \Controls\submit_tag(__("Save")); - print ""; + - /* 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 "
"; - print "

" . __("Currently enabled for (click to edit):") . "

"; - - print ""; - } - - } - - print "
"; + + +
+ +
+ +
+ + + + + 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); + ?> + + 0) { ?> +
+

+ +
    + +
  • + rss_feed + + + +
  • + +
+ +
+ Date: Wed, 17 Feb 2021 13:35:10 +0300 Subject: af_redditimgur: cleanup markup --- plugins/af_redditimgur/init.php | 101 ++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/plugins/af_redditimgur/init.php b/plugins/af_redditimgur/init.php index 1c520b7ed..1fa61953a 100755 --- a/plugins/af_redditimgur/init.php +++ b/plugins/af_redditimgur/init.php @@ -31,60 +31,61 @@ class Af_RedditImgur extends Plugin { function hook_prefs_tab($args) { if ($args != "prefFeeds") return; - print "
extension ".__('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"); +
+ +
- if (version_compare(PHP_VERSION, '5.6.0', '<')) { - print_error("Readability requires PHP version 5.6."); - } +
+ +
- print ""; +
+ +
- print ""; - - print \Controls\hidden_tag("op", "pluginhandler"); - print \Controls\hidden_tag("method", "save"); - print \Controls\hidden_tag("plugin", "af_redditimgur"); - - print "
"; - print ""; - print "
"; - - print "
"; - print ""; - print "
"; - - print "
"; - print ""; - - print "
"; - print \Controls\submit_tag(__("Save")); - print ""; - - print "
"; +
+ + +
+ + Date: Wed, 17 Feb 2021 13:36:24 +0300 Subject: RIP af_tumblr_1280 --- plugins/af_tumblr_1280/init.php | 91 ----------------------------------------- 1 file changed, 91 deletions(-) delete mode 100755 plugins/af_tumblr_1280/init.php 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 @@ - 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('' . $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; - } - -} -- cgit v1.2.3 From 3c14eed1c2e0ad80b521e874762f9a326c33cce5 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 17 Feb 2021 13:45:38 +0300 Subject: close_button: fix color not applying --- plugins/close_button/init.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) { -- cgit v1.2.3 From 00b31c3f53db740984220bd9a745f76032890bea Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 17 Feb 2021 13:55:58 +0300 Subject: af_readability: cleanup markup --- plugins/af_readability/init.php | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/plugins/af_readability/init.php b/plugins/af_readability/init.php index 84cfe869a..aeef8cddc 100755 --- a/plugins/af_readability/init.php +++ b/plugins/af_readability/init.php @@ -129,26 +129,26 @@ class Af_Readability extends Plugin { } function hook_prefs_edit_feed($feed_id) { - print "
".__("Readability")."
"; - print "
"; - $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 "
"; - - print ""; - - print "
"; - - print ""; - - print "
"; +
+
+
+ +
+
+ +
+
+ Date: Wed, 17 Feb 2021 14:05:12 +0300 Subject: pluginhandlers: post notice if pluginmethod is requested without CSRF token --- classes/pluginhandler.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/classes/pluginhandler.php b/classes/pluginhandler.php index 9682e440f..3fd823aa8 100644 --- a/classes/pluginhandler.php +++ b/classes/pluginhandler.php @@ -7,16 +7,22 @@ class PluginHandler extends Handler_Protected { function catchall($method) { $plugin_name = clean($_REQUEST["plugin"]); $plugin = PluginHost::getInstance()->get_plugin($plugin_name); + $csrf_token = ($_POST["csrf_token"] ?? ""); if ($plugin) { if (method_exists($plugin, $method)) { - $plugin->$method(); + if (validate_csrf($csrf_token)) { + $plugin->$method(); + } else { + user_error("Requested ${plugin_name}->${method}() with invalid CSRF token.", E_USER_DEPRECATED); + $plugin->$method(); + } } else { - user_error("PluginHandler: Requested unknown method '$method' of plugin '$plugin_name'.", E_USER_WARNING); + user_error("Rejected ${plugin_name}->${method}(): unknown method.", E_USER_WARNING); print error_json(13); } } else { - user_error("PluginHandler: Requested method '$method' of unknown plugin '$plugin_name'.", E_USER_WARNING); + user_error("Rejected ${plugin_name}->${method}(): unknown plugin.", E_USER_WARNING); print error_json(14); } } -- cgit v1.2.3 From 538f87e41551745a49cacf41c1a5d3772d3581a6 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 17 Feb 2021 14:08:06 +0300 Subject: af_psql_trgm: don't load dialog via http --- plugins/af_psql_trgm/init.js | 17 ++++++++++------- plugins/af_psql_trgm/init.php | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/plugins/af_psql_trgm/init.js b/plugins/af_psql_trgm/init.js index a22e673f6..e5bc21885 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, xhrPost, __ */ Plugins.Psql_Trgm = { showRelated: function (id) { - const query = "backend.php?op=pluginhandler&plugin=af_psql_trgm&method=showrelated¶m=" + 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); + + xhrPost("backend.php", {op: 'pluginhandler', plugin: 'af_psql_trgm', method: 'showrelated', id: id}, (transport) => { + dialog.attr('content', transport.responseText); + }); }); dialog.show(); diff --git a/plugins/af_psql_trgm/init.php b/plugins/af_psql_trgm/init.php index 3662e490a..d1a029adc 100644 --- a/plugins/af_psql_trgm/init.php +++ b/plugins/af_psql_trgm/init.php @@ -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 -- cgit v1.2.3 From e9c3118ddd2b08aff0285166ba608edd3fd5708f Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 17 Feb 2021 14:14:10 +0300 Subject: don't show E_USER_DEPRECATED on the frontpage --- classes/rpc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/rpc.php b/classes/rpc.php index d6284033a..6831d5667 100755 --- a/classes/rpc.php +++ b/classes/rpc.php @@ -507,7 +507,7 @@ class RPC extends Handler_Protected { $sth = $pdo->prepare("SELECT COUNT(id) AS cid FROM ttrss_error_log WHERE - errno != 1024 AND + errno NOT IN (".E_USER_NOTICE.", ".E_USER_DEPRECATED.") AND $log_interval AND errstr NOT LIKE '%imagecreatefromstring(): Data is not in a recognized format%'"); $sth->execute(); -- cgit v1.2.3 From 4632d6cf558774cfcb17b3b4cf10399c3626096b Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 17 Feb 2021 14:14:17 +0300 Subject: fix some php8 warnings --- plugins/af_psql_trgm/init.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/af_psql_trgm/init.php b/plugins/af_psql_trgm/init.php index d1a029adc..1d83ce5e0 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; -- cgit v1.2.3 From 2b2833bb4fa6f958b89a83adea89d9e7c73daee7 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 17 Feb 2021 14:56:36 +0300 Subject: plugins: load dialogs via xhr instead of http --- plugins/af_redditimgur/init.php | 6 +++++- plugins/mail/init.php | 35 ++++++++--------------------------- plugins/mail/mail.js | 19 ++++++++----------- plugins/mailto/init.js | 17 ++++++++++++----- plugins/mailto/init.php | 6 +++--- plugins/note/init.php | 6 +++--- plugins/note/note.js | 15 ++++++++++----- 7 files changed, 49 insertions(+), 55 deletions(-) diff --git a/plugins/af_redditimgur/init.php b/plugins/af_redditimgur/init.php index 1fa61953a..63a23cd36 100755 --- a/plugins/af_redditimgur/init.php +++ b/plugins/af_redditimgur/init.php @@ -647,7 +647,11 @@ class Af_RedditImgur extends Plugin { fieldset { border : 0; } label { display : inline-block; min-width : 120px; } -
+ + + + + ">
diff --git a/plugins/mail/init.php b/plugins/mail/init.php index c054196df..8a0d01aca 100644 --- a/plugins/mail/init.php +++ b/plugins/mail/init.php @@ -19,7 +19,7 @@ class Mail extends Plugin { } 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) { @@ -83,9 +83,11 @@ class Mail extends Plugin { function emailArticle() { - $ids = explode(",", $_REQUEST['param']); + $ids = explode(",", clean($_REQUEST['ids'])); $ids_qmarks = arr_qmarks($ids); + print ""; + print \Controls\hidden_tag("op", "pluginhandler"); print \Controls\hidden_tag("plugin", "mail"); print \Controls\hidden_tag("method", "sendEmail"); @@ -156,15 +158,8 @@ class Mail extends Plugin { print ""; -/* print ""; */ - print \Controls\select_tag("destination", "", $addresslist, - ["style" => "width: 30em", "dojoType" => "dijit.form.ComboBox"]); - -/* print "
"; */ + ["style" => "width: 30em", "required" => 1, "dojoType" => "dijit.form.ComboBox"]); print ""; @@ -184,11 +179,11 @@ class Mail extends Plugin { print ""; print "
"; - print " "; - print ""; + print \Controls\submit_tag(__('Send email')); + print \Controls\cancel_dialog_tag(__('Cancel')); print "
"; - //return; + print ""; } function sendEmail() { @@ -229,20 +224,6 @@ class Mail extends Plugin { print json_encode($reply); } - /* function completeEmails() { - $search = $_REQUEST["search"]; - - print "
    "; - - foreach ($_SESSION['stored_emails'] as $email) { - if (strpos($email, $search) !== false) { - print "
  • $email
  • "; - } - } - - print "
"; - } */ - function api_version() { return 2; } diff --git a/plugins/mail/mail.js b/plugins/mail/mail.js index 5ddc0dc41..4cdf6999d 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, xhrPost, xhrJson, Notify, fox, __ */ Plugins.Mail = { send: function(id) { @@ -13,10 +13,7 @@ Plugins.Mail = { id = ids.toString(); } - const query = "backend.php?op=pluginhandler&plugin=mail&method=emailArticle¶m=" + encodeURIComponent(id); - const dialog = new fox.SingleUseDialog({ - id: "emailArticleDlg", title: __("Forward article by email"), execute: function () { if (this.validate()) { @@ -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" }); - }); */ + xhrPost("backend.php", {op: "pluginhandler", plugin: "mail", method: "emailArticle", ids: id}, (transport) => { + dialog.attr('content', transport.responseText); + }); + }); dialog.show(); }, diff --git a/plugins/mailto/init.js b/plugins/mailto/init.js index ae68bf49b..4bf672a88 100644 --- a/plugins/mailto/init.js +++ b/plugins/mailto/init.js @@ -1,4 +1,4 @@ -/* global Plugins, Headlines, fox, __ */ +/* global Plugins, Headlines, xhrPost, 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¶m=" + encodeURIComponent(id); - const dialog = new fox.SingleUseDialog({ - id: "emailArticleDlg", title: __("Forward article by email"), - href: query}); + content: __("Loading, please wait...") + }); + + const tmph = dojo.connect(dialog, 'onShow', function () { + dojo.disconnect(tmph); + + xhrPost("backend.php", {op: "pluginhandler", plugin: "mailto", method: "emailArticle", ids: id}, (transport) => { + dialog.attr('content', transport.responseText); + }); + }); + dialog.show(); } diff --git a/plugins/mailto/init.php b/plugins/mailto/init.php index 3e24dcf29..4b858eae6 100644 --- a/plugins/mailto/init.php +++ b/plugins/mailto/init.php @@ -20,7 +20,7 @@ class MailTo extends Plugin { } function get_js() { - return file_get_contents(dirname(__FILE__) . "/init.js"); + return file_get_contents(__DIR__ . "/init.js"); } function hook_article_button($line) { @@ -31,7 +31,7 @@ class MailTo extends Plugin { function emailArticle() { - $ids = explode(",", $_REQUEST['param']); + $ids = explode(",", clean($_REQUEST['ids'])); $ids_qmarks = arr_qmarks($ids); $tpl = new Templator(); @@ -85,7 +85,7 @@ class MailTo extends Plugin { print "

"; print "

"; - print ""; + print \Controls\submit_tag(__('Close this dialog')); print "
"; //return; diff --git a/plugins/note/init.php b/plugins/note/init.php index 12c56f7ad..65e1f0eef 100644 --- a/plugins/note/init.php +++ b/plugins/note/init.php @@ -27,17 +27,17 @@ 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 \Controls\hidden_tag("id", "$param"); + print \Controls\hidden_tag("id", $id); print \Controls\hidden_tag("op", "pluginhandler"); print \Controls\hidden_tag("method", "setNote"); print \Controls\hidden_tag("plugin", "note"); diff --git a/plugins/note/note.js b/plugins/note/note.js index ab2ed9208..215058b21 100644 --- a/plugins/note/note.js +++ b/plugins/note/note.js @@ -1,11 +1,8 @@ -/* global Plugins, xhrJson, Notify, fox, __ */ +/* global dojo, xhrPost, Plugins, xhrJson, Notify, fox, __ */ Plugins.Note = { edit: function(id) { - const query = "backend.php?op=pluginhandler&plugin=note&method=edit¶m=" + encodeURIComponent(id); - const dialog = new fox.SingleUseDialog({ - id: "editNoteDlg", title: __("Edit article note"), execute: function () { if (this.validate()) { @@ -30,7 +27,15 @@ Plugins.Note = { }); } }, - href: query, + content: __("Loading, please wait...") + }); + + const tmph = dojo.connect(dialog, 'onShow', function () { + dojo.disconnect(tmph); + + xhrPost("backend.php", {op: "pluginhandler", plugin: "note", method: "edit", id: id}, (transport) => { + dialog.attr('content', transport.responseText); + }); }); dialog.show(); -- cgit v1.2.3 From 7be1e3ed38baf8233b7f6733db3f57859c1b2086 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 17 Feb 2021 15:04:39 +0300 Subject: pluginhandler: reject method requests without CSRF --- classes/pluginhandler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/pluginhandler.php b/classes/pluginhandler.php index 3fd823aa8..a0e60b4e6 100644 --- a/classes/pluginhandler.php +++ b/classes/pluginhandler.php @@ -14,8 +14,8 @@ class PluginHandler extends Handler_Protected { if (validate_csrf($csrf_token)) { $plugin->$method(); } else { - user_error("Requested ${plugin_name}->${method}() with invalid CSRF token.", E_USER_DEPRECATED); - $plugin->$method(); + user_error("Rejected ${plugin_name}->${method}(): invalid CSRF token.", E_USER_WARNING); + print error_json(6); } } else { user_error("Rejected ${plugin_name}->${method}(): unknown method.", E_USER_WARNING); -- cgit v1.2.3 From 2ac6508fe697ed5e95cc7bf73ffb9e2e0bf0d3fa Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 17 Feb 2021 15:53:00 +0300 Subject: mail, mailto: cleanup markup --- plugins/mail/init.php | 150 ++++++++++++++++++++++++++---------------------- plugins/mailto/init.js | 2 +- plugins/mailto/init.php | 33 +++++------ 3 files changed, 97 insertions(+), 88 deletions(-) diff --git a/plugins/mail/init.php b/plugins/mail/init.php index 8a0d01aca..bb576a4d9 100644 --- a/plugins/mail/init.php +++ b/plugins/mail/init.php @@ -37,42 +37,44 @@ class Mail extends Plugin { function hook_prefs_tab($args) { if ($args != "prefPrefs") return; - print "
mail ".__('Mail plugin')."\">"; - - print "

" . __("You can set predefined email addressed here (comma-separated list):") . "

"; - - print "
"; - - print ""; + - print \Controls\hidden_tag("op", "pluginhandler"); - print \Controls\hidden_tag("method", "save"); - print \Controls\hidden_tag("plugin", "mail"); +
- $addresslist = $this->host->get($this, "addresslist"); + - print ""; +
- print "

"; + - print "

"; + - print "
"; +
+ "; - - print \Controls\hidden_tag("op", "pluginhandler"); - print \Controls\hidden_tag("plugin", "mail"); - print \Controls\hidden_tag("method", "sendEmail"); $sth = $this->pdo->prepare("SELECT email, full_name FROM ttrss_users WHERE id = ?"); @@ -107,9 +104,6 @@ class Mail extends Plugin { if (!$user_name) $user_name = $_SESSION['name']; - print \Controls\hidden_tag("from_email", "$user_email"); - print \Controls\hidden_tag("from_name", "$user_name"); - $tpl = new Templator(); $tpl->readTemplateFromFile("email_article_template.txt"); @@ -150,40 +144,58 @@ class Mail extends Plugin { $content = ""; $tpl->generateOutputToString($content); - print ""; - - print "
"; - $addresslist = explode(",", $this->host->get($this, "addresslist")); - print __('To:'); - - print ""; - - print \Controls\select_tag("destination", "", $addresslist, - ["style" => "width: 30em", "required" => 1, "dojoType" => "dijit.form.ComboBox"]); - - print "
"; - - print __('Subject:'); - - print ""; - - print ""; - - print "
"; - - print "
"; - - print "
"; - print \Controls\submit_tag(__('Send email')); - print \Controls\cancel_dialog_tag(__('Cancel')); - print "
"; - - print ""; + ?> + +
+ + + + + + + + + + +
+
+ + "width: 380px", "required" => 1, "dojoType" => "dijit.form.ComboBox"]) ?> +
+
+ +
+
+ + +
+
+ + + +
+ + +
+ +
+ ".__('Forward by email')."
"; + return "
".__('Forward by email (mailto:)')."
"; } function get_js() { @@ -26,7 +26,7 @@ class MailTo extends Plugin { function hook_article_button($line) { return "mail_outline"; + title='".__('Forward by email (mailto:)')."'>mail_outline"; } function emailArticle() { @@ -42,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 = ?"); @@ -70,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 ""; + $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 "

"; +

+
+ + + +
+
- print "
"; - print \Controls\submit_tag(__('Close this dialog')); - print "
"; +
+ +
- //return; + Date: Wed, 17 Feb 2021 15:53:58 +0300 Subject: delete unused mail .pngs --- plugins/mail/mail.png | Bin 641 -> 0 bytes plugins/mailto/mail.png | Bin 821 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 plugins/mail/mail.png delete mode 100644 plugins/mailto/mail.png diff --git a/plugins/mail/mail.png b/plugins/mail/mail.png deleted file mode 100644 index 7348aed77..000000000 Binary files a/plugins/mail/mail.png and /dev/null differ diff --git a/plugins/mailto/mail.png b/plugins/mailto/mail.png deleted file mode 100644 index 2c49f78a6..000000000 Binary files a/plugins/mailto/mail.png and /dev/null differ -- cgit v1.2.3 From 6ecee2abbd96eac2b0efab259c184644b71d1449 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 17 Feb 2021 16:17:05 +0300 Subject: cache_starred_images: minor fixes --- plugins/cache_starred_images/init.php | 53 ++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/plugins/cache_starred_images/init.php b/plugins/cache_starred_images/init.php index 9c2d4cb7e..bd44a2b28 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, @@ -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']]); } @@ -71,7 +71,10 @@ class Cache_Starred_Images extends Plugin { Debug::log("expiring " . $this->cache->get_dir() . "..."); - $files = glob($this->cache->get_dir() . "/*.{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; @@ -105,7 +108,7 @@ class Cache_Starred_Images extends Plugin { } 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])'); @@ -158,30 +161,30 @@ class Cache_Starred_Images extends Plugin { 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 = []; - $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('' . $content)) { + if (@$doc->loadHTML('' . $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; -- cgit v1.2.3 From 35b6d63289dcce3be27127aec607c970b050a986 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 17 Feb 2021 16:27:52 +0300 Subject: af_proxy_http: don't try to proxy back to ourselves --- plugins/af_proxy_http/init.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/plugins/af_proxy_http/init.php b/plugins/af_proxy_http/init.php index 79d2f5294..5804e450f 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; } @@ -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))) { -- cgit v1.2.3 From 92cb91e2e22282e3ad9da2f5312d7815720af6b6 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 17 Feb 2021 16:33:28 +0300 Subject: search dialog: bring back id of language dropdown --- classes/feeds.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/feeds.php b/classes/feeds.php index 8448d2670..68edfbf89 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -608,7 +608,7 @@ class Feeds extends Handler_Protected { print "
"; print ""; print \Controls\select_tag("search_language", get_pref('DEFAULT_SEARCH_LANGUAGE'), Pref_Feeds::get_ts_languages(), - ["title" => __('Used for word stemming')]); + ["title" => __('Used for word stemming')], "search_language"); print "
"; } -- cgit v1.2.3 From b16abc157ee584f4be80a537ee24ec9e5ff25496 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 17 Feb 2021 19:34:54 +0300 Subject: * App: rename hidden to hidden_tag * search: use client dialog * add some form field helpers --- .eslintrc.js | 2 +- classes/feeds.php | 46 ++++++---------------------------------------- js/App.js | 40 +++++++++++++++++++++++++++++++++++----- js/Article.js | 6 +++--- js/CommonDialogs.js | 4 ++-- js/CommonFilters.js | 4 ++-- js/Feeds.js | 42 +++++++++++++++++++++++++++++++++++------- js/PrefFeedTree.js | 4 ++-- js/PrefHelpers.js | 6 +++--- js/PrefLabelTree.js | 10 +++++----- js/PrefUsers.js | 16 ++++++++-------- 11 files changed, 102 insertions(+), 78 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 6d76f6082..523ada08e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,7 +1,7 @@ module.exports = { "env": { "browser": true, - "es6": true, + "ecmaVersion": 8, "jquery": true, "webextensions": true }, diff --git a/classes/feeds.php b/classes/feeds.php index 68edfbf89..30478ae39 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -588,46 +588,12 @@ class Feeds extends Handler_Protected { } function search() { - $this->params = explode(":", $_REQUEST["param"], 2); - - $active_feed_id = sprintf("%d", $this->params[0]); - $is_cat = $this->params[1] != "false"; - - print "
"; - - print "
"; - - print "
"; - print "_get_title($active_feed_id, $is_cat))."\" - name='query' type='search' value=''>"; - print "
"; - - if (DB_TYPE == "pgsql") { - print "
"; - print ""; - print \Controls\select_tag("search_language", get_pref('DEFAULT_SEARCH_LANGUAGE'), Pref_Feeds::get_ts_languages(), - ["title" => __('Used for word stemming')], "search_language"); - print "
"; - } - - print "
"; - - print "
"; - - if (count(PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SEARCH)) == 0) { - print ""; - } - - print " - "; - - print "
"; - - print "
"; + print json_encode([ + "show_language" => DB_TYPE == "pgsql", + "show_syntax_help" => count(PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SEARCH)) == 0, + "all_languages" => Pref_Feeds::get_ts_languages(), + "default_language" => get_pref('DEFAULT_SEARCH_LANGUAGE') + ]); } function updatedebugger() { diff --git a/js/App.js b/js/App.js index bb861829d..9d8f6c275 100644 --- a/js/App.js +++ b/js/App.js @@ -18,14 +18,44 @@ const App = { is_prefs: false, LABEL_BASE_INDEX: -1024, FormFields: { - hidden: function(name, value, id = "") { - return `` + attributes_to_string: function(attributes) { + return Object.keys(attributes).map((k) => + `${App.escapeHtml(k)}="${App.escapeHtml(attributes[k])}"`) + .join(" "); }, - select_hash: function(name, value, values, attributes) { + hidden_tag: function(name, value, attributes = {}, id = "") { + return `` + }, + // allow html inside because of icons + button_tag: function(value, type, attributes = {}) { + return `` + + }, + icon: function(icon, attributes = {}) { + return `${icon}`; + }, + submit_tag: function(value, attributes = {}) { + return this.button_tag(value, "submit", {...{class: "alt-primary"}, ...attributes}); + }, + cancel_dialog_tag: function(value, attributes = {}) { + return this.button_tag(value, "", {...{onclick: "App.dialogOf(this).hide()"}, ...attributes}); + }, + select_tag: function(name, value, values = [], attributes = {}, id = "") { + return ` + + ` + }, + select_hash: function(name, value, values = {}, attributes = {}, id = "") { return ` - ${Object.keys(values).map((vk) => - `` + `` ).join("")} ` diff --git a/js/Article.js b/js/Article.js index 4d400e2dc..a42d3af67 100644 --- a/js/Article.js +++ b/js/Article.js @@ -314,9 +314,9 @@ const Article = { const dialog = new fox.SingleUseDialog({ title: __("Edit article Tags"), content: ` - ${App.FormFields.hidden("id", id.toString())} - ${App.FormFields.hidden("op", "article")} - ${App.FormFields.hidden("method", "setArticleTags")} + ${App.FormFields.hidden_tag("id", id.toString())} + ${App.FormFields.hidden_tag("op", "article")} + ${App.FormFields.hidden_tag("method", "setArticleTags")}
${__("Tags for this article (separated by commas):")} diff --git a/js/CommonDialogs.js b/js/CommonDialogs.js index c16afed82..dd0d56194 100644 --- a/js/CommonDialogs.js +++ b/js/CommonDialogs.js @@ -86,8 +86,8 @@ const CommonDialogs = { content: `
- ${App.FormFields.hidden("op", "feeds")} - ${App.FormFields.hidden("method", "add")} + ${App.FormFields.hidden_tag("op", "feeds")} + ${App.FormFields.hidden_tag("method", "add")} diff --git a/js/CommonFilters.js b/js/CommonFilters.js index 5afffafdc..e3629157b 100644 --- a/js/CommonFilters.js +++ b/js/CommonFilters.js @@ -45,7 +45,7 @@ const Filters = { li.innerHTML = ` ${transport.responseText} - ${App.FormFields.hidden("rule[]", rule)}`; + ${App.FormFields.hidden_tag("rule[]", rule)}`; dojo.parser.parse(li); @@ -76,7 +76,7 @@ const Filters = { li.innerHTML = ` ${transport.responseText} - ${App.FormFields.hidden("action[]", action)}`; + ${App.FormFields.hidden_tag("action[]", action)}`; dojo.parser.parse(li); diff --git a/js/Feeds.js b/js/Feeds.js index 73f1bc338..0567cf8c5 100644 --- a/js/Feeds.js +++ b/js/Feeds.js @@ -1,6 +1,6 @@ 'use strict' -/* global __, App, Headlines, xhrPost, dojo, dijit, Form, fox, PluginHost, Notify, $$, fox */ +/* global __, App, Headlines, xhrPost, xhrJson, dojo, dijit, Form, fox, PluginHost, Notify, $$, fox */ const Feeds = { counters_last_request: 0, @@ -566,14 +566,42 @@ const Feeds = { return tree.model.store.getValue(nuf, 'bare_id'); }, search: function() { - xhrPost("backend.php", - {op: "feeds", method: "search", - param: Feeds.getActive() + ":" + Feeds.activeIsCat()}, - (transport) => { + xhrJson("backend.php", + {op: "feeds", method: "search"}, + (reply) => { try { const dialog = new fox.SingleUseDialog({ - id: "searchDlg", - content: transport.responseText, + content: ` + +
+
+ +
+ + ${reply.show_language ? + ` +
+ + ${App.FormFields.select_tag("search_language", reply.default_language, reply.all_languages, + {title: __('Used for word stemming')}, "search_language")} +
+ ` : ''} +
+ +
+ ${reply.show_syntax_help ? + `${App.FormFields.button_tag(App.FormFields.icon("help") + " " + __("Search syntax"), "", + {class: 'alt-info pull-left', onclick: "window.open('https://tt-rss.org/wiki/SearchSyntax')"})} + ` : ''} + + ${App.FormFields.submit_tag(__('Search'), {onclick: "App.dialogOf(this).execute()"})} + ${App.FormFields.cancel_dialog_tag(__('Cancel'))} +
+
+ `, title: __("Search"), execute: function () { if (this.validate()) { diff --git a/js/PrefFeedTree.js b/js/PrefFeedTree.js index e0a2dd932..e081e2e31 100644 --- a/js/PrefFeedTree.js +++ b/js/PrefFeedTree.js @@ -405,8 +405,8 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_b }, content: `
- ${App.FormFields.hidden("op", "pref-feeds")} - ${App.FormFields.hidden("method", "batchaddfeeds")} + ${App.FormFields.hidden_tag("op", "pref-feeds")} + ${App.FormFields.hidden_tag("method", "batchaddfeeds")}
${__("One valid feed per line (no detection is done)")} diff --git a/js/PrefHelpers.js b/js/PrefHelpers.js index f7eca59a0..96d524953 100644 --- a/js/PrefHelpers.js +++ b/js/PrefHelpers.js @@ -183,9 +183,9 @@ const Helpers = { ${__("You can override colors, fonts and layout of your currently selected theme with custom CSS declarations here.")}
- ${App.FormFields.hidden('op', 'rpc')} - ${App.FormFields.hidden('method', 'setpref')} - ${App.FormFields.hidden('key', 'USER_STYLESHEET')} + ${App.FormFields.hidden_tag('op', 'rpc')} + ${App.FormFields.hidden_tag('method', 'setpref')} + ${App.FormFields.hidden_tag('key', 'USER_STYLESHEET')} +
+ +
+ +
+ + + ${reply.map((profile) => ` + + + + + `).join("")} +
+ ${App.FormFields.checkbox_tag("", false, "", {onclick: 'Tables.onRowChecked(this)'})} + + ${profile.id > 0 ? + `${profile.title} + + ` : `${profile.title}`} + ${profile.active ? __("(active)") : ""} +
+
+
+ ${App.FormFields.button_tag(__('Remove selected profiles'), "", + {class: 'pull-left alt-danger', onclick: 'App.dialogOf(this).removeSelected()'})} + ${App.FormFields.submit_tag(__('Activate profile'), {onclick: 'App.dialogOf(this).execute()'})} + ${App.FormFields.cancel_dialog_tag(__('Cancel'))} +
+
+ `); }); }, execute: function () { @@ -143,7 +195,7 @@ const Helpers = { if (confirm(__("Activate selected profile?"))) { Notify.progress("Loading, please wait..."); - xhrPost("backend.php", {op: "rpc", method: "setprofile", id: sel_rows.toString()}, () => { + xhrPost("backend.php", {op: "pref-prefs", method: "activateprofile", id: sel_rows.toString()}, () => { window.location.reload(); }); } -- cgit v1.2.3 From e03d6379a68cfe79616298051881e3a71201d652 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 18 Feb 2021 11:55:00 +0300 Subject: small markup adjustment --- js/PrefHelpers.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/js/PrefHelpers.js b/js/PrefHelpers.js index 7e0993cb6..671431c39 100644 --- a/js/PrefHelpers.js +++ b/js/PrefHelpers.js @@ -152,9 +152,7 @@ const Helpers = {
-
- ${reply.map((profile) => ` @@ -178,6 +176,7 @@ const Helpers = { `).join("")}
+
${App.FormFields.button_tag(__('Remove selected profiles'), "", {class: 'pull-left alt-danger', onclick: 'App.dialogOf(this).removeSelected()'})} -- cgit v1.2.3 From ee0b66b6bd14d116b878a57f1d35b466d9994138 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 18 Feb 2021 12:13:02 +0300 Subject: af_proxy_http: markup cleanup --- plugins/af_proxy_http/init.php | 50 ++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/plugins/af_proxy_http/init.php b/plugins/af_proxy_http/init.php index d6cee5fcd..2c472a32d 100644 --- a/plugins/af_proxy_http/init.php +++ b/plugins/af_proxy_http/init.php @@ -209,39 +209,37 @@ class Af_Proxy_Http extends Plugin { function hook_prefs_tab($args) { if ($args != "prefFeeds") return; + ?> - print "
extension ".__('Image proxy settings (af_proxy_http)')."\">"; +
- print ""; + - print ""; - - print \Controls\pluginhandler_tags($this, "save"); + - $proxy_all = sql_bool_to_bool($this->host->get($this, "proxy_all")); - print \Controls\checkbox_tag("proxy_all", $proxy_all); - print " 
"; - - print "
"; + - print \Controls\submit_tag(__("Save")); +
+ +
- print ""; +
- print "
"; + + +
+ Date: Thu, 18 Feb 2021 12:27:26 +0300 Subject: shorten many invocations of Ajax.Request in inline form methods --- classes/pref/prefs.php | 141 +++++++++++++++++----------------------- plugins/af_readability/init.php | 16 ++--- plugins/af_redditimgur/init.php | 13 ++-- plugins/mail/init.php | 14 ++-- plugins/nsfw/init.php | 10 ++- 5 files changed, 80 insertions(+), 114 deletions(-) diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index 4a4aa45d6..b760affa0 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -291,18 +291,14 @@ class Pref_Prefs extends Handler_Protected { -
@@ -350,32 +346,28 @@ class Pref_Prefs extends Handler_Protected { - @@ -459,23 +451,20 @@ class Pref_Prefs extends Handler_Protected { - @@ -521,22 +510,20 @@ class Pref_Prefs extends Handler_Protected {
- @@ -806,26 +793,23 @@ class Pref_Prefs extends Handler_Protected {
- @@ -956,20 +940,15 @@ class Pref_Prefs extends Handler_Protected { ?> diff --git a/plugins/af_readability/init.php b/plugins/af_readability/init.php index 43d064fc7..a1f92815c 100755 --- a/plugins/af_readability/init.php +++ b/plugins/af_readability/init.php @@ -69,19 +69,15 @@ class Af_Readability extends Plugin { - +
"; - } + } */ } private function _get_rule_name($rule) { @@ -592,8 +670,7 @@ class Pref_Filters extends Handler_Protected { $sth->execute(array_merge($ids, [$_SESSION['uid']])); } - private function _save_rules_and_actions($filter_id) - { + private function _save_rules_and_actions($filter_id) { $sth = $this->pdo->prepare("DELETE FROM ttrss_filters2_rules WHERE filter_id = ?"); $sth->execute([$filter_id]); @@ -670,7 +747,7 @@ class Pref_Filters extends Handler_Protected { } } - function add() { + function add () { $enabled = checkbox_to_sql_bool(clean($_REQUEST["enabled"] ?? false)); $match_any_rule = checkbox_to_sql_bool(clean($_REQUEST["match_any_rule"] ?? false)); $title = clean($_REQUEST["title"]); @@ -764,7 +841,15 @@ class Pref_Filters extends Handler_Protected { $this->feed_multi_select("feed_id", $feed_ids, 'style="width : 500px; height : 300px" dojoType="dijit.form.MultiSelect"') + ]); + + /*return; + $rule = json_decode(clean($_REQUEST["rule"]), true); if ($rule) { @@ -818,7 +903,7 @@ class Pref_Filters extends Handler_Protected { print "
"; print ""; - $this->feed_multi_select("feed_id", + print $this->feed_multi_select("feed_id", $feed_id, 'style="width : 500px; height : 300px" dojoType="dijit.form.MultiSelect"'); print ""; @@ -840,7 +925,7 @@ class Pref_Filters extends Handler_Protected { print ""; - print ""; + print "";*/ } function newaction() { @@ -1071,102 +1156,106 @@ class Pref_Filters extends Handler_Protected { $attributes = "", $include_all_feeds = true, $root_id = null, $nest_level = 0) { - $pdo = Db::pdo(); + $pdo = Db::pdo(); - // print_r(in_array("CAT:6",$default_ids)); + $rv = ""; - if (!$root_id) { - print ""; + if ($include_all_feeds) { + $is_selected = (in_array("0", $default_ids)) ? "selected=\"1\"" : ""; + $rv .= ""; + } + } - if (!$root_id) $root_id = null; + if (get_pref('ENABLE_FEED_CATS')) { - $sth = $pdo->prepare("SELECT id,title, - (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE - c2.parent_cat = ttrss_feed_categories.id) AS num_children - FROM ttrss_feed_categories - WHERE owner_uid = :uid AND - (parent_cat = :root_id OR (:root_id IS NULL AND parent_cat IS NULL)) ORDER BY title"); + if (!$root_id) $root_id = null; - $sth->execute([":uid" => $_SESSION['uid'], ":root_id" => $root_id]); + $sth = $pdo->prepare("SELECT id,title, + (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE + c2.parent_cat = ttrss_feed_categories.id) AS num_children + FROM ttrss_feed_categories + WHERE owner_uid = :uid AND + (parent_cat = :root_id OR (:root_id IS NULL AND parent_cat IS NULL)) ORDER BY title"); - while ($line = $sth->fetch()) { + $sth->execute([":uid" => $_SESSION['uid'], ":root_id" => $root_id]); - for ($i = 0; $i < $nest_level; $i++) - $line["title"] = " " . $line["title"]; + while ($line = $sth->fetch()) { - $is_selected = in_array("CAT:".$line["id"], $default_ids) ? "selected=\"1\"" : ""; + for ($i = 0; $i < $nest_level; $i++) + $line["title"] = " " . $line["title"]; - printf("", - $line["id"], htmlspecialchars($line["title"])); + $is_selected = in_array("CAT:".$line["id"], $default_ids) ? "selected=\"1\"" : ""; - if ($line["num_children"] > 0) - $this->feed_multi_select($id, $default_ids, $attributes, - $include_all_feeds, $line["id"], $nest_level+1); + $rv .= sprintf("", + $line["id"], htmlspecialchars($line["title"])); - $f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds - WHERE cat_id = ? AND owner_uid = ? ORDER BY title"); + if ($line["num_children"] > 0) + $rv .= $this->feed_multi_select($id, $default_ids, $attributes, + $include_all_feeds, $line["id"], $nest_level+1); - $f_sth->execute([$line['id'], $_SESSION['uid']]); + $f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds + WHERE cat_id = ? AND owner_uid = ? ORDER BY title"); - while ($fline = $f_sth->fetch()) { - $is_selected = (in_array($fline["id"], $default_ids)) ? "selected=\"1\"" : ""; + $f_sth->execute([$line['id'], $_SESSION['uid']]); - $fline["title"] = " " . $fline["title"]; + while ($fline = $f_sth->fetch()) { + $is_selected = (in_array($fline["id"], $default_ids)) ? "selected=\"1\"" : ""; - for ($i = 0; $i < $nest_level; $i++) $fline["title"] = " " . $fline["title"]; - printf("", - $fline["id"], htmlspecialchars($fline["title"])); - } - } + for ($i = 0; $i < $nest_level; $i++) + $fline["title"] = " " . $fline["title"]; - if (!$root_id) { - $is_selected = in_array("CAT:0", $default_ids) ? "selected=\"1\"" : ""; + $rv .= sprintf("", + $fline["id"], htmlspecialchars($fline["title"])); + } + } - printf("", - __("Uncategorized")); + if (!$root_id) { + $is_selected = in_array("CAT:0", $default_ids) ? "selected=\"1\"" : ""; - $f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds - WHERE cat_id IS NULL AND owner_uid = ? ORDER BY title"); - $f_sth->execute([$_SESSION['uid']]); + $rv .= sprintf("", + __("Uncategorized")); - while ($fline = $f_sth->fetch()) { - $is_selected = in_array($fline["id"], $default_ids) ? "selected=\"1\"" : ""; + $f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds + WHERE cat_id IS NULL AND owner_uid = ? ORDER BY title"); + $f_sth->execute([$_SESSION['uid']]); - $fline["title"] = " " . $fline["title"]; + while ($fline = $f_sth->fetch()) { + $is_selected = in_array($fline["id"], $default_ids) ? "selected=\"1\"" : ""; - for ($i = 0; $i < $nest_level; $i++) $fline["title"] = " " . $fline["title"]; - printf("", - $fline["id"], htmlspecialchars($fline["title"])); + for ($i = 0; $i < $nest_level; $i++) + $fline["title"] = " " . $fline["title"]; + + $rv .= sprintf("", + $fline["id"], htmlspecialchars($fline["title"])); + } } - } - } else { - $sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds - WHERE owner_uid = ? ORDER BY title"); - $sth->execute([$_SESSION['uid']]); + } else { + $sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds + WHERE owner_uid = ? ORDER BY title"); + $sth->execute([$_SESSION['uid']]); - while ($line = $sth->fetch()) { + while ($line = $sth->fetch()) { - $is_selected = (in_array($line["id"], $default_ids)) ? "selected=\"1\"" : ""; + $is_selected = (in_array($line["id"], $default_ids)) ? "selected=\"1\"" : ""; - printf("", - $line["id"], htmlspecialchars($line["title"])); + $rv .= sprintf("", + $line["id"], htmlspecialchars($line["title"])); + } } - } - if (!$root_id) { - print ""; + if (!$root_id) { + $rv .= ""; + } + + return $rv; } } -} diff --git a/classes/rpc.php b/classes/rpc.php index 643ad29d8..20a11b994 100755 --- a/classes/rpc.php +++ b/classes/rpc.php @@ -399,7 +399,7 @@ class RPC extends Handler_Protected { $params["icon_indicator_white"] = $this->image_to_base64("images/indicator_white.gif"); - $params["labels"] = Labels::get_all_labels($_SESSION["uid"]); + $params["labels"] = Labels::get_all($_SESSION["uid"]); return $params; } @@ -430,7 +430,7 @@ class RPC extends Handler_Protected { $data["max_feed_id"] = (int) $max_feed_id; $data["num_feeds"] = (int) $num_feeds; $data['cdm_expanded'] = get_pref('CDM_EXPANDED'); - $data["labels"] = Labels::get_all_labels($_SESSION["uid"]); + $data["labels"] = Labels::get_all($_SESSION["uid"]); if (LOG_DESTINATION == 'sql' && $_SESSION['access_level'] >= 10) { if (DB_TYPE == 'pgsql') { diff --git a/include/controls.php b/include/controls.php index ae5fba739..e3349d31b 100755 --- a/include/controls.php +++ b/include/controls.php @@ -60,21 +60,11 @@ return $rv; } - function select_labels(string $name, string $value, array $attributes = [], string $id = "") { - $pdo = \Db::pdo(); - - $sth = $pdo->prepare("SELECT caption FROM ttrss_labels2 - WHERE owner_uid = ? ORDER BY caption"); - $sth->execute([$_SESSION['uid']]); - - $values = []; - - while ($row = $sth->fetch()) { - array_push($values, $row["caption"]); - } + /*function select_labels(string $name, string $value, array $attributes = [], string $id = "") { + $values = \Labels::get_as_hash($_SESSION["uid"]); return select_tag($name, $value, $values, $attributes, $id); - } + }*/ function select_hash(string $name, $value, array $values, array $attributes = [], string $id = "") { $attributes_str = attributes_to_string($attributes); diff --git a/js/CommonFilters.js b/js/CommonFilters.js index bd6808f59..75e1fa8af 100644 --- a/js/CommonFilters.js +++ b/js/CommonFilters.js @@ -7,26 +7,17 @@ /* exported Filters */ const Filters = { - edit: function(id) { // if no id, new filter dialog - let query; - - if (!App.isPrefs()) { - query = { - op: "pref-filters", method: "edit", - feed: Feeds.getActive(), is_cat: Feeds.activeIsCat() - }; - } else { - query = {op: "pref-filters", method: "edit", id: id}; - } + edit: function(filter_id = null) { // if no id, new filter dialog const dialog = new fox.SingleUseDialog({ id: "filterEditDlg", - title: id ? __("Edit Filter") : __("Create Filter"), + title: filter_id ? __("Edit Filter") : __("Create Filter"), ACTION_TAG: 4, ACTION_SCORE: 6, ACTION_LABEL: 7, ACTION_PLUGIN: 9, PARAM_ACTIONS: [4, 6, 7, 9], + filter_info: {}, test: function() { const test_dialog = new fox.SingleUseDialog({ title: "Test Filter", @@ -116,16 +107,17 @@ const Filters = { test_dialog.show(); }, - createNewRuleElement: function(parentNode, replaceNode) { + insertRule: function(parentNode, replaceNode) { const rule = dojo.formToJson("filter_new_rule_form"); xhr.post("backend.php", {op: "pref-filters", method: "printrulename", rule: rule}, (reply) => { try { const li = document.createElement('li'); + li.addClassName("rule"); - li.innerHTML = ` - ${reply} - ${App.FormFields.hidden_tag("rule[]", rule)}`; + li.innerHTML = `${App.FormFields.checkbox_tag("", false, {onclick: 'Lists.onRowChecked(this)'})} + ${reply} + ${App.FormFields.hidden_tag("rule[]", rule)}`; dojo.parser.parse(li); @@ -139,7 +131,7 @@ const Filters = { } }); }, - createNewActionElement: function(parentNode, replaceNode) { + insertAction: function(parentNode, replaceNode) { const form = document.forms["filter_new_action_form"]; if (form.action_id.value == 7) { @@ -153,10 +145,11 @@ const Filters = { xhr.post("backend.php", { op: "pref-filters", method: "printactionname", action: action }, (reply) => { try { const li = document.createElement('li'); + li.addClassName("action"); - li.innerHTML = ` - ${reply} - ${App.FormFields.hidden_tag("action[]", action)}`; + li.innerHTML = `${App.FormFields.checkbox_tag("", false, {onclick: 'Lists.onRowChecked(this)'})} + ${reply} + ${App.FormFields.hidden_tag("action[]", action)}`; dojo.parser.parse(li); @@ -171,30 +164,84 @@ const Filters = { } }); }, - editRule: function(replaceNode, ruleStr) { + editRule: function(replaceNode, ruleStr = null) { const edit_rule_dialog = new fox.SingleUseDialog({ id: "filterNewRuleDlg", title: ruleStr ? __("Edit rule") : __("Add rule"), execute: function () { if (this.validate()) { - dialog.createNewRuleElement(App.byId("filterDlg_Matches"), replaceNode); + dialog.insertRule(App.byId("filterDlg_Matches"), replaceNode); this.hide(); } }, content: __('Loading, please wait...'), }); - const tmph = dojo.connect(edit_rule_dialog, "onShow", null, function (/* e */) { + const tmph = dojo.connect(edit_rule_dialog, "onShow", null, function () { dojo.disconnect(tmph); - xhr.post("backend.php", {op: 'pref-filters', method: 'newrule', rule: ruleStr}, (reply) => { - edit_rule_dialog.attr('content', reply); + let rule; + + if (ruleStr) { + rule = JSON.parse(ruleStr); + } else { + rule = { + reg_exp: "", + filter_type: 1, + feed_id: ["0"], + inverse: false, + }; + } + + console.log(rule, dialog.filter_info); + + xhr.json("backend.php", {op: "pref-filters", method: "editrule", ids: rule.feed_id.join(",")}, function (editrule) { + edit_rule_dialog.attr('content', + ` +
+ +
+ + +
+ +
+ +
+
+ + ${App.FormFields.select_hash("filter_type", rule.filter_type, dialog.filter_info.filter_types)} + +
+
+ + ${editrule.multiselect} + +
+
+ +
+ ${App.FormFields.button_tag(App.FormFields.icon("help") + " " + __("More info"), "", {class: 'pull-left alt-info', + onclick: "window.open('https://tt-rss.org/wiki/ContentFilters')"})} + ${App.FormFields.submit_tag(__("Save rule"), {onclick: "App.dialogOf(this).execute()"})} + ${App.FormFields.cancel_dialog_tag(__("Cancel"))} +
+ +
+ `); }); + }); edit_rule_dialog.show(); }, - editAction: function(replaceNode, actionStr) { + /*editAction: function(replaceNode, actionStr) { const edit_action_dialog = new fox.SingleUseDialog({ title: actionStr ? __("Edit action") : __("Add action"), hideOrShowActionParam: function(sender) { @@ -221,7 +268,7 @@ const Filters = { } }); - const tmph = dojo.connect(edit_action_dialog, "onShow", null, function (/* e */) { + const tmph = dojo.connect(edit_action_dialog, "onShow", null, function () { dojo.disconnect(tmph); xhr.post("backend.php", {op: 'pref-filters', method: 'newaction', action: actionStr}, (reply) => { @@ -234,22 +281,65 @@ const Filters = { }); edit_action_dialog.show(); - }, + }, */ + /*editAction: function(replaceNode, actionStr) { + const edit_action_dialog = new fox.SingleUseDialog({ + title: actionStr ? __("Edit action") : __("Add action"), + hideOrShowActionParam: function(sender) { + const action = parseInt(sender.value); + + dijit.byId("filterDlg_actionParam").domNode.hide(); + dijit.byId("filterDlg_actionParamLabel").domNode.hide(); + dijit.byId("filterDlg_actionParamPlugin").domNode.hide(); + + // if selected action supports parameters, enable params field + if (action == dialog.ACTION_LABEL) { + dijit.byId("filterDlg_actionParamLabel").domNode.show(); + } else if (action == dialog.ACTION_PLUGIN) { + dijit.byId("filterDlg_actionParamPlugin").domNode.show(); + } else if (dialog.PARAM_ACTIONS.indexOf(action) != -1) { + dijit.byId("filterDlg_actionParam").domNode.show(); + } + }, + execute: function () { + if (this.validate()) { + dialog.createNewActionElement(App.byId("filterDlg_Actions"), replaceNode); + this.hide(); + } + } + }); + + const tmph = dojo.connect(edit_action_dialog, "onShow", null, function () { + dojo.disconnect(tmph); + + xhr.post("backend.php", {op: 'pref-filters', method: 'newaction', action: actionStr}, (reply) => { + edit_action_dialog.attr('content', reply); + + setTimeout(() => { + edit_action_dialog.hideOrShowActionParam(dijit.byId("filterDlg_actionSelect").attr('value')); + }, 250); + }); + }); + + edit_action_dialog.show(); + },*/ selectRules: function (select) { Lists.select("filterDlg_Matches", select); }, selectActions: function (select) { Lists.select("filterDlg_Actions", select); }, - onRuleClicked: function (e) { - const li = e.closest('li'); - const rule = li.querySelector('input[name="rule[]"]').value + onRuleClicked: function (elem) { + + const li = elem.closest('li'); + const rule = li.querySelector('input[name="rule[]"]').value; this.editRule(li, rule); }, - onActionClicked: function (e) { - const li = e.closest('li'); - const action = li.querySelector('input[name="action[]"]').value + onActionClicked: function (elem) { + + const li = elem.closest('li'); + const action = li.querySelector('input[name="action[]"]').value; this.editAction(li, action); }, @@ -302,13 +392,141 @@ const Filters = { content: __("Loading, please wait...") }); - - const tmph = dojo.connect(dialog, 'onShow', function () { dojo.disconnect(tmph); - xhr.post("backend.php", query, function (reply) { - dialog.attr('content', reply); + const query = {op: "pref-filters", method: "edit", id: filter_id}; + + /*if (!App.isPrefs()) { + query = { + op: "pref-filters", method: "edit", + feed: Feeds.getActive(), is_cat: Feeds.activeIsCat() + }; + } else { + query = {op: "pref-filters", method: "edit", id: id}; + }*/ + + xhr.json("backend.php", query, function (filter) { + + dialog.filter_info = filter; + + const options = { + enabled: [ filter.enabled, __('Enabled') ], + match_any_rule: [ filter.match_any_rule, __('Match any rule') ], + inverse: [ filter.inverse, __('Inverse matching') ], + }; + + dialog.attr('content', + ` +
+ + ${App.FormFields.hidden_tag("op", "pref-filters")} + ${App.FormFields.hidden_tag("id", filter_id)} + ${App.FormFields.hidden_tag("method", filter_id ? "editSave" : "add")} + ${App.FormFields.hidden_tag("csrf_token", App.getInitParam('csrf_token'))} + +
+ +
+ +
+
+
+
+
+ ${__("Select")} +
+ +
${__("All")}
+
${__("None")}
+
+
+ + +
+
+
    + ${filter.rules.map((rule) => ` +
  • + ${App.FormFields.checkbox_tag("", false, "", {onclick: 'Lists.onRowChecked(this)'})} + ${rule.name} + ${App.FormFields.hidden_tag("rule[]", JSON.stringify(rule))} +
  • + `).join("")} +
+
+
+
+
+
+
+
+ ${__("Select")} +
+
${__("All")}
+
${__("None")}
+
+
+ + +
+
+
    + ${filter.actions.map((action) => ` +
  • + ${App.FormFields.checkbox_tag("", false, "", {onclick: 'Lists.onRowChecked(this)'})} + ${App.escapeHtml(action.name)} + ${App.FormFields.hidden_tag("action[]", JSON.stringify(action))} +
  • + `).join("")} +
+
+
+
+
+ +
+ +
+ ${Object.keys(options).map((name) => + ` +
+ +
+ `).join("")} +
+ +
+ ${filter_id ? + ` + ${App.FormFields.button_tag(__("Remove"), "", {class: "pull-left alt-danger", onclick: "App.dialogOf(this).removeFilter()"})} + ${App.FormFields.button_tag(__("Test"), "", {class: "alt-info", onclick: "App.dialogOf(this).test()"})} + ${App.FormFields.submit_tag(__("Save"), {onclick: "App.dialogOf(this).execute()"})} + ${App.FormFields.cancel_dialog_tag(__("Cancel"))} + ` : ` + ${App.FormFields.button_tag(__("Test"), "", {class: "alt-info", onclick: "App.dialogOf(this).test()"})} + ${App.FormFields.submit_tag(__("Create"), {onclick: "App.dialogOf(this).execute()"})} + ${App.FormFields.cancel_dialog_tag(__("Cancel"))} + `} +
+
+ `); if (!App.isPrefs()) { const selectedText = App.getSelectedText(); 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/themes/compact.css b/themes/compact.css index 92e9928c8..16bdcf1f0 100644 --- a/themes/compact.css +++ b/themes/compact.css @@ -822,16 +822,18 @@ body.ttrss_main #headlines-spacer a:hover { } body.ttrss_main ul#filterDlg_Matches, body.ttrss_main ul#filterDlg_Actions { - max-height: 100px; - overflow: auto; list-style-type: none; - border-style: solid; - border-color: #ddd; - border-width: 1px 1px 1px 1px; - background-color: white; - margin: 0px 0px 5px 0px; - padding: 4px; - min-height: 16px; + margin: 0; + padding: 0; + /*max-height : 100px; + overflow : auto; + border-style : solid; + border-color : @border-default; + border-width : 1px 1px 1px 1px; + background-color : @default-bg; + margin : 0px 0px 5px 0px; + padding : 4px; + min-height : 16px;*/ } body.ttrss_main ul#filterDlg_Matches li, body.ttrss_main ul#filterDlg_Actions li { diff --git a/themes/compact_night.css b/themes/compact_night.css index 29eff9cb9..37adf3fda 100644 --- a/themes/compact_night.css +++ b/themes/compact_night.css @@ -822,16 +822,18 @@ body.ttrss_main #headlines-spacer a:hover { } body.ttrss_main ul#filterDlg_Matches, body.ttrss_main ul#filterDlg_Actions { - max-height: 100px; - overflow: auto; list-style-type: none; - border-style: solid; - border-color: #222; - border-width: 1px 1px 1px 1px; - background-color: #333; - margin: 0px 0px 5px 0px; - padding: 4px; - min-height: 16px; + margin: 0; + padding: 0; + /*max-height : 100px; + overflow : auto; + border-style : solid; + border-color : @border-default; + border-width : 1px 1px 1px 1px; + background-color : @default-bg; + margin : 0px 0px 5px 0px; + padding : 4px; + min-height : 16px;*/ } body.ttrss_main ul#filterDlg_Matches li, body.ttrss_main ul#filterDlg_Actions li { diff --git a/themes/light.css b/themes/light.css index 583f9cabb..0f2ffc1b6 100644 --- a/themes/light.css +++ b/themes/light.css @@ -822,16 +822,18 @@ body.ttrss_main #headlines-spacer a:hover { } body.ttrss_main ul#filterDlg_Matches, body.ttrss_main ul#filterDlg_Actions { - max-height: 100px; - overflow: auto; list-style-type: none; - border-style: solid; - border-color: #ddd; - border-width: 1px 1px 1px 1px; - background-color: white; - margin: 0px 0px 5px 0px; - padding: 4px; - min-height: 16px; + margin: 0; + padding: 0; + /*max-height : 100px; + overflow : auto; + border-style : solid; + border-color : @border-default; + border-width : 1px 1px 1px 1px; + background-color : @default-bg; + margin : 0px 0px 5px 0px; + padding : 4px; + min-height : 16px;*/ } body.ttrss_main ul#filterDlg_Matches li, body.ttrss_main ul#filterDlg_Actions li { diff --git a/themes/light/tt-rss.less b/themes/light/tt-rss.less index b1895f318..2794d8177 100644 --- a/themes/light/tt-rss.less +++ b/themes/light/tt-rss.less @@ -955,16 +955,18 @@ body.ttrss_main { } ul#filterDlg_Matches, ul#filterDlg_Actions { - max-height : 100px; - overflow : auto; list-style-type : none; + margin : 0; + padding: 0; + /*max-height : 100px; + overflow : auto; border-style : solid; border-color : @border-default; border-width : 1px 1px 1px 1px; background-color : @default-bg; margin : 0px 0px 5px 0px; padding : 4px; - min-height : 16px; + min-height : 16px;*/ } ul#filterDlg_Matches li, ul#filterDlg_Actions li { diff --git a/themes/night.css b/themes/night.css index e428d8aa7..e012c92b2 100644 --- a/themes/night.css +++ b/themes/night.css @@ -823,16 +823,18 @@ body.ttrss_main #headlines-spacer a:hover { } body.ttrss_main ul#filterDlg_Matches, body.ttrss_main ul#filterDlg_Actions { - max-height: 100px; - overflow: auto; list-style-type: none; - border-style: solid; - border-color: #222; - border-width: 1px 1px 1px 1px; - background-color: #333; - margin: 0px 0px 5px 0px; - padding: 4px; - min-height: 16px; + margin: 0; + padding: 0; + /*max-height : 100px; + overflow : auto; + border-style : solid; + border-color : @border-default; + border-width : 1px 1px 1px 1px; + background-color : @default-bg; + margin : 0px 0px 5px 0px; + padding : 4px; + min-height : 16px;*/ } body.ttrss_main ul#filterDlg_Matches li, body.ttrss_main ul#filterDlg_Actions li { diff --git a/themes/night_blue.css b/themes/night_blue.css index c9ccaf737..b49a496e3 100644 --- a/themes/night_blue.css +++ b/themes/night_blue.css @@ -823,16 +823,18 @@ body.ttrss_main #headlines-spacer a:hover { } body.ttrss_main ul#filterDlg_Matches, body.ttrss_main ul#filterDlg_Actions { - max-height: 100px; - overflow: auto; list-style-type: none; - border-style: solid; - border-color: #222; - border-width: 1px 1px 1px 1px; - background-color: #333; - margin: 0px 0px 5px 0px; - padding: 4px; - min-height: 16px; + margin: 0; + padding: 0; + /*max-height : 100px; + overflow : auto; + border-style : solid; + border-color : @border-default; + border-width : 1px 1px 1px 1px; + background-color : @default-bg; + margin : 0px 0px 5px 0px; + padding : 4px; + min-height : 16px;*/ } body.ttrss_main ul#filterDlg_Matches li, body.ttrss_main ul#filterDlg_Actions li { -- cgit v1.2.3 From 3b8d69206ccc24b41b45acd55f0c63681e749fd1 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 21 Feb 2021 10:28:59 +0300 Subject: deal with filter actions UI --- classes/pref/filters.php | 354 +++-------------------------------------------- js/CommonFilters.js | 118 ++++++++-------- 2 files changed, 74 insertions(+), 398 deletions(-) diff --git a/classes/pref/filters.php b/classes/pref/filters.php index 4e52260c9..c00e52bde 100755 --- a/classes/pref/filters.php +++ b/classes/pref/filters.php @@ -328,7 +328,8 @@ class Pref_Filters extends Handler_Protected { "rules" => [], "actions" => [], "filter_types" => [], - "filter_actions" => [], + "action_types" => [], + "plugin_actions" => [], "labels" => Labels::get_all($_SESSION["uid"]) ]; @@ -343,7 +344,17 @@ class Pref_Filters extends Handler_Protected { ORDER BY name"); while ($line = $res->fetch()) { - $rv["filter_actions"][$line["id"]] = __($line["description"]); + $rv["action_types"][$line["id"]] = __($line["description"]); + } + + $filter_actions = PluginHost::getInstance()->get_filter_actions(); + + foreach ($filter_actions as $fclass => $factions) { + foreach ($factions as $faction) { + + $rv["plugin_actions"][$fclass . ":" . $faction["action"]] = + $fclass . ": " . $faction["description"]; + } } if ($filter_id) { @@ -393,167 +404,6 @@ class Pref_Filters extends Handler_Protected { } print json_encode($rv); } - - /*return; - - if (empty($filter_id) || $row = $sth->fetch()) { - - $enabled = $row["enabled"] ?? true; - $match_any_rule = $row["match_any_rule"] ?? false; - $inverse = $row["inverse"] ?? false; - $title = htmlspecialchars($row["title"] ?? ""); - - print "
"; - - print \Controls\hidden_tag("op", "pref-filters"); - - if ($filter_id) { - print \Controls\hidden_tag("id", "$filter_id"); - print \Controls\hidden_tag("method", "editSave"); - } else { - print \Controls\hidden_tag("method", "add"); - } - - print \Controls\hidden_tag("csrf_token", $_SESSION['csrf_token']); - - print "
".__("Caption")."
-
- -
-
".__("Match")."
-
-
-
- " . __('Select')." -
- -
".__('All')."
-
".__('None')."
-
-
- - -
"; - - print "
    "; - - if ($filter_id) { - $rules_sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2_rules - WHERE filter_id = ? ORDER BY reg_exp, id"); - $rules_sth->execute([$filter_id]); - - while ($line = $rules_sth->fetch()) { - if ($line["match_on"]) { - $line["feed_id"] = json_decode($line["match_on"], true); - } else { - if ($line["cat_filter"]) { - $feed_id = "CAT:" . (int)$line["cat_id"]; - } else { - $feed_id = (int)$line["feed_id"]; - } - - $line["feed_id"] = ["" . $feed_id]; // set item type to string for in_array() - } - - unset($line["cat_filter"]); - unset($line["cat_id"]); - unset($line["filter_id"]); - unset($line["id"]); - if (!$line["inverse"]) unset($line["inverse"]); - unset($line["match_on"]); - - print "
  • - ".$this->_get_rule_name($line)."". - \Controls\hidden_tag("rule[]", (string)json_encode($line))."
  • "; - } - } - - print "
-
"; - - print "
".__("Apply actions")."
-
-
-
- ".__('Select')." -
-
".__('All')."
-
".__('None')."
-
-
- - -
"; - - print "
    "; - - if ($filter_id) { - $actions_sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2_actions - WHERE filter_id = ? ORDER BY id"); - $actions_sth->execute([$filter_id]); - - while ($line = $actions_sth->fetch()) { - $line["action_param_label"] = $line["action_param"]; - - unset($line["filter_id"]); - unset($line["id"]); - - print "
  • - ".$this->_get_action_name($line)."". - \Controls\hidden_tag("action[]", (string)json_encode($line))."
  • "; - } - } - - print "
"; - - print "
"; - - print "
".__("Options")."
-
"; - - print "
-
"; - - print "
- -
"; - - print "
-
"; - - print "
-
"; - - if ($filter_id) { - print "
- -
- - - "; - } else { - print " - - "; - } - - print "
"; - } */ } private function _get_rule_name($rule) { @@ -845,180 +695,8 @@ class Pref_Filters extends Handler_Protected { $feed_ids = explode(",", clean($_REQUEST["ids"])); print json_encode([ - "multiselect" => $this->feed_multi_select("feed_id", $feed_ids, 'style="width : 500px; height : 300px" dojoType="dijit.form.MultiSelect"') + "multiselect" => $this->_feed_multi_select("feed_id", $feed_ids, 'style="width : 540px; height : 300px" dojoType="dijit.form.MultiSelect"') ]); - - /*return; - - $rule = json_decode(clean($_REQUEST["rule"]), true); - - if ($rule) { - $reg_exp = htmlspecialchars($rule["reg_exp"]); - $filter_type = $rule["filter_type"]; - $feed_id = $rule["feed_id"]; - $inverse_checked = !empty($rule["inverse"]); - } else { - $reg_exp = ""; - $filter_type = 1; - $feed_id = ["0"]; - $inverse_checked = false; - } - - print "
"; - - $res = $this->pdo->query("SELECT id,description - FROM ttrss_filter_types WHERE id != 5 ORDER BY description"); - - $filter_types = array(); - - while ($line = $res->fetch()) { - $filter_types[$line["id"]] = __($line["description"]); - } - - print "
".__("Match")."
"; - - print "
"; - - print ""; - - print "
"; - - print "
"; - print ""; - print "
"; - - print "
"; - print " "; - print \Controls\select_hash("filter_type", $filter_type, $filter_types); - print " "; - - print "
"; - - print "
"; - print ""; - print $this->feed_multi_select("feed_id", - $feed_id, - 'style="width : 500px; height : 300px" dojoType="dijit.form.MultiSelect"'); - print ""; - - print "
"; - - print "
"; - - print "
"; - - print ""; - - print " "; - - print ""; - - print "
"; - - print "
";*/ - } - - function newaction() { - $action = json_decode(clean($_REQUEST["action"]), true); - - if ($action) { - $action_param = $action["action_param"]; - $action_id = (int)$action["action_id"]; - } else { - $action_param = ""; - $action_id = 0; - } - - print "
"; - - print "
".__("Perform Action")."
"; - - print "
"; - - print ""; - - #$param_box_hidden = ($action_id == 7 || $action_id == 4 || $action_id == 6 || $action_id == 9) ? - # "" : "display : none"; - - #$param_hidden = ($action_id == 4 || $action_id == 6) ? - # "" : "display : none"; - - #$label_param_hidden = ($action_id == 7) ? "" : "display : none"; - #$plugin_param_hidden = ($action_id == 9) ? "" : "display : none"; - - #print ""; - #print " "; - //print " " . __("with parameters:") . " "; - print ""; - - print \Controls\select_labels("action_param_label", $action_param, - ["style" => $label_param_hidden], - "filterDlg_actionParamLabel"); - - $filter_actions = PluginHost::getInstance()->get_filter_actions(); - $filter_action_hash = array(); - - foreach ($filter_actions as $fclass => $factions) { - foreach ($factions as $faction) { - - $filter_action_hash[$fclass . ":" . $faction["action"]] = - $fclass . ": " . $faction["description"]; - } - } - - if (count($filter_action_hash) == 0) { - $filter_plugin_disabled = ["disabled" => "1"]; - - $filter_action_hash["no-data"] = __("No actions available"); - - } else { - $filter_plugin_disabled = []; - } - - print \Controls\select_hash("action_param_plugin", $action_param, $filter_action_hash, - array_merge(["style" => $plugin_param_hidden], $filter_plugin_disabled), - "filterDlg_actionParamPlugin"); - - #print ""; - - print " "; // tiny layout hack - - print "
"; - - print "
"; - - print " "; - - print ""; - - print "
"; - - print "
"; } private function _get_name($id) { @@ -1152,7 +830,7 @@ class Pref_Filters extends Handler_Protected { $this->pdo->commit(); } - private function feed_multi_select($id, $default_ids = [], + private function _feed_multi_select($id, $default_ids = [], $attributes = "", $include_all_feeds = true, $root_id = null, $nest_level = 0) { @@ -1194,7 +872,7 @@ class Pref_Filters extends Handler_Protected { $line["id"], htmlspecialchars($line["title"])); if ($line["num_children"] > 0) - $rv .= $this->feed_multi_select($id, $default_ids, $attributes, + $rv .= $this->_feed_multi_select($id, $default_ids, $attributes, $include_all_feeds, $line["id"], $nest_level+1); $f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds diff --git a/js/CommonFilters.js b/js/CommonFilters.js index 75e1fa8af..5874170b8 100644 --- a/js/CommonFilters.js +++ b/js/CommonFilters.js @@ -209,13 +209,13 @@ const Filters = {
-
- + ${App.FormFields.select_hash("filter_type", rule.filter_type, dialog.filter_info.filter_types)}
@@ -241,10 +241,14 @@ const Filters = { edit_rule_dialog.show(); }, - /*editAction: function(replaceNode, actionStr) { + editAction: function(replaceNode, actionStr) { const edit_action_dialog = new fox.SingleUseDialog({ title: actionStr ? __("Edit action") : __("Add action"), - hideOrShowActionParam: function(sender) { + select_labels: function(name, value, labels, attributes = {}, id = "") { + const values = Object.values(labels).map((label) => label.caption); + return App.FormFields.select_tag(name, value, values, attributes, id); + }, + toggleParam: function(sender) { const action = parseInt(sender.value); dijit.byId("filterDlg_actionParam").domNode.hide(); @@ -262,67 +266,72 @@ const Filters = { }, execute: function () { if (this.validate()) { - dialog.createNewActionElement(App.byId("filterDlg_Actions"), replaceNode); + dialog.insertAction(App.byId("filterDlg_Actions"), replaceNode); this.hide(); } - } + }, + content: __("Loading, please wait...") }); const tmph = dojo.connect(edit_action_dialog, "onShow", null, function () { dojo.disconnect(tmph); - xhr.post("backend.php", {op: 'pref-filters', method: 'newaction', action: actionStr}, (reply) => { - edit_action_dialog.attr('content', reply); + let action; - setTimeout(() => { - edit_action_dialog.hideOrShowActionParam(dijit.byId("filterDlg_actionSelect").attr('value')); - }, 250); - }); - }); - - edit_action_dialog.show(); - }, */ - /*editAction: function(replaceNode, actionStr) { - const edit_action_dialog = new fox.SingleUseDialog({ - title: actionStr ? __("Edit action") : __("Add action"), - hideOrShowActionParam: function(sender) { - const action = parseInt(sender.value); + if (actionStr) { + action = JSON.parse(actionStr); + } else { + action = { + action_id: 2, + action_param: "" + }; + } - dijit.byId("filterDlg_actionParam").domNode.hide(); - dijit.byId("filterDlg_actionParamLabel").domNode.hide(); - dijit.byId("filterDlg_actionParamPlugin").domNode.hide(); + console.log(action); - // if selected action supports parameters, enable params field - if (action == dialog.ACTION_LABEL) { - dijit.byId("filterDlg_actionParamLabel").domNode.show(); - } else if (action == dialog.ACTION_PLUGIN) { - dijit.byId("filterDlg_actionParamPlugin").domNode.show(); - } else if (dialog.PARAM_ACTIONS.indexOf(action) != -1) { - dijit.byId("filterDlg_actionParam").domNode.show(); - } - }, - execute: function () { - if (this.validate()) { - dialog.createNewActionElement(App.byId("filterDlg_Actions"), replaceNode); - this.hide(); - } - } - }); + edit_action_dialog.attr('content', + ` +
+
+ ${App.FormFields.select_hash("action_id", -1, + dialog.filter_info.action_types, + {onchange: "App.dialogOf(this).toggleParam(this)"}, + "filterDlg_actionSelect")} + + + + ${edit_action_dialog.select_labels("action_param_label", action.action_param, + dialog.filter_info.labels, + {}, + "filterDlg_actionParamLabel")} + + ${App.FormFields.select_hash("action_param_plugin", action.action_param, + dialog.filter_info.plugin_actions, + {}, + "filterDlg_actionParamPlugin")} +
+
+ ${App.FormFields.submit_tag(__("Save action"), {onclick: "App.dialogOf(this).execute()"})} + ${App.FormFields.cancel_dialog_tag(__("Cancel"))} +
+
+ `); - const tmph = dojo.connect(edit_action_dialog, "onShow", null, function () { - dojo.disconnect(tmph); + dijit.byId("filterDlg_actionSelect").attr('value', action.action_id); - xhr.post("backend.php", {op: 'pref-filters', method: 'newaction', action: actionStr}, (reply) => { + /*xhr.post("backend.php", {op: 'pref-filters', method: 'newaction', action: actionStr}, (reply) => { edit_action_dialog.attr('content', reply); setTimeout(() => { edit_action_dialog.hideOrShowActionParam(dijit.byId("filterDlg_actionSelect").attr('value')); }, 250); - }); + });*/ }); edit_action_dialog.show(); - },*/ + }, selectRules: function (select) { Lists.select("filterDlg_Matches", select); }, @@ -395,18 +404,7 @@ const Filters = { const tmph = dojo.connect(dialog, 'onShow', function () { dojo.disconnect(tmph); - const query = {op: "pref-filters", method: "edit", id: filter_id}; - - /*if (!App.isPrefs()) { - query = { - op: "pref-filters", method: "edit", - feed: Feeds.getActive(), is_cat: Feeds.activeIsCat() - }; - } else { - query = {op: "pref-filters", method: "edit", id: id}; - }*/ - - xhr.json("backend.php", query, function (filter) { + xhr.json("backend.php", {op: "pref-filters", method: "edit", id: filter_id}, function (filter) { dialog.filter_info = filter; @@ -476,10 +474,10 @@ const Filters = { dojoType="dijit.MenuItem">${__("None")} - - @@ -488,7 +486,7 @@ const Filters = { ${filter.actions.map((action) => `
  • ${App.FormFields.checkbox_tag("", false, "", {onclick: 'Lists.onRowChecked(this)'})} - ${App.escapeHtml(action.name)} + ${App.escapeHtml(action.name)} ${App.FormFields.hidden_tag("action[]", JSON.stringify(action))}
  • `).join("")} -- cgit v1.2.3 From 9e56896bd428114ff9bfd979c2d4ff8d93f99485 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 21 Feb 2021 10:34:28 +0300 Subject: Element visible: check for offsetHeight/offsetWidth --- js/common.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/js/common.js b/js/common.js index 6e8168357..e85862990 100755 --- a/js/common.js +++ b/js/common.js @@ -86,8 +86,7 @@ Element.prototype.fadeIn = function(display = undefined){ }; Element.prototype.visible = function() { - // TODO: should we actually check for offsetWidth/offsetHeight == 0? - return this.style.display != "none"; + return this.style.display != "none" && this.offsetHeight != 0 && this.offsetWidth != 0; } Element.visible = function(elem) { -- cgit v1.2.3 From 810afdaf5a429468d31b09ae5a7f995dd11568f8 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 21 Feb 2021 12:28:25 +0300 Subject: prevent creation of filter rules matching no feeds --- classes/pref/filters.php | 2 +- js/form/ValidationMultiSelect.js | 20 ++++++++++++++++++++ js/prefs.js | 1 + js/tt-rss.js | 1 + 4 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 js/form/ValidationMultiSelect.js diff --git a/classes/pref/filters.php b/classes/pref/filters.php index c00e52bde..9388cc8d7 100755 --- a/classes/pref/filters.php +++ b/classes/pref/filters.php @@ -695,7 +695,7 @@ class Pref_Filters extends Handler_Protected { $feed_ids = explode(",", clean($_REQUEST["ids"])); print json_encode([ - "multiselect" => $this->_feed_multi_select("feed_id", $feed_ids, 'style="width : 540px; height : 300px" dojoType="dijit.form.MultiSelect"') + "multiselect" => $this->_feed_multi_select("feed_id", $feed_ids, 'required="1" style="width : 540px; height : 300px" dojoType="fox.form.ValidationMultiSelect"') ]); } diff --git a/js/form/ValidationMultiSelect.js b/js/form/ValidationMultiSelect.js new file mode 100644 index 000000000..4e7263c61 --- /dev/null +++ b/js/form/ValidationMultiSelect.js @@ -0,0 +1,20 @@ +/* global define */ + +// only supports required for the time being +// TODO: maybe show dojo native error message? i dunno +define(["dojo/_base/declare", "dojo/_base/lang", "dijit/form/MultiSelect", ], + function(declare, lang, MultiSelect) { + + return declare('fox.form.ValidationMultiSelect', [MultiSelect], { + constructor: function(params){ + this.constraints = {}; + this.baseClass += ' dijitValidationMultiSelect'; + }, + validate: function(/*Boolean*/ isFocused){ + if (this.required && this.attr('value').length == 0) + return false; + + return true; + }, + }) + }); diff --git a/js/prefs.js b/js/prefs.js index a23a74856..8f4f45700 100755 --- a/js/prefs.js +++ b/js/prefs.js @@ -53,6 +53,7 @@ require(["dojo/_base/kernel", "fox/PrefLabelTree", "fox/Toolbar", "fox/SingleUseDialog", + "fox/form/ValidationMultiSelect", "fox/form/ValidationTextArea", "fox/form/Select", "fox/form/ComboButton", diff --git a/js/tt-rss.js b/js/tt-rss.js index 8d7dd00a6..4a7f2e643 100644 --- a/js/tt-rss.js +++ b/js/tt-rss.js @@ -51,6 +51,7 @@ require(["dojo/_base/kernel", "fox/FeedTree", "fox/Toolbar", "fox/SingleUseDialog", + "fox/form/ValidationMultiSelect", "fox/form/ValidationTextArea", "fox/form/Select", "fox/form/ComboButton", -- cgit v1.2.3 From 2843b9917133e5ed8e1adf4ffd12356459fc16e4 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 21 Feb 2021 13:08:34 +0300 Subject: minor filter UI layout fix --- classes/pref/filters.php | 2 +- js/CommonFilters.js | 8 +++----- themes/compact.css | 1 + themes/compact_night.css | 1 + themes/light.css | 1 + themes/light/tt-rss.less | 1 + themes/night.css | 1 + themes/night_blue.css | 1 + 8 files changed, 10 insertions(+), 6 deletions(-) diff --git a/classes/pref/filters.php b/classes/pref/filters.php index 9388cc8d7..fda4a6513 100755 --- a/classes/pref/filters.php +++ b/classes/pref/filters.php @@ -695,7 +695,7 @@ class Pref_Filters extends Handler_Protected { $feed_ids = explode(",", clean($_REQUEST["ids"])); print json_encode([ - "multiselect" => $this->_feed_multi_select("feed_id", $feed_ids, 'required="1" style="width : 540px; height : 300px" dojoType="fox.form.ValidationMultiSelect"') + "multiselect" => $this->_feed_multi_select("feed_id", $feed_ids, 'required="1" style="width : 100%; height : 300px" dojoType="fox.form.ValidationMultiSelect"') ]); } diff --git a/js/CommonFilters.js b/js/CommonFilters.js index 5874170b8..0c138760d 100644 --- a/js/CommonFilters.js +++ b/js/CommonFilters.js @@ -423,8 +423,8 @@ const Filters = { ${App.FormFields.hidden_tag("method", filter_id ? "editSave" : "add")} ${App.FormFields.hidden_tag("csrf_token", App.getInitParam('csrf_token'))} -
    - +
    @@ -496,9 +496,7 @@ const Filters = { -
    - -
    +
    ${Object.keys(options).map((name) => `
    diff --git a/themes/compact.css b/themes/compact.css index 16bdcf1f0..9baf14227 100644 --- a/themes/compact.css +++ b/themes/compact.css @@ -456,6 +456,7 @@ body.ttrss_main .dijitDialog .dlgSecCont { margin: 10px 20px; } body.ttrss_main .dijitDialog header.horizontal + section, +body.ttrss_main .dijitDialog section.horizontal, body.ttrss_main .dijitDialog .dlgSecHoriz + .dlgSecCont { margin: 10px 0; } diff --git a/themes/compact_night.css b/themes/compact_night.css index 37adf3fda..4d57208d0 100644 --- a/themes/compact_night.css +++ b/themes/compact_night.css @@ -456,6 +456,7 @@ body.ttrss_main .dijitDialog .dlgSecCont { margin: 10px 20px; } body.ttrss_main .dijitDialog header.horizontal + section, +body.ttrss_main .dijitDialog section.horizontal, body.ttrss_main .dijitDialog .dlgSecHoriz + .dlgSecCont { margin: 10px 0; } diff --git a/themes/light.css b/themes/light.css index 0f2ffc1b6..e47ee5adf 100644 --- a/themes/light.css +++ b/themes/light.css @@ -456,6 +456,7 @@ body.ttrss_main .dijitDialog .dlgSecCont { margin: 10px 20px; } body.ttrss_main .dijitDialog header.horizontal + section, +body.ttrss_main .dijitDialog section.horizontal, body.ttrss_main .dijitDialog .dlgSecHoriz + .dlgSecCont { margin: 10px 0; } diff --git a/themes/light/tt-rss.less b/themes/light/tt-rss.less index 2794d8177..7b24c7701 100644 --- a/themes/light/tt-rss.less +++ b/themes/light/tt-rss.less @@ -530,6 +530,7 @@ body.ttrss_main { } header.horizontal + section, + section.horizontal, .dlgSecHoriz + .dlgSecCont { margin : 10px 0; } diff --git a/themes/night.css b/themes/night.css index e012c92b2..8ca622d8d 100644 --- a/themes/night.css +++ b/themes/night.css @@ -457,6 +457,7 @@ body.ttrss_main .dijitDialog .dlgSecCont { margin: 10px 20px; } body.ttrss_main .dijitDialog header.horizontal + section, +body.ttrss_main .dijitDialog section.horizontal, body.ttrss_main .dijitDialog .dlgSecHoriz + .dlgSecCont { margin: 10px 0; } diff --git a/themes/night_blue.css b/themes/night_blue.css index b49a496e3..6d98bbb26 100644 --- a/themes/night_blue.css +++ b/themes/night_blue.css @@ -457,6 +457,7 @@ body.ttrss_main .dijitDialog .dlgSecCont { margin: 10px 20px; } body.ttrss_main .dijitDialog header.horizontal + section, +body.ttrss_main .dijitDialog section.horizontal, body.ttrss_main .dijitDialog .dlgSecHoriz + .dlgSecCont { margin: 10px 0; } -- cgit v1.2.3 From d1328321bebadcf8a530d3c05f52a8f3c58bb969 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 21 Feb 2021 15:16:39 +0300 Subject: move published OPML endpoint to public.php --- classes/handler/public.php | 21 +++++++++++++++++++++ classes/opml.php | 9 +++------ classes/pref/feeds.php | 4 ++-- classes/rpc.php | 13 +++++-------- opml.php | 36 ------------------------------------ themes/compact.css | 26 +++++++++++++++----------- themes/compact_night.css | 26 +++++++++++++++----------- themes/light.css | 26 +++++++++++++++----------- themes/light/prefs.less | 4 ++-- themes/light/tt-rss.less | 4 ++-- themes/night.css | 26 +++++++++++++++----------- themes/night_blue.css | 26 +++++++++++++++----------- 12 files changed, 110 insertions(+), 111 deletions(-) delete mode 100644 opml.php diff --git a/classes/handler/public.php b/classes/handler/public.php index 8076bb303..b0ce2306d 100755 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -737,6 +737,27 @@ class Handler_Public extends Handler { prepare( "SELECT owner_uid + FROM ttrss_access_keys WHERE + access_key = ? AND feed_id = 'OPML:Publish'"); + $sth->execute([$key]); + + if ($row = $sth->fetch()) { + $owner_uid = $row['owner_uid']; + + $opml = new OPML($_REQUEST); + $opml->opml_export("published.opml", $owner_uid, true, false); + + } else { + header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found"); + echo "File not found."; + } + } + function cached() { list ($cache_dir, $filename) = explode("/", $_GET["file"], 2); diff --git a/classes/opml.php b/classes/opml.php index 78ddb2842..04d287125 100644 --- a/classes/opml.php +++ b/classes/opml.php @@ -634,13 +634,10 @@ class OPML extends Handler_Protected { print "$msg
    "; } - static function opml_publish_url(){ - - $url_path = get_self_url_prefix(); - $url_path .= "/opml.php?op=publish&key=" . + static function get_publish_url(){ + return get_self_url_prefix() . + "/public.php?op=publishOpml&key=" . Feeds::_get_access_key('OPML:Publish', false, $_SESSION["uid"]); - - return $url_path; } function get_feed_category($feed_cat, $parent_cat_id = false) { diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index cf9e7c95e..8d3f84a03 100755 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -1329,14 +1329,14 @@ class Pref_Feeds extends Handler_Protected { } function getOPMLKey() { - print json_encode(["link" => OPML::opml_publish_url()]); + print json_encode(["link" => OPML::get_publish_url()]); } function regenOPMLKey() { $this->update_feed_access_key('OPML:Publish', false, $_SESSION["uid"]); - print json_encode(["link" => OPML::opml_publish_url()]); + print json_encode(["link" => OPML::get_publish_url()]); } function regenFeedKey() { diff --git a/classes/rpc.php b/classes/rpc.php index 20a11b994..95fd0f5ae 100755 --- a/classes/rpc.php +++ b/classes/rpc.php @@ -656,12 +656,10 @@ class RPC extends Handler_Protected {
      $hotkeys) { - - if ($cur_section) print "
    •  
    • "; - print "
    • " . $section . "

    • "; - $cur_section = $section; + ?> +
    • + $description) { @@ -705,9 +703,8 @@ class RPC extends Handler_Protected { } } } - print "
    "; - - ?> + ?> +
    diff --git a/opml.php b/opml.php deleted file mode 100644 index 6f13a6f3c..000000000 --- a/opml.php +++ /dev/null @@ -1,36 +0,0 @@ -prepare( "SELECT owner_uid - FROM ttrss_access_keys WHERE - access_key = ? AND feed_id = 'OPML:Publish'"); - $sth->execute([$key]); - - if ($row = $sth->fetch()) { - $owner_uid = $row['owner_uid']; - - $opml = new OPML($_REQUEST); - $opml->opml_export("", $owner_uid, true, false); - - } else { - print "User not found"; - } - } - -?> diff --git a/themes/compact.css b/themes/compact.css index 9baf14227..64f60641c 100644 --- a/themes/compact.css +++ b/themes/compact.css @@ -898,12 +898,14 @@ body.ttrss_main .dijitTooltipBelow .dijitTooltipConnector { body.ttrss_main .dijitTooltipAbove .dijitTooltipConnector { border-top-color: #1c5c7d; } -body.ttrss_main .dijitDialog h1:first-of-type, -body.ttrss_main .dijitDialog h2:first-of-type, -body.ttrss_main .dijitDialog h3:first-of-type, -body.ttrss_main .dijitDialog h4:first-of-type { - margin-top: 0px; -} +/*body.ttrss_main .dijitDialog { + h1:first-of-type, + h2:first-of-type, + h3:first-of-type, + h4:first-of-type { + margin-top: 0px; + } +}*/ body.ttrss_main[view-mode="marked"] #feeds-holder #feedTree .dijitTreeRow.Has_Marked .dijitTreeLabel { color: #257aa7; } @@ -1413,6 +1415,13 @@ body.ttrss_prefs { background-color: #f5f5f5; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; + /*.dijitContentPane { + h1:first-of-type, + h2:first-of-type, + h3:first-of-type { + margin-top: 0px; + } + }*/ } body.ttrss_prefs h1, body.ttrss_prefs h2, @@ -1422,11 +1431,6 @@ body.ttrss_prefs h4 { font-weight: 600; color: #555; } -body.ttrss_prefs .dijitContentPane h1:first-of-type, -body.ttrss_prefs .dijitContentPane h2:first-of-type, -body.ttrss_prefs .dijitContentPane h3:first-of-type { - margin-top: 0px; -} body.ttrss_prefs #footer, body.ttrss_prefs #header { padding: 8px; diff --git a/themes/compact_night.css b/themes/compact_night.css index 4d57208d0..a5acb9c86 100644 --- a/themes/compact_night.css +++ b/themes/compact_night.css @@ -898,12 +898,14 @@ body.ttrss_main .dijitTooltipBelow .dijitTooltipConnector { body.ttrss_main .dijitTooltipAbove .dijitTooltipConnector { border-top-color: #d29745; } -body.ttrss_main .dijitDialog h1:first-of-type, -body.ttrss_main .dijitDialog h2:first-of-type, -body.ttrss_main .dijitDialog h3:first-of-type, -body.ttrss_main .dijitDialog h4:first-of-type { - margin-top: 0px; -} +/*body.ttrss_main .dijitDialog { + h1:first-of-type, + h2:first-of-type, + h3:first-of-type, + h4:first-of-type { + margin-top: 0px; + } +}*/ body.ttrss_main[view-mode="marked"] #feeds-holder #feedTree .dijitTreeRow.Has_Marked .dijitTreeLabel { color: #b87d2c; } @@ -1413,6 +1415,13 @@ body.ttrss_prefs { background-color: #222; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; + /*.dijitContentPane { + h1:first-of-type, + h2:first-of-type, + h3:first-of-type { + margin-top: 0px; + } + }*/ } body.ttrss_prefs h1, body.ttrss_prefs h2, @@ -1422,11 +1431,6 @@ body.ttrss_prefs h4 { font-weight: 600; color: #ccc; } -body.ttrss_prefs .dijitContentPane h1:first-of-type, -body.ttrss_prefs .dijitContentPane h2:first-of-type, -body.ttrss_prefs .dijitContentPane h3:first-of-type { - margin-top: 0px; -} body.ttrss_prefs #footer, body.ttrss_prefs #header { padding: 8px; diff --git a/themes/light.css b/themes/light.css index e47ee5adf..7030e5182 100644 --- a/themes/light.css +++ b/themes/light.css @@ -898,12 +898,14 @@ body.ttrss_main .dijitTooltipBelow .dijitTooltipConnector { body.ttrss_main .dijitTooltipAbove .dijitTooltipConnector { border-top-color: #1c5c7d; } -body.ttrss_main .dijitDialog h1:first-of-type, -body.ttrss_main .dijitDialog h2:first-of-type, -body.ttrss_main .dijitDialog h3:first-of-type, -body.ttrss_main .dijitDialog h4:first-of-type { - margin-top: 0px; -} +/*body.ttrss_main .dijitDialog { + h1:first-of-type, + h2:first-of-type, + h3:first-of-type, + h4:first-of-type { + margin-top: 0px; + } +}*/ body.ttrss_main[view-mode="marked"] #feeds-holder #feedTree .dijitTreeRow.Has_Marked .dijitTreeLabel { color: #257aa7; } @@ -1413,6 +1415,13 @@ body.ttrss_prefs { background-color: #f5f5f5; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; + /*.dijitContentPane { + h1:first-of-type, + h2:first-of-type, + h3:first-of-type { + margin-top: 0px; + } + }*/ } body.ttrss_prefs h1, body.ttrss_prefs h2, @@ -1422,11 +1431,6 @@ body.ttrss_prefs h4 { font-weight: 600; color: #555; } -body.ttrss_prefs .dijitContentPane h1:first-of-type, -body.ttrss_prefs .dijitContentPane h2:first-of-type, -body.ttrss_prefs .dijitContentPane h3:first-of-type { - margin-top: 0px; -} body.ttrss_prefs #footer, body.ttrss_prefs #header { padding: 8px; diff --git a/themes/light/prefs.less b/themes/light/prefs.less index ec3006ce5..510388391 100644 --- a/themes/light/prefs.less +++ b/themes/light/prefs.less @@ -9,13 +9,13 @@ body.ttrss_prefs { color : @default-text; } - .dijitContentPane { + /*.dijitContentPane { h1:first-of-type, h2:first-of-type, h3:first-of-type { margin-top: 0px; } - } + }*/ #footer, #header { padding : 8px; diff --git a/themes/light/tt-rss.less b/themes/light/tt-rss.less index 7b24c7701..35eec3e48 100644 --- a/themes/light/tt-rss.less +++ b/themes/light/tt-rss.less @@ -1049,14 +1049,14 @@ body.ttrss_main { } } -body.ttrss_main .dijitDialog { +/*body.ttrss_main .dijitDialog { h1:first-of-type, h2:first-of-type, h3:first-of-type, h4:first-of-type { margin-top: 0px; } -} +}*/ body.ttrss_main[view-mode="marked"] #feeds-holder #feedTree { .dijitTreeRow.Has_Marked .dijitTreeLabel { diff --git a/themes/night.css b/themes/night.css index 8ca622d8d..73f1ec96d 100644 --- a/themes/night.css +++ b/themes/night.css @@ -899,12 +899,14 @@ body.ttrss_main .dijitTooltipBelow .dijitTooltipConnector { body.ttrss_main .dijitTooltipAbove .dijitTooltipConnector { border-top-color: #d29745; } -body.ttrss_main .dijitDialog h1:first-of-type, -body.ttrss_main .dijitDialog h2:first-of-type, -body.ttrss_main .dijitDialog h3:first-of-type, -body.ttrss_main .dijitDialog h4:first-of-type { - margin-top: 0px; -} +/*body.ttrss_main .dijitDialog { + h1:first-of-type, + h2:first-of-type, + h3:first-of-type, + h4:first-of-type { + margin-top: 0px; + } +}*/ body.ttrss_main[view-mode="marked"] #feeds-holder #feedTree .dijitTreeRow.Has_Marked .dijitTreeLabel { color: #b87d2c; } @@ -1414,6 +1416,13 @@ body.ttrss_prefs { background-color: #222; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; + /*.dijitContentPane { + h1:first-of-type, + h2:first-of-type, + h3:first-of-type { + margin-top: 0px; + } + }*/ } body.ttrss_prefs h1, body.ttrss_prefs h2, @@ -1423,11 +1432,6 @@ body.ttrss_prefs h4 { font-weight: 600; color: #ccc; } -body.ttrss_prefs .dijitContentPane h1:first-of-type, -body.ttrss_prefs .dijitContentPane h2:first-of-type, -body.ttrss_prefs .dijitContentPane h3:first-of-type { - margin-top: 0px; -} body.ttrss_prefs #footer, body.ttrss_prefs #header { padding: 8px; diff --git a/themes/night_blue.css b/themes/night_blue.css index 6d98bbb26..aa3ead557 100644 --- a/themes/night_blue.css +++ b/themes/night_blue.css @@ -899,12 +899,14 @@ body.ttrss_main .dijitTooltipBelow .dijitTooltipConnector { body.ttrss_main .dijitTooltipAbove .dijitTooltipConnector { border-top-color: #2e99d1; } -body.ttrss_main .dijitDialog h1:first-of-type, -body.ttrss_main .dijitDialog h2:first-of-type, -body.ttrss_main .dijitDialog h3:first-of-type, -body.ttrss_main .dijitDialog h4:first-of-type { - margin-top: 0px; -} +/*body.ttrss_main .dijitDialog { + h1:first-of-type, + h2:first-of-type, + h3:first-of-type, + h4:first-of-type { + margin-top: 0px; + } +}*/ body.ttrss_main[view-mode="marked"] #feeds-holder #feedTree .dijitTreeRow.Has_Marked .dijitTreeLabel { color: #257aa7; } @@ -1414,6 +1416,13 @@ body.ttrss_prefs { background-color: #222; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; + /*.dijitContentPane { + h1:first-of-type, + h2:first-of-type, + h3:first-of-type { + margin-top: 0px; + } + }*/ } body.ttrss_prefs h1, body.ttrss_prefs h2, @@ -1423,11 +1432,6 @@ body.ttrss_prefs h4 { font-weight: 600; color: #ccc; } -body.ttrss_prefs .dijitContentPane h1:first-of-type, -body.ttrss_prefs .dijitContentPane h2:first-of-type, -body.ttrss_prefs .dijitContentPane h3:first-of-type { - margin-top: 0px; -} body.ttrss_prefs #footer, body.ttrss_prefs #header { padding: 8px; -- cgit v1.2.3 From 1bd5152c8016c6019a647bf995d29d81c6b731ed Mon Sep 17 00:00:00 2001 From: wn_ Date: Sat, 20 Feb 2021 13:44:42 +0000 Subject: Open the default feed after unsubscribing. Previously the UI appeared to hang, even though the backend request had already completed successfully. --- js/CommonDialogs.js | 2 +- js/Feeds.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/js/CommonDialogs.js b/js/CommonDialogs.js index 704e797d3..a100a546d 100644 --- a/js/CommonDialogs.js +++ b/js/CommonDialogs.js @@ -312,7 +312,7 @@ const CommonDialogs = { } else { if (feed_id == Feeds.getActive()) setTimeout(() => { - Feeds.open({feed: -5}) + Feeds.openDefaultFeed(); }, 100); diff --git a/js/Feeds.js b/js/Feeds.js index e9ac5328d..4841fd0df 100644 --- a/js/Feeds.js +++ b/js/Feeds.js @@ -3,6 +3,7 @@ /* global __, App, Headlines, xhrPost, xhr, dojo, dijit, fox, PluginHost, Notify, fox */ const Feeds = { + _default_feed_id: -3, counters_last_request: 0, _active_feed_id: undefined, _active_feed_is_cat: false, @@ -109,6 +110,9 @@ const Feeds = { } return false; // block unneeded form submits }, + openDefaultFeed: function() { + this.open({feed: this._default_feed_id}); + }, openNextUnread: function() { const is_cat = this.activeIsCat(); const nuf = this.getNextUnread(this.getActive(), is_cat); @@ -215,7 +219,7 @@ const Feeds = { if (hash_feed_id != undefined) { this.open({feed: hash_feed_id, is_cat: hash_feed_is_cat}); } else { - this.open({feed: -3}); + this.openDefaultFeed(); } this.hideOrShowFeeds(App.getInitParam("hide_read_feeds")); -- cgit v1.2.3 From 521d0b65c73f90d6e86c1a38dcab492b5a23982b Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 21 Feb 2021 16:02:57 +0300 Subject: batch feed editor: use tab layout, cleanup --- classes/pref/feeds.php | 243 +++++++++++++++++++------------------------------ js/PrefFeedTree.js | 21 +++-- 2 files changed, 108 insertions(+), 156 deletions(-) diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index 8d3f84a03..c901bc96e 100755 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -22,11 +22,6 @@ class Pref_Feeds extends Handler_Protected { return $rv; } - function batch_edit_cbox($elem, $label = false) { - print ""; - } - function renamecat() { $title = clean($_REQUEST['title']); $id = clean($_REQUEST['id']); @@ -571,165 +566,117 @@ class Pref_Feeds extends Handler_Protected { } } + private function _batch_toggle_checkbox($name) { + return \Controls\checkbox_tag("", false, "", + ["data-control-for" => $name, "title" => __("Check to enable field"), "onchange" => "App.dialogOf(this).toggleField(this)"]); + } + function editfeeds() { global $purge_intervals; global $update_intervals; $feed_ids = clean($_REQUEST["ids"]); - print_notice("Enable the options you wish to apply using checkboxes on the right:"); - - print \Controls\hidden_tag("ids", "$feed_ids"); - print \Controls\hidden_tag("op", "pref-feeds"); - print \Controls\hidden_tag("method", "batchEditSave"); - - print "
    ".__("Feed")."
    "; - print "
    "; - - /* Category */ - - if (get_pref('ENABLE_FEED_CATS')) { - - print "
    "; - - print " "; - - print \Controls\select_feeds_cats("cat_id", null, ['disabled' => '1']); - - $this->batch_edit_cbox("cat_id"); - - print "
    "; - } - - /* FTS Stemming Language */ - - if (DB_TYPE == "pgsql") { - print "
    "; - - print " "; - print \Controls\select_tag("feed_language", "", $this::get_ts_languages(), ["disabled"=> 1]); - - $this->batch_edit_cbox("feed_language"); - - print "
    "; - } - - print "
    "; - - print "
    ".__("Update")."
    "; - print "
    "; - - /* Update Interval */ - - print "
    "; - - print " "; - $local_update_intervals = $update_intervals; $local_update_intervals[0] .= sprintf(" (%s)", $update_intervals[get_pref("DEFAULT_UPDATE_INTERVAL")]); - print \Controls\select_hash("update_interval", "", $local_update_intervals, ["disabled" => 1]); - - $this->batch_edit_cbox("update_interval"); - - print "
    "; - - /* Purge intl */ - - if (FORCE_ARTICLE_PURGE == 0) { - - print "
    "; - - print " "; - - $local_purge_intervals = $purge_intervals; - $default_purge_interval = get_pref("PURGE_OLD_DAYS"); - - if ($default_purge_interval > 0) - $local_purge_intervals[0] .= " " . T_sprintf("(%d days)", $default_purge_interval); - else - $local_purge_intervals[0] .= " " . sprintf("(%s)", __("Disabled")); + $local_purge_intervals = $purge_intervals; + $default_purge_interval = get_pref("PURGE_OLD_DAYS"); - print \Controls\select_hash("purge_interval", "", $local_purge_intervals, ["disabled" => 1]); - - $this->batch_edit_cbox("purge_interval"); - - print "
    "; - } - - print "
    "; - print "
    ".__("Authentication")."
    "; - print "
    "; - - print "
    "; - - print ""; - - $this->batch_edit_cbox("auth_login"); - - print ""; - - $this->batch_edit_cbox("auth_pass"); - - print "
    "; - - print "
    "; - print "
    ".__("Options")."
    "; - print "
    "; - - print "
    "; - print ""; - - print " "; $this->batch_edit_cbox("include_in_digest", "include_in_digest_l"); - - print "
    "; - - print ""; - - print " "; $this->batch_edit_cbox("always_display_enclosures", "always_display_enclosures_l"); - - print "
    "; - - print ""; - - print " "; $this->batch_edit_cbox("hide_images", "hide_images_l"); - - print "
    "; - - print ""; - - print " "; $this->batch_edit_cbox("cache_images", "cache_images_l"); - - print "
    "; + if ($default_purge_interval > 0) + $local_purge_intervals[0] .= " " . T_sprintf("(%d days)", $default_purge_interval); + else + $local_purge_intervals[0] .= " " . sprintf("(%s)", __("Disabled")); - print ""; + $options = [ + "include_in_digest" => __('Include in e-mail digest'), + "always_display_enclosures" => __('Always display image attachments'), + "hide_images" => __('Do not embed media'), + "cache_images" => __('Cache media'), + "mark_unread_on_update" => __('Mark updated articles as unread') + ]; - print " "; $this->batch_edit_cbox("mark_unread_on_update", "mark_unread_on_update_l"); + print_notice("Enable the options you wish to apply using checkboxes on the right."); + ?> - print "
    "; + + + - print "
    "; +
    +
    +
    + +
    + + '1']) ?> + _batch_toggle_checkbox("cat_id") ?> +
    + - print "
    - - -
    "; + +
    + + 1]) ?> + _batch_toggle_checkbox("feed_language") ?> +
    + +
    + +
    + +
    +
    + + 1]) ?> + _batch_toggle_checkbox("update_interval") ?> +
    + + +
    + + 1]) ?> + _batch_toggle_checkbox("purge_interval") ?> +
    + +
    +
    +
    +
    +
    + + + _batch_toggle_checkbox("auth_login") ?> +
    +
    + + + _batch_toggle_checkbox("auth_pass") ?> +
    +
    +
    +
    + $caption) { + ?> +
    + +
    + +
    +
    - return; +
    + + +
    + Date: Sun, 21 Feb 2021 16:06:46 +0300 Subject: batch editor: comment out getChildByName --- js/PrefFeedTree.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/PrefFeedTree.js b/js/PrefFeedTree.js index 468e2025c..bb5d25e67 100644 --- a/js/PrefFeedTree.js +++ b/js/PrefFeedTree.js @@ -301,7 +301,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_b try { const dialog = new fox.SingleUseDialog({ title: __("Edit Multiple Feeds"), - getChildByName: function (name) { + /*getChildByName: function (name) { let rv = null; this.getChildren().forEach( function (child) { @@ -311,7 +311,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_b } }); return rv; - }, + },*/ toggleField: function (checkbox) { const name = checkbox.attr("data-control-for"); const target = dijit.getEnclosingWidget(dialog.domNode.querySelector(`input[name="${name}"]`)); -- cgit v1.2.3 From 861a632ac7c283c55dfd947ed0152d9846fa2ac0 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 21 Feb 2021 18:04:44 +0300 Subject: move published opml JS code to pref helpers --- classes/pref/feeds.php | 2 +- js/CommonDialogs.js | 58 -------------------------------------------------- js/PrefHelpers.js | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 59 deletions(-) diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index c901bc96e..e583a5f51 100755 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -1043,7 +1043,7 @@ class Pref_Feeds extends Handler_Protected {

    - diff --git a/js/CommonDialogs.js b/js/CommonDialogs.js index a100a546d..321ddf6d3 100644 --- a/js/CommonDialogs.js +++ b/js/CommonDialogs.js @@ -587,64 +587,6 @@ const CommonDialogs = { dialog.show(); }, - publishedOPML: function() { - - Notify.progress("Loading, please wait...", true); - - xhr.json("backend.php", {op: "pref-feeds", method: "getOPMLKey"}, (reply) => { - try { - const dialog = new fox.SingleUseDialog({ - title: __("Public OPML URL"), - regenOPMLKey: function() { - if (confirm(__("Replace current OPML publishing address with a new one?"))) { - Notify.progress("Trying to change address...", true); - - xhr.json("backend.php", {op: "pref-feeds", method: "regenOPMLKey"}, (reply) => { - if (reply) { - const new_link = reply.link; - const target = this.domNode.querySelector('.generated_url'); - - if (new_link && target) { - target.href = new_link; - target.innerHTML = new_link; - - Notify.close(); - - } else { - Notify.error("Could not change feed URL."); - } - } - }); - } - return false; - }, - content: ` -
    ${__("Your Public OPML URL is:")}
    -
    - -
    -
    - - -
    - ` - }); - - dialog.show(); - - Notify.close(); - - } catch (e) { - App.Error.report(e); - } - }); - }, generatedFeed: function(feed, is_cat, search = "") { Notify.progress("Loading, please wait...", true); diff --git a/js/PrefHelpers.js b/js/PrefHelpers.js index d27e0e071..62f6d91b1 100644 --- a/js/PrefHelpers.js +++ b/js/PrefHelpers.js @@ -347,5 +347,62 @@ const Helpers = { console.log("export"); window.open("backend.php?op=opml&method=export&" + dojo.formToQuery("opmlExportForm")); }, + publish: function() { + Notify.progress("Loading, please wait...", true); + + xhr.json("backend.php", {op: "pref-feeds", method: "getOPMLKey"}, (reply) => { + try { + const dialog = new fox.SingleUseDialog({ + title: __("Public OPML URL"), + regenOPMLKey: function() { + if (confirm(__("Replace current OPML publishing address with a new one?"))) { + Notify.progress("Trying to change address...", true); + + xhr.json("backend.php", {op: "pref-feeds", method: "regenOPMLKey"}, (reply) => { + if (reply) { + const new_link = reply.link; + const target = this.domNode.querySelector('.generated_url'); + + if (new_link && target) { + target.href = new_link; + target.innerHTML = new_link; + + Notify.close(); + + } else { + Notify.error("Could not change feed URL."); + } + } + }); + } + return false; + }, + content: ` +
    ${__("Your Public OPML URL is:")}
    +
    + +
    +
    + + +
    + ` + }); + + dialog.show(); + + Notify.close(); + + } catch (e) { + App.Error.report(e); + } + }); + }, } }; -- cgit v1.2.3 From cb401af6f6f278b94b664569796c49f045e3ffde Mon Sep 17 00:00:00 2001 From: wn_ Date: Sun, 21 Feb 2021 18:40:43 +0000 Subject: Let 'RSSUtils::check_feed_favicon' update existing favicons. --- classes/rssutils.php | 106 +++++++++++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 46 deletions(-) diff --git a/classes/rssutils.php b/classes/rssutils.php index 5fb2e7712..13f63bc7a 100755 --- a/classes/rssutils.php +++ b/classes/rssutils.php @@ -594,7 +594,7 @@ class RSSUtils { $favicon_file = ICONS_DIR . "/$feed.ico"; $favicon_modified = file_exists($favicon_file) ? filemtime($favicon_file) : -1; - Debug::log("checking favicon...", Debug::$LOG_VERBOSE); + Debug::log("checking favicon for feed $feed...", Debug::$LOG_VERBOSE); self::check_feed_favicon($site_url, $feed); $favicon_modified_new = file_exists($favicon_file) ? filemtime($favicon_file) : -1; @@ -1643,58 +1643,72 @@ class RSSUtils { } static function check_feed_favicon($site_url, $feed) { - # print "FAVICON [$site_url]: $favicon_url\n"; - $icon_file = ICONS_DIR . "/$feed.ico"; - if (!file_exists($icon_file)) { - $favicon_url = self::get_favicon_url($site_url); + $favicon_url = self::get_favicon_url($site_url); + if (!$favicon_url) { + Debug::log("couldn't find favicon URL in $site_url", Debug::$LOG_VERBOSE); + return false; + } - if ($favicon_url) { - // Limiting to "image" type misses those served with text/plain - $contents = UrlHelper::fetch($favicon_url); // , "image"); + // Limiting to "image" type misses those served with text/plain + $contents = UrlHelper::fetch(['url' => $favicon_url]); // , "image"); + if (!$contents) { + Debug::log("fetching favicon $favicon_url failed", Debug::$LOG_VERBOSE); + return false; + } - if ($contents) { - // Crude image type matching. - // Patterns gleaned from the file(1) source code. - if (preg_match('/^\x00\x00\x01\x00/', $contents)) { - // 0 string \000\000\001\000 MS Windows icon resource - //error_log("check_feed_favicon: favicon_url=$favicon_url isa MS Windows icon resource"); - } - elseif (preg_match('/^GIF8/', $contents)) { - // 0 string GIF8 GIF image data - //error_log("check_feed_favicon: favicon_url=$favicon_url isa GIF image"); - } - elseif (preg_match('/^\x89PNG\x0d\x0a\x1a\x0a/', $contents)) { - // 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data - //error_log("check_feed_favicon: favicon_url=$favicon_url isa PNG image"); - } - elseif (preg_match('/^\xff\xd8/', $contents)) { - // 0 beshort 0xffd8 JPEG image data - //error_log("check_feed_favicon: favicon_url=$favicon_url isa JPG image"); - } - elseif (preg_match('/^BM/', $contents)) { - // 0 string BM PC bitmap (OS2, Windows BMP files) - //error_log("check_feed_favicon, favicon_url=$favicon_url isa BMP image"); - } - else { - //error_log("check_feed_favicon: favicon_url=$favicon_url isa UNKNOWN type"); - $contents = ""; - } - } + $original_contents = file_exists($icon_file) ? file_get_contents($icon_file) : null; + if ($original_contents) { + if (strcmp($contents, $original_contents) === 0) { + Debug::log("favicon content has not changed", Debug::$LOG_VERBOSE); + return $icon_file; + } + Debug::log("favicon content has changed", Debug::$LOG_VERBOSE); + } - if ($contents) { - $fp = @fopen($icon_file, "w"); + // Crude image type matching. + // Patterns gleaned from the file(1) source code. + if (preg_match('/^\x00\x00\x01\x00/', $contents)) { + // 0 string \000\000\001\000 MS Windows icon resource + //error_log("check_feed_favicon: favicon_url=$favicon_url isa MS Windows icon resource"); + } + elseif (preg_match('/^GIF8/', $contents)) { + // 0 string GIF8 GIF image data + //error_log("check_feed_favicon: favicon_url=$favicon_url isa GIF image"); + } + elseif (preg_match('/^\x89PNG\x0d\x0a\x1a\x0a/', $contents)) { + // 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data + //error_log("check_feed_favicon: favicon_url=$favicon_url isa PNG image"); + } + elseif (preg_match('/^\xff\xd8/', $contents)) { + // 0 beshort 0xffd8 JPEG image data + //error_log("check_feed_favicon: favicon_url=$favicon_url isa JPG image"); + } + elseif (preg_match('/^BM/', $contents)) { + // 0 string BM PC bitmap (OS2, Windows BMP files) + //error_log("check_feed_favicon, favicon_url=$favicon_url isa BMP image"); + } + else { + //error_log("check_feed_favicon: favicon_url=$favicon_url isa UNKNOWN type"); + Debug::log("favicon $favicon_url type is unknown (not updating)", Debug::$LOG_VERBOSE); + return false; + } - if ($fp) { - fwrite($fp, $contents); - fclose($fp); - chmod($icon_file, 0644); - } - } - } - return $icon_file; + Debug::log("setting contents of $icon_file", Debug::$LOG_VERBOSE); + + $fp = @fopen($icon_file, "w"); + if (!$fp) { + Debug::log("failed to open $icon_file for writing", Debug::$LOG_VERBOSE); + return false; } + + fwrite($fp, $contents); + fclose($fp); + chmod($icon_file, 0644); + clearstatcache(); + + return $icon_file; } static function is_gzipped($feed_data) { -- cgit v1.2.3 From f6bfb89b2912933f638416135dff7bb8cb28890d Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 21 Feb 2021 23:18:32 +0300 Subject: pref-prefs: switch to new control shorthand in a few places --- classes/pref/prefs.php | 51 +++++++++++++++++++++----------------------------- include/controls.php | 17 +++++++++++++++++ 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index 2ea2e9f01..a26281fee 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -712,59 +712,50 @@ class Pref_Prefs extends Handler_Protected { array_push($listed_boolean_prefs, $pref_name); - $is_checked = ($value == "true") ? "checked=\"checked\"" : ""; - if ($pref_name == "PURGE_UNREAD_ARTICLES" && FORCE_ARTICLE_PURGE != 0) { - $is_disabled = "disabled=\"1\""; - $is_checked = "checked=\"checked\""; + $is_disabled = true; + $is_checked = true; } else { - $is_disabled = ""; + $is_disabled = false; + $is_checked = ($value == "true"); } - print ""; + print \Controls\checkbox_tag($pref_name, $is_checked, "true", + ["disabled" => $is_disabled], "CB_$pref_name"); } else if (in_array($pref_name, ['FRESH_ARTICLE_MAX_AGE', 'PURGE_OLD_DAYS', 'LONG_DATE_FORMAT', 'SHORT_DATE_FORMAT'])) { - $regexp = ($type_name == 'integer') ? 'regexp="^\d*$"' : ''; - if ($pref_name == "PURGE_OLD_DAYS" && FORCE_ARTICLE_PURGE != 0) { - $is_disabled = "disabled='1'"; + $attributes = ["disabled" => true, "required" => true]; $value = FORCE_ARTICLE_PURGE; } else { - $is_disabled = ""; + $attributes = ["required" => true]; } if ($type_name == 'integer') - print ""; + print \Controls\number_spinner_tag($pref_name, $value, $attributes); else - print ""; + print \Controls\input_tag($pref_name, $value, "text", $attributes); } else if ($pref_name == "SSL_CERT_SERIAL") { - print ""; + print \Controls\input_tag($pref_name, $value, "text", ["readonly" => true], "SSL_CERT_SERIAL"); $cert_serial = htmlspecialchars(get_ssl_certificate_id()); - $has_serial = ($cert_serial) ? "false" : "true"; + $has_serial = ($cert_serial) ? true : false; - print ""; + print \Controls\button_tag(__('Register'), "", [ + "disabled" => !$has_serial, + "onclick" => "dijit.byId('SSL_CERT_SERIAL').attr('value', '$cert_serial')"]); - print ""; + print \Controls\button_tag(__('Clear'), "", [ + "class" => "alt-danger", + "onclick" => "dijit.byId('SSL_CERT_SERIAL').attr('value', '')"]); - print ""; + print \Controls\button_tag(\Controls\icon("help") . " " . __("More info..."), "", [ + "class" => "alt-info", + "onclick" => "window.open('https://tt-rss.org/wiki/SSL%20Certificate%20Authentication')"]); } else if ($pref_name == 'DIGEST_PREFERRED_TIME') { print " $v) { + + // special handling for "disabled" + if ($k === "disabled" && !sql_bool_to_bool($v)) + continue; + $rv .= "$k=\"" . htmlspecialchars($v) . "\""; } @@ -30,6 +35,18 @@ return ""; } + function input_tag(string $name, string $value, string $type = "text", array $attributes = [], string $id = "") { + $attributes_str = attributes_to_string($attributes); + $dojo_type = strpos($attributes_str, "dojoType") === false ? "dojoType='dijit.form.TextBox'" : ""; + + return ""; + } + + function number_spinner_tag(string $name, string $value, array $attributes = [], string $id = "") { + return input_tag($name, $value, "text", array_merge(["dojoType" => "dijit.form.NumberSpinner"], $attributes), $id); + } + function submit_tag(string $value, array $attributes = []) { return button_tag($value, "submit", array_merge(["class" => "alt-primary"], $attributes)); } -- cgit v1.2.3 From 6f29ecbbb96440552f434f5f487b96753bdb02df Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 21 Feb 2021 23:19:58 +0300 Subject: add phpstan config --- phpstan.neon | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 phpstan.neon diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 000000000..0703e0d15 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,17 @@ +parameters: + level: 5 + ignoreErrors: + - '#Constant.*not found#' + - '#Call to an undefined method DOMNode::getAttribute\(\).#' + - '#PHPDoc tag @param has invalid value#' + - message: '##' + paths: + - lib/* + - vendor/* + - plugins/*/vendor/* + - plugins.local/*/vendor/* + + excludePaths: + - node_modules/* + paths: + - . \ No newline at end of file -- cgit v1.2.3 From 02a9485966dbbac1ed52ecbfb29fcc15125cba43 Mon Sep 17 00:00:00 2001 From: wn_ Date: Sun, 21 Feb 2021 23:30:31 +0000 Subject: Try to limit max favicon size, don't store current/old in a var. --- classes/rssutils.php | 12 ++++++++---- include/functions.php | 2 ++ utils/phpstan_tunables.php | 2 ++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/classes/rssutils.php b/classes/rssutils.php index 13f63bc7a..6785ab3f5 100755 --- a/classes/rssutils.php +++ b/classes/rssutils.php @@ -1652,15 +1652,19 @@ class RSSUtils { } // Limiting to "image" type misses those served with text/plain - $contents = UrlHelper::fetch(['url' => $favicon_url]); // , "image"); + $contents = UrlHelper::fetch([ + 'url' => $favicon_url, + 'max_size' => MAX_FAVICON_FILE_SIZE, + //'type' => 'image', + ]); if (!$contents) { Debug::log("fetching favicon $favicon_url failed", Debug::$LOG_VERBOSE); return false; } - $original_contents = file_exists($icon_file) ? file_get_contents($icon_file) : null; - if ($original_contents) { - if (strcmp($contents, $original_contents) === 0) { + $original_contents_md5 = file_exists($icon_file) ? md5_file($icon_file) : null; + if ($original_contents_md5) { + if (md5($contents) == $original_contents_md5) { Debug::log("favicon content has not changed", Debug::$LOG_VERBOSE); return $icon_file; } diff --git a/include/functions.php b/include/functions.php index 174ef39f0..df8730aca 100644 --- a/include/functions.php +++ b/include/functions.php @@ -68,6 +68,8 @@ // do not cache files larger than that (bytes) define_default('MAX_DOWNLOAD_FILE_SIZE', 16*1024*1024); // do not download general files larger than that (bytes) + define_default('MAX_FAVICON_FILE_SIZE', 1*1024*1024); + // do not download favicon files larger than that (bytes) define_default('CACHE_MAX_DAYS', 7); // max age in days for various automatically cached (temporary) files define_default('MAX_CONDITIONAL_INTERVAL', 3600*12); diff --git a/utils/phpstan_tunables.php b/utils/phpstan_tunables.php index 7d5d8f03a..e192bcdba 100644 --- a/utils/phpstan_tunables.php +++ b/utils/phpstan_tunables.php @@ -27,6 +27,8 @@ // do not cache files larger than that (bytes) define('MAX_DOWNLOAD_FILE_SIZE', 16*1024*1024); // do not download general files larger than that (bytes) + define('MAX_FAVICON_FILE_SIZE', 1*1024*1024); + // do not download favicon files larger than that (bytes) define('CACHE_MAX_DAYS', 7); // max age in days for various automatically cached (temporary) files define('MAX_CONDITIONAL_INTERVAL', 3600*12); -- cgit v1.2.3 From 33fff2686946021314a24feef61032beaf48e7a4 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Mon, 22 Feb 2021 10:00:50 +0300 Subject: reinstate HOOK_RENDER_ENCLOSURE --- classes/article.php | 23 ++++++++++++++++++----- classes/pluginhost.php | 4 ++-- js/Article.js | 6 +----- plugins/af_youtube_embed/init.php | 6 +++--- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/classes/article.php b/classes/article.php index dd1470caa..acd83694c 100755 --- a/classes/article.php +++ b/classes/article.php @@ -343,6 +343,7 @@ class Article extends Handler_Protected { } $rv = [ + 'formatted' => '', 'entries' => [] ]; @@ -358,12 +359,24 @@ class Article extends Handler_Protected { // this is highly approximate $enc["filename"] = basename($enc["content_url"]); - PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_ENCLOSURE_ENTRY, - function ($result) use (&$enc) { - $enc = $result; + $rendered_enc = ""; + PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_RENDER_ENCLOSURE, + function ($result) use (&$rendered_enc) { + $rendered_enc = $result; }, - $enc, $id); - array_push($rv['entries'], $enc); + $enc, $id, $rv); + + if ($rendered_enc) { + $rv['formatted'] .= $rendered_enc; + } else { + PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_ENCLOSURE_ENTRY, + function ($result) use (&$enc) { + $enc = $result; + }, + $enc, $id, $rv); + + array_push($rv['entries'], $enc); + } } return $rv; diff --git a/classes/pluginhost.php b/classes/pluginhost.php index d50c5a706..a05938111 100755 --- a/classes/pluginhost.php +++ b/classes/pluginhost.php @@ -51,11 +51,11 @@ class PluginHost { const HOOK_FORMAT_ENCLOSURES = "hook_format_enclosures"; // hook__format_enclosures($rv, $result, $id, $always_display_enclosures, $article_content, $hide_images) (byref) const HOOK_SUBSCRIBE_FEED = "hook_subscribe_feed"; // hook_subscribe_feed($contents, $url, $auth_login, $auth_pass) (byref) const HOOK_HEADLINES_BEFORE = "hook_headlines_before"; // hook_headlines_before($feed, $is_cat, $qfh_ret) - const HOOK_RENDER_ENCLOSURE = "hook_render_enclosure"; // hook_render_enclosure($entry, $hide_images) + const HOOK_RENDER_ENCLOSURE = "hook_render_enclosure"; // hook_render_enclosure($entry, $id, $rv) const HOOK_ARTICLE_FILTER_ACTION = "hook_article_filter_action"; // hook_article_filter_action($article, $action) const HOOK_ARTICLE_EXPORT_FEED = "hook_article_export_feed"; // hook_article_export_feed($line, $feed, $is_cat, $owner_uid) (byref) const HOOK_MAIN_TOOLBAR_BUTTON = "hook_main_toolbar_button"; // hook_main_toolbar_button() - const HOOK_ENCLOSURE_ENTRY = "hook_enclosure_entry"; // hook_enclosure_entry($row, $id) (byref) + const HOOK_ENCLOSURE_ENTRY = "hook_enclosure_entry"; // hook_enclosure_entry($entry, $id, $rv) (byref) const HOOK_FORMAT_ARTICLE = "hook_format_article"; // hook_format_article($html, $row) const HOOK_FORMAT_ARTICLE_CDM = "hook_format_article_cdm"; /* RIP */ const HOOK_FEED_BASIC_INFO = "hook_feed_basic_info"; // hook_feed_basic_info($basic_info, $fetch_url, $owner_uid, $feed_id, $auth_login, $auth_pass) (byref) diff --git a/js/Article.js b/js/Article.js index d039882ec..5f695561c 100644 --- a/js/Article.js +++ b/js/Article.js @@ -150,12 +150,8 @@ const Article = { ).join("")}`; }, renderEnclosures: function (enclosures) { - - // enclosure list was handled by backend (HOOK_FORMAT_ENCLOSURES) - if (enclosures.formatted) - return enclosures.formatted; - return ` + ${enclosures.formatted} ${enclosures.can_inline ? `
    ${enclosures.entries.map((enc) => { 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]; -- cgit v1.2.3 From 167ed87684e7eb392d2f762d7294acf4b261c5da Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Mon, 22 Feb 2021 11:40:31 +0300 Subject: add launch.json for xdebug --- .vscode/launch.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..d8aee785f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Listen for XDebug", + "type": "php", + "request": "launch", + "pathMappings": { + "/var/www/html/tt-rss": "${workspaceRoot}", + }, + "port": 9000 + }] +} \ No newline at end of file -- cgit v1.2.3 From 043ef3dad68f774598e1b172ecb0f5ea75f112af Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Mon, 22 Feb 2021 13:44:25 +0300 Subject: add chrome configuration for debugging --- .vscode/launch.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index d8aee785f..0e10b4b23 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,7 +1,17 @@ { "version": "0.2.0", "configurations": [ - { + { + "name": "Launch Chrome", + "request": "launch", + "type": "chrome", + "pathMapping": { + "/tt-rss/": "${workspaceFolder}" + }, + "urlFilter": "*/tt-rss/*", + "runtimeExecutable": "chrome.exe", + }, + { "name": "Listen for XDebug", "type": "php", "request": "launch", -- cgit v1.2.3 From be4e7b13403666fc477d4b563ea8c075d0fd2022 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Mon, 22 Feb 2021 14:41:09 +0300 Subject: fix several issues reported by phpstan --- api/index.php | 4 ++-- classes/api.php | 2 +- classes/digest.php | 9 --------- classes/feeds.php | 6 ------ classes/handler/public.php | 10 +++++----- classes/pluginhost.php | 3 ++- classes/pref/prefs.php | 7 ------- include/functions.php | 8 ++++---- include/sanity_check.php | 6 +++--- include/sanity_config.php | 4 ++-- include/sessions.php | 8 ++++---- phpstan.neon | 4 +++- plugins/af_fsckportal/init.php | 5 +---- plugins/af_redditimgur/init.php | 5 ++++- update.php | 4 ++-- utils/regen_config_checks.sh | 4 ++-- 16 files changed, 35 insertions(+), 54 deletions(-) diff --git a/api/index.php b/api/index.php index eb79422f9..1b713d561 100644 --- a/api/index.php +++ b/api/index.php @@ -18,8 +18,8 @@ require_once "functions.php"; require_once "sessions.php"; - ini_set('session.use_cookies', 0); - ini_set("session.gc_maxlifetime", 86400); + ini_set('session.use_cookies', "0"); + ini_set("session.gc_maxlifetime", "86400"); ob_start(); diff --git a/classes/api.php b/classes/api.php index 03eea1927..5677cb908 100755 --- a/classes/api.php +++ b/classes/api.php @@ -292,7 +292,7 @@ class API extends Handler { $sanitize_content = !isset($_REQUEST["sanitize"]) || self::_param_to_bool($_REQUEST["sanitize"]); - if ($article_ids) { + if (count($article_ids) > 0) { $article_qmarks = arr_qmarks($article_ids); diff --git a/classes/digest.php b/classes/digest.php index 77eb92c54..e0c23d705 100644 --- a/classes/digest.php +++ b/classes/digest.php @@ -1,12 +1,6 @@ addBlock('enclosure'); } } else { - $tpl->setVariable('ARTICLE_ENCLOSURE_URL', null, true); - $tpl->setVariable('ARTICLE_ENCLOSURE_TYPE', null, true); - $tpl->setVariable('ARTICLE_ENCLOSURE_LENGTH', null, true); + $tpl->setVariable('ARTICLE_ENCLOSURE_URL', "", true); + $tpl->setVariable('ARTICLE_ENCLOSURE_TYPE', "", true); + $tpl->setVariable('ARTICLE_ENCLOSURE_LENGTH', "", true); } list ($og_image, $og_stream) = Article::_get_image($enclosures, $line['content'], $feed_site_url); @@ -207,8 +207,8 @@ class Handler_Public extends Handler { $article['content'] = Sanitizer::sanitize($line["content"], false, $owner_uid, $feed_site_url, false, $line["id"]); $article['updated'] = date('c', strtotime($line["updated"])); - if ($line['note']) $article['note'] = $line['note']; - if ($article['author']) $article['author'] = $line['author']; + if (!empty($line['note'])) $article['note'] = $line['note']; + if (!empty($line['author'])) $article['author'] = $line['author']; if (count($line["tags"]) > 0) { $article['tags'] = array(); diff --git a/classes/pluginhost.php b/classes/pluginhost.php index a05938111..5121c8491 100755 --- a/classes/pluginhost.php +++ b/classes/pluginhost.php @@ -108,8 +108,9 @@ class PluginHost { return false; } + // needed for compatibility with API 2 (?) function get_dbh() { - return Db::get(); + return false; } function get_pdo(): PDO { diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index a26281fee..adb249dac 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -311,13 +311,6 @@ class Pref_Prefs extends Handler_Protected {
    - -
    - - -
    - -