From 362dbad0b8ff0a29241f4e29556910dfaf7d66e5 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 24 Jan 2016 13:18:48 +0100 Subject: Several position problems, in particular in the global view Multiple small bugs in global and reader views. Related to these old issues: https://github.com/FreshRSS/FreshRSS/issues/634 https://github.com/FreshRSS/FreshRSS/issues/275 --- app/views/helpers/javascript_vars.phtml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'app/views') diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml index adf0783f3..36d6e693d 100644 --- a/app/views/helpers/javascript_vars.phtml +++ b/app/views/helpers/javascript_vars.phtml @@ -4,8 +4,7 @@ $mark = FreshRSS_Context::$user_conf->mark_when; $mail = Minz_Session::param('mail', false); $auto_actualize = Minz_Session::param('actualize_feeds', false); -$hide_posts = (FreshRSS_Context::$user_conf->display_posts || - Minz_Request::param('output') === 'reader'); +$hide_posts = !(FreshRSS_Context::$user_conf->display_posts || Minz_Request::actionName() === 'reader'); $s = FreshRSS_Context::$user_conf->shortcuts; $url_login = Minz_Url::display(array( @@ -19,7 +18,7 @@ $url_logout = Minz_Url::display(array( echo 'var context={', 'auto_remove_article:', FreshRSS_Context::isAutoRemoveAvailable() ? 'true' : 'false', ',', - 'hide_posts:', $hide_posts ? 'false' : 'true', ',', + 'hide_posts:', $hide_posts ? 'true' : 'false', ',', 'display_order:"', Minz_Request::param('order', FreshRSS_Context::$user_conf->sort_order), '",', 'auto_mark_article:', $mark['article'] ? 'true' : 'false', ',', 'auto_mark_site:', $mark['site'] ? 'true' : 'false', ',', @@ -31,7 +30,7 @@ echo 'var context={', 'html5_notif_timeout:', FreshRSS_Context::$user_conf->html5_notif_timeout, ',', 'auth_type:"', FreshRSS_Context::$system_conf->auth_type, '",', 'current_user_mail:', $mail ? ('"' . $mail . '"') : 'null', ',', - 'current_view:"', Minz_Request::param('output', 'normal'), '"', + 'current_view:"', Minz_Request::actionName(), '"', "},\n"; echo 'shortcuts={', -- cgit v1.2.3 From e4a459a6edc40b64cba7845b52f3e90666b2818a Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 16 Feb 2016 23:53:39 +0100 Subject: CSP no inline javascript draft https://github.com/FreshRSS/FreshRSS/issues/1075 --- app/FreshRSS.php | 62 +++++++++++++++++++++++++++++ app/layout/layout.phtml | 6 +-- app/views/helpers/javascript_vars.phtml | 70 --------------------------------- lib/Minz/Session.php | 15 ++++--- p/scripts/main.js | 13 +++++- p/scripts/persona.js | 2 +- 6 files changed, 87 insertions(+), 81 deletions(-) delete mode 100644 app/views/helpers/javascript_vars.phtml (limited to 'app/views') diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 044de9cd4..e0e82457c 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -110,6 +110,68 @@ class FreshRSS extends Minz_FrontController { } } + private static function setJavascriptCookie() { + $mark = FreshRSS_Context::$user_conf->mark_when; + $mail = Minz_Session::param('mail', false); + $s = FreshRSS_Context::$user_conf->shortcuts; + $json = json_encode(array( + 'context' => array( + 'auto_remove_article' => !!FreshRSS_Context::isAutoRemoveAvailable(), + 'hide_posts' => !(FreshRSS_Context::$user_conf->display_posts || Minz_Request::actionName() === 'reader'), + 'display_order' => Minz_Request::param('order', FreshRSS_Context::$user_conf->sort_order), + 'auto_mark_article' => !!$mark['article'], + 'auto_mark_site' => !!$mark['site'], + 'auto_mark_scroll' => !!$mark['scroll'], + 'auto_load_more' => !!FreshRSS_Context::$user_conf->auto_load_more, + 'auto_actualize_feeds' => !!Minz_Session::param('actualize_feeds', false), + 'does_lazyload' => !!FreshRSS_Context::$user_conf->lazyload , + 'sticky_post' => !!FreshRSS_Context::isStickyPostEnabled(), + 'html5_notif_timeout' => FreshRSS_Context::$user_conf->html5_notif_timeout, + 'auth_type' => FreshRSS_Context::$system_conf->auth_type, + 'current_user_mail' => $mail ? ('"' . $mail . '"') : null, + 'current_view' => Minz_Request::actionName(), + ), + 'shortcuts' => array( + 'mark_read' => @$s['mark_read'], + 'mark_favorite' => @$s['mark_favorite'], + 'go_website' => @$s['go_website'], + 'prev_entry' => @$s['prev_entry'], + 'next_entry' => @$s['next_entry'], + 'first_entry' => @$s['first_entry'], + 'last_entry' => @$s['last_entry'], + 'collapse_entry' => @$s['collapse_entry'], + 'load_more' => @$s['load_more'], + 'auto_share' => @$s['auto_share'], + 'focus_search' => @$s['focus_search'], + 'user_filter' => @$s['user_filter'], + 'help' => @$s['help'], + 'close_dropdown' => @$s['close_dropdown'], + ), + 'url' => array( + 'index' => _url('index', 'index'), + 'login' => Minz_Url::display(array('c' => 'auth', 'a' => 'login'), 'php'), + 'logout' => Minz_Url::display(array('c' => 'auth', 'a' => 'logout'), 'php'), + 'help' => FRESHRSS_WIKI, + ), + 'i18n' => array( + 'confirmation_default' => _t('gen.js.confirm_action'), + 'notif_title_articles' => _t('gen.js.feedback.title_new_articles'), + 'notif_body_articles' => _t('gen.js.feedback.body_new_articles'), + 'notif_request_failed' => _t('gen.js.feedback.request_failed'), + 'category_empty' => _t('gen.js.category_empty'), + ), + 'icons' => array( + 'close' => _i('close'), + ), + ), JSON_UNESCAPED_UNICODE); + setrawcookie('FreshRSS-vars', rawurlencode($json), 0, Minz_Session::getCookieDir()); + } + + public static function preLayout() { + header("Content-Security-Policy: default-src 'self'; img-src * data:; media-src *; style-src 'self' 'unsafe-inline'"); + self::setJavascriptCookie(); + } + private function loadNotifications() { $notif = Minz_Session::param('notification'); if ($notif) { diff --git a/app/layout/layout.phtml b/app/layout/layout.phtml index 1d3afbf71..7d718efbd 100644 --- a/app/layout/layout.phtml +++ b/app/layout/layout.phtml @@ -1,3 +1,6 @@ + @@ -5,9 +8,6 @@ - mark_when; -$mail = Minz_Session::param('mail', false); -$auto_actualize = Minz_Session::param('actualize_feeds', false); -$hide_posts = !(FreshRSS_Context::$user_conf->display_posts || Minz_Request::actionName() === 'reader'); -$s = FreshRSS_Context::$user_conf->shortcuts; - -$url_login = Minz_Url::display(array( - 'c' => 'auth', - 'a' => 'login' -), 'php'); -$url_logout = Minz_Url::display(array( - 'c' => 'auth', - 'a' => 'logout' -), 'php'); - -echo 'var context={', - 'auto_remove_article:', FreshRSS_Context::isAutoRemoveAvailable() ? 'true' : 'false', ',', - 'hide_posts:', $hide_posts ? 'true' : 'false', ',', - 'display_order:"', Minz_Request::param('order', FreshRSS_Context::$user_conf->sort_order), '",', - 'auto_mark_article:', $mark['article'] ? 'true' : 'false', ',', - 'auto_mark_site:', $mark['site'] ? 'true' : 'false', ',', - 'auto_mark_scroll:', $mark['scroll'] ? 'true' : 'false', ',', - 'auto_load_more:', FreshRSS_Context::$user_conf->auto_load_more ? 'true' : 'false', ',', - 'auto_actualize_feeds:', $auto_actualize ? 'true' : 'false', ',', - 'does_lazyload:', FreshRSS_Context::$user_conf->lazyload ? 'true' : 'false', ',', - 'sticky_post:', FreshRSS_Context::isStickyPostEnabled() ? 'true' : 'false', ',', - 'html5_notif_timeout:', FreshRSS_Context::$user_conf->html5_notif_timeout, ',', - 'auth_type:"', FreshRSS_Context::$system_conf->auth_type, '",', - 'current_user_mail:', $mail ? ('"' . $mail . '"') : 'null', ',', - 'current_view:"', Minz_Request::actionName(), '"', -"},\n"; - -echo 'shortcuts={', - 'mark_read:"', @$s['mark_read'], '",', - 'mark_favorite:"', @$s['mark_favorite'], '",', - 'go_website:"', @$s['go_website'], '",', - 'prev_entry:"', @$s['prev_entry'], '",', - 'next_entry:"', @$s['next_entry'], '",', - 'first_entry:"', @$s['first_entry'], '",', - 'last_entry:"', @$s['last_entry'], '",', - 'collapse_entry:"', @$s['collapse_entry'], '",', - 'load_more:"', @$s['load_more'], '",', - 'auto_share:"', @$s['auto_share'], '",', - 'focus_search:"', @$s['focus_search'], '",', - 'user_filter:"', @$s['user_filter'], '",', - 'help:"', @$s['help'], '",', - 'close_dropdown:"', @$s['close_dropdown'], '"', -"},\n"; - -echo 'url={', - 'index:"', _url('index', 'index'), '",', - 'login:"', $url_login, '",', - 'logout:"', $url_logout, '",', - 'help:"', FRESHRSS_WIKI, '"', -"},\n"; - -echo 'i18n={', - 'confirmation_default:"', _t('gen.js.confirm_action'), '",', - 'notif_title_articles:"', _t('gen.js.feedback.title_new_articles'), '",', - 'notif_body_articles:"', _t('gen.js.feedback.body_new_articles'), '",', - 'notif_request_failed:"', _t('gen.js.feedback.request_failed'), '",', - 'category_empty:"', _t('gen.js.category_empty'), '"', -"},\n"; - -echo 'icons={', - 'close:\'', _i('close'), '\'', -"}\n"; \ No newline at end of file diff --git a/lib/Minz/Session.php b/lib/Minz/Session.php index 057e7746a..940cd27d9 100644 --- a/lib/Minz/Session.php +++ b/lib/Minz/Session.php @@ -59,18 +59,21 @@ class Minz_Session { } } + public static function getCookieDir() { + // Get the script_name (e.g. /p/i/index.php) and keep only the path. + $cookie_dir = empty($_SERVER['REQUEST_URI']) ? '/' : $_SERVER['REQUEST_URI']; + if (substr($cookie_dir, -1) !== '/') { + $cookie_dir = dirname($cookie_dir) . '/'; + } + return $cookie_dir; + } /** * Spécifie la durée de vie des cookies * @param $l la durée de vie */ public static function keepCookie($l) { - // Get the script_name (e.g. /p/i/index.php) and keep only the path. - $cookie_dir = empty($_SERVER['REQUEST_URI']) ? '/' : $_SERVER['REQUEST_URI']; - if (substr($cookie_dir, -1) !== '/') { - $cookie_dir = dirname($cookie_dir) . '/'; - } - session_set_cookie_params($l, $cookie_dir, '', false, true); + session_set_cookie_params($l, self::getCookieDir(), '', false, true); } diff --git a/p/scripts/main.js b/p/scripts/main.js index 968c945c8..32a2ca913 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -1245,8 +1245,19 @@ function init_configuration_alert() { }); } +function parseJavaScriptCookie() { + var json = JSON.parse(decodeURIComponent(document.cookie.replace(/(?:(?:^|.*;\s*)FreshRSS-vars\s*\=\s*([^;]*).*$)|^.*$/, "$1"))) || {}; + document.cookie = 'FreshRSS-vars=; expires=Thu, 01 Jan 1970 00:00:00 GMT'; + window.context = json.context; + window.shortcuts = json.shortcuts; + window.url = json.url; + window.i18n = json.i18n; + window.icons = json.icons; +} + function init_all() { - if (!(window.$ && window.context)) { + parseJavaScriptCookie(); + if (!window.$) { if (window.console) { console.log('FreshRSS waiting for JS…'); } diff --git a/p/scripts/persona.js b/p/scripts/persona.js index 36aeeaf56..63ab43795 100644 --- a/p/scripts/persona.js +++ b/p/scripts/persona.js @@ -1,7 +1,7 @@ "use strict"; function init_persona() { - if (!(navigator.id && window.$)) { + if (!(navigator.id && window.$ && window.url)) { if (window.console) { console.log('FreshRSS (Persona) waiting for JS…'); } -- cgit v1.2.3 From e3dc7d46e15d97f8bd008acf3489d5e6c22b8daa Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 21 Feb 2016 14:23:24 +0100 Subject: CSP: Use inline JSON instead of one-time cookie Simpler, lighter https://github.com/FreshRSS/FreshRSS/issues/1075 --- app/FreshRSS.php | 58 --------------------------------- app/layout/layout.phtml | 3 ++ app/views/helpers/javascript_vars.phtml | 54 ++++++++++++++++++++++++++++++ p/scripts/main.js | 10 +++--- 4 files changed, 62 insertions(+), 63 deletions(-) create mode 100644 app/views/helpers/javascript_vars.phtml (limited to 'app/views') diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 8eb862aeb..a6ed2a306 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -110,66 +110,8 @@ class FreshRSS extends Minz_FrontController { } } - private static function setJavascriptCookie() { - $mark = FreshRSS_Context::$user_conf->mark_when; - $mail = Minz_Session::param('mail', false); - $s = FreshRSS_Context::$user_conf->shortcuts; - $json = json_encode(array( - 'context' => array( - 'auto_remove_article' => !!FreshRSS_Context::isAutoRemoveAvailable(), - 'hide_posts' => !(FreshRSS_Context::$user_conf->display_posts || Minz_Request::actionName() === 'reader'), - 'display_order' => Minz_Request::param('order', FreshRSS_Context::$user_conf->sort_order), - 'auto_mark_article' => !!$mark['article'], - 'auto_mark_site' => !!$mark['site'], - 'auto_mark_scroll' => !!$mark['scroll'], - 'auto_load_more' => !!FreshRSS_Context::$user_conf->auto_load_more, - 'auto_actualize_feeds' => !!Minz_Session::param('actualize_feeds', false), - 'does_lazyload' => !!FreshRSS_Context::$user_conf->lazyload , - 'sticky_post' => !!FreshRSS_Context::isStickyPostEnabled(), - 'html5_notif_timeout' => FreshRSS_Context::$user_conf->html5_notif_timeout, - 'auth_type' => FreshRSS_Context::$system_conf->auth_type, - 'current_user_mail' => $mail ? ('"' . $mail . '"') : null, - 'current_view' => Minz_Request::actionName(), - ), - 'shortcuts' => array( - 'mark_read' => @$s['mark_read'], - 'mark_favorite' => @$s['mark_favorite'], - 'go_website' => @$s['go_website'], - 'prev_entry' => @$s['prev_entry'], - 'next_entry' => @$s['next_entry'], - 'first_entry' => @$s['first_entry'], - 'last_entry' => @$s['last_entry'], - 'collapse_entry' => @$s['collapse_entry'], - 'load_more' => @$s['load_more'], - 'auto_share' => @$s['auto_share'], - 'focus_search' => @$s['focus_search'], - 'user_filter' => @$s['user_filter'], - 'help' => @$s['help'], - 'close_dropdown' => @$s['close_dropdown'], - ), - 'url' => array( - 'index' => _url('index', 'index'), - 'login' => Minz_Url::display(array('c' => 'auth', 'a' => 'login'), 'php'), - 'logout' => Minz_Url::display(array('c' => 'auth', 'a' => 'logout'), 'php'), - 'help' => FRESHRSS_WIKI, - ), - 'i18n' => array( - 'confirmation_default' => _t('gen.js.confirm_action'), - 'notif_title_articles' => _t('gen.js.feedback.title_new_articles'), - 'notif_body_articles' => _t('gen.js.feedback.body_new_articles'), - 'notif_request_failed' => _t('gen.js.feedback.request_failed'), - 'category_empty' => _t('gen.js.category_empty'), - ), - 'icons' => array( - 'close' => _i('close'), - ), - ), JSON_UNESCAPED_UNICODE); - setrawcookie('FreshRSS-vars', rawurlencode($json), 0, Minz_Session::getCookieDir()); - } - public static function preLayout() { header("Content-Security-Policy: default-src 'self'; child-src *; img-src * data:; media-src *; style-src 'self' 'unsafe-inline'"); - self::setJavascriptCookie(); } private function loadNotifications() { diff --git a/app/layout/layout.phtml b/app/layout/layout.phtml index 7d718efbd..99a3717bc 100644 --- a/app/layout/layout.phtml +++ b/app/layout/layout.phtml @@ -8,6 +8,9 @@ + mark_when; +$mail = Minz_Session::param('mail', false); +$s = FreshRSS_Context::$user_conf->shortcuts; +echo htmlspecialchars(json_encode(array( + 'context' => array( + 'auto_remove_article' => !!FreshRSS_Context::isAutoRemoveAvailable(), + 'hide_posts' => !(FreshRSS_Context::$user_conf->display_posts || Minz_Request::actionName() === 'reader'), + 'display_order' => Minz_Request::param('order', FreshRSS_Context::$user_conf->sort_order), + 'auto_mark_article' => !!$mark['article'], + 'auto_mark_site' => !!$mark['site'], + 'auto_mark_scroll' => !!$mark['scroll'], + 'auto_load_more' => !!FreshRSS_Context::$user_conf->auto_load_more, + 'auto_actualize_feeds' => !!Minz_Session::param('actualize_feeds', false), + 'does_lazyload' => !!FreshRSS_Context::$user_conf->lazyload , + 'sticky_post' => !!FreshRSS_Context::isStickyPostEnabled(), + 'html5_notif_timeout' => FreshRSS_Context::$user_conf->html5_notif_timeout, + 'auth_type' => FreshRSS_Context::$system_conf->auth_type, + 'current_user_mail' => $mail ? ('"' . $mail . '"') : null, + 'current_view' => Minz_Request::actionName(), + ), + 'shortcuts' => array( + 'mark_read' => @$s['mark_read'], + 'mark_favorite' => @$s['mark_favorite'], + 'go_website' => @$s['go_website'], + 'prev_entry' => @$s['prev_entry'], + 'next_entry' => @$s['next_entry'], + 'first_entry' => @$s['first_entry'], + 'last_entry' => @$s['last_entry'], + 'collapse_entry' => @$s['collapse_entry'], + 'load_more' => @$s['load_more'], + 'auto_share' => @$s['auto_share'], + 'focus_search' => @$s['focus_search'], + 'user_filter' => @$s['user_filter'], + 'help' => @$s['help'], + 'close_dropdown' => @$s['close_dropdown'], + ), + 'url' => array( + 'index' => _url('index', 'index'), + 'login' => Minz_Url::display(array('c' => 'auth', 'a' => 'login'), 'php'), + 'logout' => Minz_Url::display(array('c' => 'auth', 'a' => 'logout'), 'php'), + 'help' => FRESHRSS_WIKI, + ), + 'i18n' => array( + 'confirmation_default' => _t('gen.js.confirm_action'), + 'notif_title_articles' => _t('gen.js.feedback.title_new_articles'), + 'notif_body_articles' => _t('gen.js.feedback.body_new_articles'), + 'notif_request_failed' => _t('gen.js.feedback.request_failed'), + 'category_empty' => _t('gen.js.category_empty'), + ), + 'icons' => array( + 'close' => _i('close'), + ), +), JSON_UNESCAPED_UNICODE), ENT_NOQUOTES); diff --git a/p/scripts/main.js b/p/scripts/main.js index 31b07721a..f07cdafd7 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -1245,10 +1245,10 @@ function init_configuration_alert() { }); } -function parseJavaScriptCookie() { - var vars = decodeURIComponent(document.cookie.replace(/(?:(?:^|.*;\s*)FreshRSS-vars\s*\=\s*([^;]*).*$)|^.*$/, "$1")); - document.cookie = 'FreshRSS-vars=; expires=Thu, 01 Jan 1970 00:00:00 GMT'; - var json = JSON.parse(vars); +function parseJsonVars() { + var jsonVars = document.getElementById('jsonVars'), + json = JSON.parse(jsonVars.innerHTML); + jsonVars.outerHTML = ''; window.context = json.context; window.shortcuts = json.shortcuts; window.url = json.url; @@ -1264,7 +1264,7 @@ function init_all() { window.setTimeout(init_all, 50); return; } - parseJavaScriptCookie(); + parseJsonVars(); init_notifications(); init_confirm_action(); $stream = $('#stream'); -- cgit v1.2.3 From 264d05297c72e87b114a8e930db7eae7affe5690 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 21 Feb 2016 17:26:37 +0100 Subject: CSP for statistics https://github.com/FreshRSS/FreshRSS/issues/1075 --- app/Models/StatsDAO.php | 20 ++++---- app/Models/StatsDAOSQLite.php | 4 +- app/views/stats/index.phtml | 64 ++++------------------- app/views/stats/repartition.phtml | 103 ++++++++------------------------------ p/scripts/repartition.js | 72 ++++++++++++++++++++++++++ p/scripts/stats.js | 56 +++++++++++++++++++++ 6 files changed, 169 insertions(+), 150 deletions(-) create mode 100644 p/scripts/repartition.js create mode 100644 p/scripts/stats.js (limited to 'app/views') diff --git a/app/Models/StatsDAO.php b/app/Models/StatsDAO.php index 80caccc49..5ca333396 100644 --- a/app/Models/StatsDAO.php +++ b/app/Models/StatsDAO.php @@ -55,9 +55,9 @@ SQL; /** * Calculates entry count per day on a 30 days period. - * Returns the result as a JSON string. + * Returns the result as a JSON object. * - * @return string + * @return JSON object */ public function calculateEntryCount() { $count = $this->initEntryCountArray(); @@ -257,9 +257,9 @@ SQL; /** * Calculates feed count per category. - * Returns the result as a JSON string. + * Returns the result as a JSON object. * - * @return string + * @return JSON object */ public function calculateFeedByCategory() { $sql = <<initEntryCountArray(); diff --git a/app/views/stats/index.phtml b/app/views/stats/index.phtml index 18bcd4d99..c11b88999 100644 --- a/app/views/stats/index.phtml +++ b/app/views/stats/index.phtml @@ -82,58 +82,12 @@ - + + diff --git a/app/views/stats/repartition.phtml b/app/views/stats/repartition.phtml index b20d9bbd0..980b26a3d 100644 --- a/app/views/stats/repartition.phtml +++ b/app/views/stats/repartition.phtml @@ -30,20 +30,20 @@
- +
- - - - + + + + - - - - + + + + -
repartition['total']; ?>repartition['read']; ?>repartition['unread']; ?>repartition['favorite']; ?>repartition['total']; ?>repartition['read']; ?>repartition['unread']; ?>repartition['favorite']; ?>
+
@@ -62,76 +62,13 @@
- + + diff --git a/p/scripts/repartition.js b/p/scripts/repartition.js new file mode 100644 index 000000000..a391de2f2 --- /dev/null +++ b/p/scripts/repartition.js @@ -0,0 +1,72 @@ +"use strict"; +function initStats() { + if (!window.Flotr) { + if (window.console) { + console.log('FreshRSS waiting for Flotr…'); + } + window.setTimeout(initStats, 50); + return; + } + var jsonRepartition = document.getElementById('jsonRepartition'), + stats = JSON.parse(jsonRepartition.innerHTML); + jsonRepartition.outerHTML = ''; + // Entry per hour + Flotr.draw(document.getElementById('statsEntryPerHour'), + [{ + data: stats.repartitionHour, + bars: {horizontal: false, show: true} + }], + { + grid: {verticalLines: false}, + xaxis: {noTicks: 23, + tickFormatter: function(x) { + var x = parseInt(x); + return x + 1; + }, + min: -0.9, + max: 23.9, + tickDecimals: 0}, + yaxis: {min: 0}, + mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}} + }); + // Entry per day of week + Flotr.draw(document.getElementById('statsEntryPerDayOfWeek'), + [{ + data: stats.repartitionDayOfWeek, + bars: {horizontal: false, show: true} + }], + { + grid: {verticalLines: false}, + xaxis: {noTicks: 6, + tickFormatter: function(x) { + var x = parseInt(x); + return stats.days[x]; + }, + min: -0.9, + max: 6.9, + tickDecimals: 0}, + yaxis: {min: 0}, + mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}} + }); + // Entry per month + Flotr.draw(document.getElementById('statsEntryPerMonth'), + [{ + data: stats.repartitionMonth, + bars: {horizontal: false, show: true} + }], + { + grid: {verticalLines: false}, + xaxis: {noTicks: 12, + tickFormatter: function(x) { + var x = parseInt(x); + return stats.months[(x - 1)]; + }, + min: 0.1, + max: 12.9, + tickDecimals: 0}, + yaxis: {min: 0}, + mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}} + }); + +} +initStats(); diff --git a/p/scripts/stats.js b/p/scripts/stats.js new file mode 100644 index 000000000..2e8ab6e27 --- /dev/null +++ b/p/scripts/stats.js @@ -0,0 +1,56 @@ +"use strict"; +function initStats() { + if (!window.Flotr) { + if (window.console) { + console.log('FreshRSS waiting for Flotr…'); + } + window.setTimeout(initStats, 50); + return; + } + var jsonStats = document.getElementById('jsonStats'), + stats = JSON.parse(jsonStats.innerHTML); + jsonStats.outerHTML = ''; + // Entry per day + var avg = []; + for (var i = -31; i <= 0; i++) { + avg.push([i, stats.average]); + } + Flotr.draw(document.getElementById('statsEntryPerDay'), + [{ + data: stats.dataCount, + bars: {horizontal: false, show: true} + },{ + data: avg, + lines: {show: true}, + label: stats.average, + }], + { + grid: {verticalLines: false}, + xaxis: {noTicks: 6, showLabels: false, tickDecimals: 0, min: -30.75, max: -0.25}, + yaxis: {min: 0}, + mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}} + }); + // Feed per category + Flotr.draw(document.getElementById('statsFeedPerCategory'), + stats.feedByCategory, + { + grid: {verticalLines: false, horizontalLines: false}, + pie: {explode: 10, show: true, labelFormatter: function(){return '';}}, + xaxis: {showLabels: false}, + yaxis: {showLabels: false}, + mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return obj.series.label + ' - '+ numberFormat(obj.y) + ' ('+ (obj.fraction * 100).toFixed(1) + '%)';}}, + legend: {container: document.getElementById('statsFeedPerCategoryLegend'), noColumns: 3} + }); + // Entry per category + Flotr.draw(document.getElementById('statsEntryPerCategory'), + stats.entryByCategory, + { + grid: {verticalLines: false, horizontalLines: false}, + pie: {explode: 10, show: true, labelFormatter: function(){return '';}}, + xaxis: {showLabels: false}, + yaxis: {showLabels: false}, + mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return obj.series.label + ' - '+ numberFormat(obj.y) + ' ('+ (obj.fraction * 100).toFixed(1) + '%)';}}, + legend: {container: document.getElementById('statsEntryPerCategoryLegend'), noColumns: 3} + }); +} +initStats(); -- cgit v1.2.3 From cb913a3a76daf357ad36ca39c26b4aaf800211d2 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 21 Feb 2016 19:15:23 +0100 Subject: CSP for statistics and forms https://github.com/FreshRSS/FreshRSS/issues/1075 --- CHANGELOG.md | 2 ++ app/FreshRSS.php | 6 +++++- app/layout/aside_feed.phtml | 2 +- app/layout/nav_menu.phtml | 2 +- app/views/extension/index.phtml | 2 +- app/views/feed/add.phtml | 2 +- app/views/helpers/pagination.phtml | 2 +- app/views/stats/idle.phtml | 2 +- app/views/stats/index.phtml | 10 +++++----- app/views/stats/repartition.phtml | 10 +++++----- app/views/subscription/index.phtml | 4 ++-- p/scripts/main.js | 6 +++--- p/themes/base-theme/template.css | 8 ++++++++ 13 files changed, 36 insertions(+), 22 deletions(-) (limited to 'app/views') diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f96839eb..7fc872040 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## 2016-xx-xx FreshRSS 1.3.1-beta +* Security + * Added CSP `Content-Security-Policy: default-src 'self'; child-src *; img-src * data:; media-src *` [#1075](https://github.com/FreshRSS/FreshRSS/pull/1075) * UI * Fixed several small bugs in global and reader view [#1050](https://github.com/FreshRSS/FreshRSS/pull/1050) * Updated to jQuery 2.2 and changed code for auto-load on scroll [#1050](https://github.com/FreshRSS/FreshRSS/pull/1050) diff --git a/app/FreshRSS.php b/app/FreshRSS.php index a6ed2a306..62ea18d96 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -111,7 +111,11 @@ class FreshRSS extends Minz_FrontController { } public static function preLayout() { - header("Content-Security-Policy: default-src 'self'; child-src *; img-src * data:; media-src *; style-src 'self' 'unsafe-inline'"); + if (Minz_Request::controllerName() === 'stats') { + header("Content-Security-Policy: default-src 'self'; style-src 'self' 'unsafe-inline'"); + } else { + header("Content-Security-Policy: default-src 'self'; child-src *; img-src * data:; media-src *"); + } } private function loadNotifications() { diff --git a/app/layout/aside_feed.phtml b/app/layout/aside_feed.phtml index 307db6af8..4e1903a7a 100644 --- a/app/layout/aside_feed.phtml +++ b/app/layout/aside_feed.phtml @@ -19,7 +19,7 @@ - +
  • diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index 3a755b560..0f303beb8 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -79,7 +79,7 @@ ); ?> - + diff --git a/app/views/helpers/pagination.phtml b/app/views/helpers/pagination.phtml index b20201c4b..7eca8c525 100755 --- a/app/views/helpers/pagination.phtml +++ b/app/views/helpers/pagination.phtml @@ -14,7 +14,7 @@ ); ?> - +
    • diff --git a/app/views/stats/idle.phtml b/app/views/stats/idle.phtml index 22117792d..11b7df8c4 100644 --- a/app/views/stats/idle.phtml +++ b/app/views/stats/idle.phtml @@ -18,7 +18,7 @@

      - +
        diff --git a/app/views/stats/index.phtml b/app/views/stats/index.phtml index c11b88999..0a2fbdb10 100644 --- a/app/views/stats/index.phtml +++ b/app/views/stats/index.phtml @@ -66,18 +66,18 @@

        -
        +

        -
        +
        -
        +

        -
        +
        diff --git a/app/views/stats/repartition.phtml b/app/views/stats/repartition.phtml index 980b26a3d..ffb2c361e 100644 --- a/app/views/stats/repartition.phtml +++ b/app/views/stats/repartition.phtml @@ -48,17 +48,17 @@

        averageHour); ?>

        -
        +

        averageDayOfWeek); ?>

        -
        -
        +

        averageMonth); ?>

        -
        +
        diff --git a/app/views/subscription/index.phtml b/app/views/subscription/index.phtml index 2cfe3f33c..07cebf817 100644 --- a/app/views/subscription/index.phtml +++ b/app/views/subscription/index.phtml @@ -28,7 +28,7 @@ - @@ -62,7 +62,7 @@
      - + categories as $cat) { diff --git a/p/scripts/main.js b/p/scripts/main.js index cfde5fd4e..d62a6aff8 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -899,7 +899,7 @@ function refreshUnreads() { if ((incUnreadsFeed(null, feed_id, nbUnreads - feed_unreads) || isAll) && //Update of current view? (nbUnreads - feed_unreads > 0)) { - $('#new-article').show(); + $('#new-article').attr('aria-hidden', 'false').show(); new_articles = true; }; }); @@ -1122,10 +1122,10 @@ function init_feed_observers() { $('select[id="category"]').on('change', function() { var detail = $('#new_category_name').parent(); if ($(this).val() === 'nc') { - detail.show(); + detail.attr('aria-hidden', 'false').show(); detail.find('input').focus(); } else { - detail.hide(); + detail.attr('aria-hidden', 'true').hide(); } }); } diff --git a/p/themes/base-theme/template.css b/p/themes/base-theme/template.css index 17a43d3ed..8a12423be 100644 --- a/p/themes/base-theme/template.css +++ b/p/themes/base-theme/template.css @@ -110,6 +110,11 @@ td.numeric { /*=== COMPONENTS */ /*===============*/ + +[aria-hidden="true"] { + display: none; +} + /*=== Forms */ .form-group::after { content: ""; @@ -620,6 +625,9 @@ br + br + br { .stat > table { width: 100%; } +.statGraph { + height: 300px; +} /*=== GLOBAL VIEW */ /*================*/ -- cgit v1.2.3 From c9d3d78340e062b9e2fe19c8c55b8bdc75392e63 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 27 Feb 2016 17:51:13 +0100 Subject: CSP manually refreshing feeds https://github.com/FreshRSS/FreshRSS/issues/1075 --- app/Controllers/javascriptController.php | 2 +- app/views/javascript/actualize.phtml | 69 ++++++-------------------------- p/scripts/main.js | 44 +++++++++++++++++--- 3 files changed, 53 insertions(+), 62 deletions(-) (limited to 'app/views') diff --git a/app/Controllers/javascriptController.php b/app/Controllers/javascriptController.php index e3ae3669e..00a7b5c38 100755 --- a/app/Controllers/javascriptController.php +++ b/app/Controllers/javascriptController.php @@ -6,7 +6,7 @@ class FreshRSS_javascript_Controller extends Minz_ActionController { } public function actualizeAction() { - header('Content-Type: text/javascript; charset=UTF-8'); + header('Content-Type: application/json; charset=UTF-8'); $feedDAO = FreshRSS_Factory::createFeedDao(); $this->view->feeds = $feedDAO->listFeedsOrderUpdate(FreshRSS_Context::$user_conf->ttl_default); } diff --git a/app/views/javascript/actualize.phtml b/app/views/javascript/actualize.phtml index 454228909..3baabf748 100644 --- a/app/views/javascript/actualize.phtml +++ b/app/views/javascript/actualize.phtml @@ -1,56 +1,13 @@ -"use strict"; -var feeds = [feeds as $feed) { ?>{url: " 'feed', 'a' => 'actualize', 'params' => array('id' => $feed->id(), 'ajax' => '1')), 'php'); ?>",title: "name(); ?>"},], - feed_processed = 0, - feed_count = feeds.length; - -function initProgressBar(init) { - if (init) { - $("body").after("\
      \ -
      /
      \ - 0 / " + feed_count + "\ -
      "); - } else { - window.location.reload(); - } -} -function updateProgressBar(i, title_feed) { - $("#actualizeProgress .progress").html(i + " / " + feed_count); - $("#actualizeProgress .title").html(title_feed); -} - -function updateFeeds() { - if (feed_count === 0) { - openNotification("", "good"); - ajax_loading = false; - return; - } - initProgressBar(true); - - for (var i = 0; i < 10; i++) { - updateFeed(); - } -} - -function updateFeed() { - var feed = feeds.pop(); - if (feed == undefined) { - return; - } - - $.ajax({ - type: 'POST', - url: feed['url'], - }).complete(function (data) { - feed_processed++; - updateProgressBar(feed_processed, feed['title']); - - if (feed_processed === feed_count) { - initProgressBar(false); - } else { - updateFeed(); - } - }); -} +feeds as $feed) { + $feeds[] = array( + 'url' => Minz_Url::display(array('c' => 'feed', 'a' => 'actualize', 'params' => array('id' => $feed->id(), 'ajax' => '1')), 'php'), + 'title' => $feed->name(), + ); +} +echo json_encode(array( + 'feeds' => $feeds, + 'feedback_no_refresh' => _t('feedback.sub.feed.no_refresh'), + 'feedback_actualize' => _t('feedback.sub.actualize'), +)); diff --git a/p/scripts/main.js b/p/scripts/main.js index d62a6aff8..51c8f4cbf 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -767,6 +767,31 @@ function init_nav_entries() { }); } +// +var feed_processed = 0; + +function updateFeed(feeds, feeds_count) { + var feed = feeds.pop(); + if (feed == undefined) { + return; + } + + $.ajax({ + type: 'POST', + url: feed['url'], + }).complete(function (data) { + feed_processed++; + $("#actualizeProgress .progress").html(feed_processed + " / " + feeds_count); + $("#actualizeProgress .title").html(feed['title']); + + if (feed_processed === feeds_count) { + window.location.reload(); + } else { + updateFeed(feeds, feeds_count); + } + }); +} + function init_actualize() { var auto = false; @@ -777,14 +802,23 @@ function init_actualize() { ajax_loading = true; - $.getScript('./?c=javascript&a=actualize').done(function () { - if (auto && feed_count < 1) { + $.getJSON('./?c=javascript&a=actualize').done(function (data) { + if (auto && data.feeds.length < 1) { auto = false; ajax_loading = false; return false; } - - updateFeeds(); + if (data.feeds.length === 0) { + openNotification(data.feedback_no_refresh, "good"); + ajax_loading = false; + return; + } + //Progress bar + var feeds_count = data.feeds.length; + $('body').after('
      ' + data.feedback_actualize + '
      /
      0 / ' + feeds_count + '
      '); + for (var i = 10; i > 0; i--) { + updateFeed(data.feeds, feeds_count); + } }); return false; @@ -795,7 +829,7 @@ function init_actualize() { $("#actualize").click(); } } - +//
      // var notification = null, -- cgit v1.2.3 From 5f04462e554e4604c04c2c9ca2fe97df6f6f6376 Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Sun, 6 Mar 2016 12:51:39 +0100 Subject: Fix redirection while deleting a feed Before, when deleting a feed from the statistics idle page, there was an error in the redirection process. Now, the redirection works properly and redirects to the idle page. --- app/views/stats/idle.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app/views') diff --git a/app/views/stats/idle.phtml b/app/views/stats/idle.phtml index 11b7df8c4..ba88b679f 100644 --- a/app/views/stats/idle.phtml +++ b/app/views/stats/idle.phtml @@ -6,10 +6,10 @@

      'stats', 'a' => 'idle'), 'php', true - )); + ); $nothing = true; foreach ($this->idleFeeds as $period => $feeds) { if (!empty($feeds)) { -- cgit v1.2.3 From 6466fc87ece4c1fc6ee8ea5ea4d751d7ae266a2e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 29 Apr 2016 18:24:46 +0200 Subject: Load styles for two JSON cases --- app/FreshRSS.php | 2 +- app/views/entry/bookmark.phtml | 1 + app/views/entry/read.phtml | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) (limited to 'app/views') diff --git a/app/FreshRSS.php b/app/FreshRSS.php index dd62c1919..4933892bc 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -77,7 +77,7 @@ class FreshRSS extends Minz_FrontController { Minz_Translate::init(FreshRSS_Context::$user_conf->language); } - private static function loadStylesAndScripts() { + public static function loadStylesAndScripts() { $theme = FreshRSS_Themes::load(FreshRSS_Context::$user_conf->theme); if ($theme) { foreach($theme['files'] as $file) { diff --git a/app/views/entry/bookmark.phtml b/app/views/entry/bookmark.phtml index c346d2c4c..6b5a4ed03 100755 --- a/app/views/entry/bookmark.phtml +++ b/app/views/entry/bookmark.phtml @@ -13,4 +13,5 @@ $url = Minz_Url::display(array( 'params' => Minz_Request::params(), )); +FreshRSS::loadStylesAndScripts(); echo json_encode(array('url' => str_ireplace('&', '&', $url), 'icon' => _i(Minz_Request::param('is_favorite') ? 'non-starred' : 'starred'))); diff --git a/app/views/entry/read.phtml b/app/views/entry/read.phtml index fabdec9e0..7d0e3de82 100755 --- a/app/views/entry/read.phtml +++ b/app/views/entry/read.phtml @@ -13,4 +13,5 @@ $url = Minz_Url::display(array( 'params' => Minz_Request::params(), )); +FreshRSS::loadStylesAndScripts(); echo json_encode(array('url' => str_ireplace('&', '&', $url), 'icon' => _i(Minz_Request::param('is_read') ? 'unread' : 'read'))); -- cgit v1.2.3 From c5524224454b6828e099e5d44383b0d2f316ceb8 Mon Sep 17 00:00:00 2001 From: Frans de Jonge Date: Sun, 1 May 2016 13:19:54 +0200 Subject: Fix validator link --- app/views/helpers/feed/update.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/views') diff --git a/app/views/helpers/feed/update.phtml b/app/views/helpers/feed/update.phtml index 12f485ec3..72084d8fa 100644 --- a/app/views/helpers/feed/update.phtml +++ b/app/views/helpers/feed/update.phtml @@ -48,7 +48,7 @@ - +
      -- cgit v1.2.3 From ea4deb6e0568adca6c0f1bea536fac6869f9c7ec Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 12 Jun 2016 13:18:31 +0200 Subject: Check minimum PHP 5.3.0+ https://github.com/FreshRSS/FreshRSS/pull/1133 --- app/install.php | 4 ++-- app/views/update/checkInstall.phtml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'app/views') diff --git a/app/install.php b/app/install.php index b47effc84..062f66814 100644 --- a/app/install.php +++ b/app/install.php @@ -309,7 +309,7 @@ function checkStep0() { } function checkStep1() { - $php = version_compare(PHP_VERSION, '5.2.1') >= 0; + $php = version_compare(PHP_VERSION, '5.3.0') >= 0; $minz = file_exists(join_path(LIB_PATH, 'Minz')); $curl = extension_loaded('curl'); $pdo_mysql = extension_loaded('pdo_mysql'); @@ -536,7 +536,7 @@ function printStep1() {

      -

      +

      diff --git a/app/views/update/checkInstall.phtml b/app/views/update/checkInstall.phtml index a92860c7e..ed3858b56 100644 --- a/app/views/update/checkInstall.phtml +++ b/app/views/update/checkInstall.phtml @@ -9,7 +9,7 @@