From 937cb4b066f07888dabe8400b71be6633a19a1d6 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 22 Jul 2014 13:41:31 +0200 Subject: Idle feeds: link to configuration page https://github.com/marienfressinaud/FreshRSS/issues/544 --- app/views/stats/idle.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/views') diff --git a/app/views/stats/idle.phtml b/app/views/stats/idle.phtml index 356fea20f..f62fa8d8b 100644 --- a/app/views/stats/idle.phtml +++ b/app/views/stats/idle.phtml @@ -11,7 +11,7 @@ -- cgit v1.2.3 From 72293427ac222dba03e88b2e34abc41e12a657c5 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 22 Jul 2014 13:56:01 +0200 Subject: Idle feeds: show tooltip with last update date https://github.com/marienfressinaud/FreshRSS/issues/544 --- app/views/stats/idle.phtml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'app/views') diff --git a/app/views/stats/idle.phtml b/app/views/stats/idle.phtml index f62fa8d8b..a167fcccb 100644 --- a/app/views/stats/idle.phtml +++ b/app/views/stats/idle.phtml @@ -1,17 +1,17 @@ partial('aside_stats'); ?>
- + -

+

- idleFeeds as $period => $feeds){ ?> + idleFeeds as $period => $feeds) { ?>
-

+

    - -
  • + +
-- cgit v1.2.3 From d5d3f6dcfa21e7defb26f3d470289e957bdb3dde Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Wed, 23 Jul 2014 23:56:16 +0200 Subject: Fix bugs in import/export - EntryDAO.php: add a missing "FreshRSS_EntryDAO::" - Fix htmlspecialchars in opml export --- app/Models/EntryDAO.php | 4 +++- app/views/helpers/export/opml.phtml | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'app/views') diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 8c001e73b..74b5306e9 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -17,7 +17,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { } public function addEntry($valuesTmp, $preparedStatement = null) { - $stm = $preparedStatement === null ? addEntryPrepare() : $preparedStatement; + $stm = $preparedStatement === null ? + FreshRSS_EntryDAO::addEntryPrepare() : + $preparedStatement; $values = array( $valuesTmp['id'], diff --git a/app/views/helpers/export/opml.phtml b/app/views/helpers/export/opml.phtml index f667d093c..8622d9144 100644 --- a/app/views/helpers/export/opml.phtml +++ b/app/views/helpers/export/opml.phtml @@ -18,9 +18,9 @@ foreach ($this->categories as $key => $cat) { $opml_array['body'][$key]['@outlines'][] = array( 'text' => htmlspecialchars_decode($feed->name()), 'type' => 'rss', - 'xmlUrl' => $feed->url(), - 'htmlUrl' => $feed->website(), - 'description' => $feed->description() + 'xmlUrl' => htmlspecialchars_decode($feed->url()), + 'htmlUrl' => htmlspecialchars_decode($feed->website()), + 'description' => htmlspecialchars_decode($feed->description()), ); } } -- cgit v1.2.3 From d049c1bc806dc0677a4b2b17faf06080600c372f Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Thu, 24 Jul 2014 21:57:59 -0400 Subject: Add article repartition in stats Add article repartition per hour, per day of week, per month for all feeds but also for individual feeds. --- app/Controllers/statsController.php | 15 ++++- app/Models/StatsDAO.php | 129 +++++++++++++++++++++++++++++++++++- app/i18n/en.php | 47 +++++++++---- app/i18n/fr.php | 23 +++++++ app/layout/aside_flux.phtml | 1 + app/layout/aside_stats.phtml | 3 + app/views/stats/main.phtml | 127 ----------------------------------- app/views/stats/repartition.phtml | 100 ++++++++++++++++++++++++++++ 8 files changed, 304 insertions(+), 141 deletions(-) delete mode 100644 app/views/stats/main.phtml create mode 100644 app/views/stats/repartition.phtml (limited to 'app/views') diff --git a/app/Controllers/statsController.php b/app/Controllers/statsController.php index 45d13e043..06a20c2a6 100644 --- a/app/Controllers/statsController.php +++ b/app/Controllers/statsController.php @@ -55,7 +55,20 @@ class FreshRSS_stats_Controller extends Minz_ActionController { $this->view->idleFeeds = $idleFeeds; } - + + public function repartitionAction() { + $statsDAO = FreshRSS_Factory::createStatsDAO(); + $feedDAO = FreshRSS_Factory::createFeedDao(); + Minz_View::appendScript(Minz_Url::display('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js'))); + $id = Minz_Request::param ('id', null); + $this->view->feed = $feedDAO->searchById($id); + $this->view->days = $statsDAO->getDays(); + $this->view->months = $statsDAO->getMonths(); + $this->view->repartitionHour = $statsDAO->calculateEntryRepartitionPerFeedPerHour($id); + $this->view->repartitionDayOfWeek = $statsDAO->calculateEntryRepartitionPerFeedPerDayOfWeek($id); + $this->view->repartitionMonth = $statsDAO->calculateEntryRepartitionPerFeedPerMonth($id); + } + public function firstAction() { if (!$this->view->loginOk) { Minz_Error::error( diff --git a/app/Models/StatsDAO.php b/app/Models/StatsDAO.php index 9a88a4fcf..ee8d0d663 100644 --- a/app/Models/StatsDAO.php +++ b/app/Models/StatsDAO.php @@ -85,9 +85,83 @@ SQL; * @return array */ protected function initEntryCountArray() { + return $this->initStatsArray(-self::ENTRY_COUNT_PERIOD, -1); + } + + /** + * Calculates the number of article per hour of the day per feed + * + * @param integer $feed id + * @return string + */ + public function calculateEntryRepartitionPerFeedPerHour($feed = null) { + return $this->calculateEntryRepartitionPerFeedPerPeriod('%k', $feed); + } + + /** + * Calculates the number of article per day of week per feed + * + * @param integer $feed id + * @return string + */ + public function calculateEntryRepartitionPerFeedPerDayOfWeek($feed = null) { + return $this->calculateEntryRepartitionPerFeedPerPeriod('%w', $feed); + } + + /** + * Calculates the number of article per month per feed + * + * @param integer $feed + * @return string + */ + public function calculateEntryRepartitionPerFeedPerMonth($feed = null) { + return $this->calculateEntryRepartitionPerFeedPerPeriod('%m', $feed); + } + + /** + * Calculates the number of article per period per feed + * + * @param string $period format string to use for grouping + * @param integer $feed id + * @return string + */ + protected function calculateEntryRepartitionPerFeedPerPeriod($period, $feed = null) { + if ($feed) { + $restrict = "WHERE e.id_feed = {$feed}"; + } else { + $restrict = ''; + } + $sql = <<prefix}entry AS e +{$restrict} +GROUP BY period +ORDER BY period ASC +SQL; + + $stm = $this->bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_NAMED); + + foreach ($res as $value) { + $repartition[(int) $value['period']] = (int) $value['count']; + } + + return $this->convertToSerie($repartition); + } + + /** + * Initialize an array for statistics depending on a range + * + * @param integer $min + * @param integer $max + * @return array + */ + protected function initStatsArray($min, $max) { return array_map(function () { return 0; - }, array_flip(range(-self::ENTRY_COUNT_PERIOD, -1))); + }, array_flip(range($min, $max))); } /** @@ -205,4 +279,57 @@ SQL; return json_encode($serie); } + /** + * Gets days ready for graphs + * + * @return string + */ + public function getDays() { + return $this->convertToTranslatedJson(array( + 'sun', + 'mon', + 'tue', + 'wed', + 'thu', + 'fri', + 'sat', + )); + } + + /** + * Gets months ready for graphs + * + * @return string + */ + public function getMonths() { + return $this->convertToTranslatedJson(array( + 'jan', + 'feb', + 'mar', + 'apr', + 'may', + 'jun', + 'jul', + 'aug', + 'sep', + 'oct', + 'nov', + 'dec', + )); + } + + /** + * Translates array content and encode it as JSON + * + * @param array $data + * @return string + */ + private function convertToTranslatedJson($data = array()) { + $translated = array_map(function ($a) { + return Minz_Translate::t($a); + }, $data); + + return json_encode($translated); + } + } diff --git a/app/i18n/en.php b/app/i18n/en.php index 8634f99b5..10327c7f5 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -48,6 +48,10 @@ return array ( 'stats' => 'Statistics', 'stats_idle' => 'Idle feeds', 'stats_main' => 'Main statistics', + 'stats_repartition' => 'Articles repartition', + 'stats_entry_per_hour' => 'Per hour', + 'stats_entry_per_day_of_week' => 'Per day of week', + 'stats_entry_per_month' => 'Per month', 'last_week' => 'Last week', 'last_month' => 'Last month', @@ -341,18 +345,37 @@ return array ( 'confirm_action' => 'Are you sure you want to perform this action? It cannot be cancelled!', // DATE - 'january' => 'january', - 'february' => 'february', - 'march' => 'march', - 'april' => 'april', - 'may' => 'may', - 'june' => 'june', - 'july' => 'july', - 'august' => 'august', - 'september' => 'september', - 'october' => 'october', - 'november' => 'november', - 'december' => 'december', + 'january' => 'January', + 'february' => 'February', + 'march' => 'March', + 'april' => 'April', + 'may' => 'May', + 'june' => 'June', + 'july' => 'July', + 'august' => 'August', + 'september' => 'September', + 'october' => 'October', + 'november' => 'November', + 'december' => 'December', + 'january' => 'Jan', + 'february' => 'Feb', + 'march' => 'Mar', + 'april' => 'Apr', + 'may' => 'May', + 'june' => 'Jun', + 'july' => 'Jul', + 'august' => 'Aug', + 'september' => 'Sep', + 'october' => 'Oct', + 'november' => 'Nov', + 'december' => 'Dec', + 'sun' => 'Sun', + 'mon' => 'Mon', + 'tue' => 'Tue', + 'wed' => 'Wed', + 'thu' => 'Thu', + 'fri' => 'Fri', + 'sat' => 'Sat', // special format for date() function 'Jan' => '\J\a\n\u\a\r\y', 'Feb' => '\F\e\b\r\u\a\r\y', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index e04078dba..6ab3d7335 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -48,6 +48,10 @@ return array ( 'stats' => 'Statistiques', 'stats_idle' => 'Flux inactifs', 'stats_main' => 'Statistiques principales', + 'stats_repartition' => 'Répartition des articles', + 'stats_entry_per_hour' => 'Par heure', + 'stats_entry_per_day_of_week' => 'Par jour de la semaine', + 'stats_entry_per_month' => 'Par mois', 'last_week' => 'La dernière semaine', 'last_month' => 'Le dernier mois', @@ -353,6 +357,25 @@ return array ( 'october' => 'octobre', 'november' => 'novembre', 'december' => 'décembre', + 'jan' => 'jan.', + 'feb' => 'fév.', + 'mar' => 'mar.', + 'apr' => 'avr.', + 'may' => 'mai.', + 'jun' => 'juin', + 'jul' => 'jui.', + 'aug' => 'août', + 'sep' => 'sep.', + 'oct' => 'oct.', + 'nov' => 'nov.', + 'dec' => 'déc.', + 'sun' => 'dim.', + 'mon' => 'lun.', + 'tue' => 'mar.', + 'wed' => 'mer.', + 'thu' => 'jeu.', + 'fri' => 'ven.', + 'sat' => 'sam.', // format spécial pour la fonction date() 'Jan' => '\j\a\n\v\i\e\r', 'Feb' => '\f\é\v\r\i\e\r', diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml index 817dae676..5fbb36730 100644 --- a/app/layout/aside_flux.phtml +++ b/app/layout/aside_flux.phtml @@ -83,6 +83,7 @@
  • +
  • diff --git a/app/layout/aside_stats.phtml b/app/layout/aside_stats.phtml index 32a3f5dee..fbfb9d84d 100644 --- a/app/layout/aside_stats.phtml +++ b/app/layout/aside_stats.phtml @@ -6,4 +6,7 @@
  • +
  • + +
  • diff --git a/app/views/stats/main.phtml b/app/views/stats/main.phtml deleted file mode 100644 index fe372e221..000000000 --- a/app/views/stats/main.phtml +++ /dev/null @@ -1,127 +0,0 @@ -partial('aside_stats'); ?> - -
    - - -

    - -
    -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     
    repartition['main_stream']['total']); ?>repartition['all_feeds']['total']); ?>
    repartition['main_stream']['read']); ?>repartition['all_feeds']['read']); ?>
    repartition['main_stream']['unread']); ?>repartition['all_feeds']['unread']); ?>
    repartition['main_stream']['favorite']); ?>repartition['all_feeds']['favorite']); ?>
    -
    - -
    -

    -
    -
    - -
    -

    -
    -
    -
    - -
    -

    -
    -
    -
    - -
    -

    - - - - - - - - - - topFeed as $feed): ?> - - - - - - - -
    -
    -
    - - diff --git a/app/views/stats/repartition.phtml b/app/views/stats/repartition.phtml new file mode 100644 index 000000000..4455abe2a --- /dev/null +++ b/app/views/stats/repartition.phtml @@ -0,0 +1,100 @@ +partial('aside_stats'); ?> + +
    + + + feed) {?> +

    + + + feed->name(); ?> + +

    + +

    + + +
    +

    +
    +
    + +
    +

    +
    +
    + +
    +

    +
    +
    +
    + + \ No newline at end of file -- cgit v1.2.3 From 584665eed4dfc1bf31c7557edd96e90b64f90c54 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 25 Jul 2014 10:33:06 +0200 Subject: Stats idle: hide empty periods https://github.com/marienfressinaud/FreshRSS/commit/e507256d0bdebd02cf1fcd6fe1477cbac0b6934e#commitcomment-7119169 --- app/views/stats/idle.phtml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'app/views') diff --git a/app/views/stats/idle.phtml b/app/views/stats/idle.phtml index a167fcccb..2ba5237f7 100644 --- a/app/views/stats/idle.phtml +++ b/app/views/stats/idle.phtml @@ -5,7 +5,10 @@

    - idleFeeds as $period => $feeds) { ?> + idleFeeds as $period => $feeds) { + if (!empty($feeds)) { + ?>

    @@ -15,5 +18,8 @@
    - +
    -- cgit v1.2.3 From 84dcd25d89a85c00df39d70525aa6d401a69b005 Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Fri, 25 Jul 2014 06:36:43 -0400 Subject: Small fixes to follow @marienfressinaud recommandations --- app/layout/aside_flux.phtml | 2 +- app/views/stats/repartition.phtml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'app/views') diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml index 5fbb36730..b1ed7a10d 100644 --- a/app/layout/aside_flux.phtml +++ b/app/layout/aside_flux.phtml @@ -77,13 +77,13 @@ diff --git a/app/views/stats/repartition.phtml b/app/views/stats/repartition.phtml index 4455abe2a..26b20e424 100644 --- a/app/views/stats/repartition.phtml +++ b/app/views/stats/repartition.phtml @@ -1,4 +1,4 @@ -partial('aside_stats'); ?> +partial ('aside_stats'); ?>
    @@ -6,8 +6,8 @@ feed) {?>

    - + feed->name(); ?>

    @@ -97,4 +97,4 @@ function initStats() { } initStats(); - \ No newline at end of file + -- cgit v1.2.3 From 2739817cfb1fde24ca4470e24fcbaa93d16e0530 Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Mon, 28 Jul 2014 17:46:03 -0400 Subject: Small fixes to follow @marienfressinaud recommandations (take 2) --- app/views/stats/repartition.phtml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'app/views') diff --git a/app/views/stats/repartition.phtml b/app/views/stats/repartition.phtml index 26b20e424..7eb858a64 100644 --- a/app/views/stats/repartition.phtml +++ b/app/views/stats/repartition.phtml @@ -1,32 +1,32 @@ -partial ('aside_stats'); ?> +partial('aside_stats'); ?>
    - + feed) {?>

    - - + + feed->name(); ?>

    -

    +

    -

    +

    -

    +

    -

    +

    -- cgit v1.2.3 From 1739e2e9d2f6b46ee7de81322f282b4ef4f0dddd Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Mon, 28 Jul 2014 17:59:20 -0400 Subject: Remove unnecessary title property --- app/views/stats/repartition.phtml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'app/views') diff --git a/app/views/stats/repartition.phtml b/app/views/stats/repartition.phtml index 7eb858a64..09892d3c5 100644 --- a/app/views/stats/repartition.phtml +++ b/app/views/stats/repartition.phtml @@ -6,8 +6,7 @@ feed) {?>

    - + feed->name(); ?>

    -- cgit v1.2.3 From 35be1769de28df3fff1a26e40d1d6b1e587a2847 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 1 Aug 2014 20:20:25 +0200 Subject: Basic protection against XSRF using Referer https://github.com/marienfressinaud/FreshRSS/issues/554 Also edited the error controler to use the log message passed in Minz_Error::error(). --- app/Controllers/errorController.php | 52 +++++++++++++++++++++++-------------- app/FreshRSS.php | 11 +++++++- app/views/error/index.phtml | 13 ++-------- lib/Minz/Translate.php | 2 +- 4 files changed, 45 insertions(+), 33 deletions(-) (limited to 'app/views') diff --git a/app/Controllers/errorController.php b/app/Controllers/errorController.php index dc9a2ee25..922650b3d 100644 --- a/app/Controllers/errorController.php +++ b/app/Controllers/errorController.php @@ -1,26 +1,38 @@ view->code = 'Error 403 - Forbidden'; - break; - case 404: - $this->view->code = 'Error 404 - Not found'; - break; - case 500: - $this->view->code = 'Error 500 - Internal Server Error'; - break; - case 503: - $this->view->code = 'Error 503 - Service Unavailable'; - break; - default: - $this->view->code = 'Error 404 - Not found'; + public function indexAction() { + switch (Minz_Request::param('code')) { + case 403: + $this->view->code = 'Error 403 - Forbidden'; + break; + case 404: + $this->view->code = 'Error 404 - Not found'; + break; + case 500: + $this->view->code = 'Error 500 - Internal Server Error'; + break; + case 503: + $this->view->code = 'Error 503 - Service Unavailable'; + break; + default: + $this->view->code = 'Error 404 - Not found'; } - - $this->view->logs = Minz_Request::param ('logs'); - - Minz_View::prependTitle ($this->view->code . ' · '); + + $errors = Minz_Request::param('logs', array()); + $this->view->errorMessage = trim(implode($errors)); + if ($this->view->errorMessage == '') { + switch(Minz_Request::param('code')) { + case 403: + $this->view->errorMessage = Minz_Translate::t('forbidden_access'); + break; + case 404: + default: + $this->view->errorMessage = Minz_Translate::t('page_not_found'); + break; + } + } + + Minz_View::prependTitle($this->view->code . ' · '); } } diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 84cf3429b..cd6048f75 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -6,6 +6,16 @@ class FreshRSS extends Minz_FrontController { } $loginOk = $this->accessControl(Minz_Session::param('currentUser', '')); $this->loadParamsView(); + if (Minz_Request::isPost() && !empty($_SERVER['HTTP_REFERER']) && + Minz_Request::getDomainName() !== parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST)) { + $loginOk = false; //Basic protection against XSRF attacks + Minz_Error::error( + 403, + array('error' => array(Minz_Translate::t('access_denied') . ' [HTTP_REFERER=' . + htmlspecialchars(empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']) . ']')) + ); + } + Minz_View::_param('loginOk', $loginOk); $this->loadStylesAndScripts($loginOk); //TODO: Do not load that when not needed, e.g. some Ajax requests $this->loadNotifications(); } @@ -95,7 +105,6 @@ class FreshRSS extends Minz_FrontController { break; } } - Minz_View::_param ('loginOk', $loginOk); return $loginOk; } diff --git a/app/views/error/index.phtml b/app/views/error/index.phtml index 6a09c3aa2..ef4fbd39d 100644 --- a/app/views/error/index.phtml +++ b/app/views/error/index.phtml @@ -1,18 +1,9 @@

    code; ?>

    -

    -
    - + errorMessage; ?>
    +

    diff --git a/lib/Minz/Translate.php b/lib/Minz/Translate.php index df48350e9..8c2f90041 100644 --- a/lib/Minz/Translate.php +++ b/lib/Minz/Translate.php @@ -75,5 +75,5 @@ function _t($key) { unset($args[0]); array_unshift($args, $key); - return call_user_func_array("Minz_Translate::t", $args); + return call_user_func_array('Minz_Translate::t', $args); } -- cgit v1.2.3 From 274c8096e3ccc8ea008c1a038134ffddc302fd0d Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 2 Aug 2014 19:57:15 +0200 Subject: Experimental: Removed lazyload.js and use postpone attribute instead https://github.com/marienfressinaud/FreshRSS/issues/316 The performance of lazyload.js was not good enough, and not really needed anyway. This change mostly affects mainly situations when the content of articles is shown by default, not so much when they are collapsed Using HTML5 lazyload and postpone attributes by default on all img, audio, iframe, video. http://www.w3.org/TR/resource-priorities/#attr-postpone Postpone attribute is removed by JavaScript if the user does not want the lazyload behaviour. In the case when users do want the lazyload behaviour, in normal view with articles hidden, we furthermore use the data-original approach to be sure to support current browsers. +Corrected some bugs with enclosures, and some images not appearing before the first scroll. +Now faster regex processing img and iframe at once (was not practical with lazyload.js) --- CHANGELOG | 2 ++ README.md | 1 - app/FreshRSS.php | 10 +++------- app/Models/Feed.php | 6 +++--- app/views/configure/reading.phtml | 2 +- app/views/helpers/javascript_vars.phtml | 1 - app/views/helpers/view/normal_view.phtml | 10 +++------- app/views/helpers/view/reader_view.phtml | 18 ++++++------------ lib/lib_rss.php | 20 ++++++-------------- p/scripts/jquery.lazyload.min.js | 15 --------------- p/scripts/main.js | 25 ++++++++----------------- 11 files changed, 32 insertions(+), 78 deletions(-) delete mode 100644 p/scripts/jquery.lazyload.min.js (limited to 'app/views') diff --git a/CHANGELOG b/CHANGELOG index 33cb810c4..969af92a7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,6 +10,8 @@ * Improvements * Security * Basic protection against XSRF (Cross-Site Request Forgery) based on HTTP Referer (POST requests only) +* Misc. + * Changed lazyload implementation * Bux fixes in export function, add/remove users, keyboard shortcuts, etc. diff --git a/README.md b/README.md index fff08472b..8963e040c 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,6 @@ mysqldump -u utilisateur -p --databases freshrss > freshrss.sql ## Uniquement pour certaines options * [bcrypt.js](https://github.com/dcodeIO/bcrypt.js) * [phpQuery](http://code.google.com/p/phpquery/) -* [Lazy Load](http://www.appelsiini.net/projects/lazyload) ## Si les fonctions natives ne sont pas disponibles * [Services_JSON](http://pear.php.net/pepr/pepr-proposal-show.php?id=198) diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 3443589c6..7c333b090 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -136,13 +136,9 @@ class FreshRSS extends Minz_FrontController { Minz_View::appendScript('https://login.persona.org/include.js'); break; } - $includeLazyLoad = $this->conf->lazyload && ($this->conf->display_posts || Minz_Request::param ('output') === 'reader'); - Minz_View::appendScript (Minz_Url::display ('/scripts/jquery.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/jquery.min.js')), false, !$includeLazyLoad, !$includeLazyLoad); - if ($includeLazyLoad) { - Minz_View::appendScript (Minz_Url::display ('/scripts/jquery.lazyload.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/jquery.lazyload.min.js'))); - } - Minz_View::appendScript (Minz_Url::display ('/scripts/shortcut.js?' . @filemtime(PUBLIC_PATH . '/scripts/shortcut.js'))); - Minz_View::appendScript (Minz_Url::display ('/scripts/main.js?' . @filemtime(PUBLIC_PATH . '/scripts/main.js'))); + Minz_View::appendScript(Minz_Url::display('/scripts/jquery.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/jquery.min.js'))); + Minz_View::appendScript(Minz_Url::display('/scripts/shortcut.js?' . @filemtime(PUBLIC_PATH . '/scripts/shortcut.js'))); + Minz_View::appendScript(Minz_Url::display('/scripts/main.js?' . @filemtime(PUBLIC_PATH . '/scripts/main.js'))); } private function loadNotifications () { diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 576f37760..fe1e52ea2 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -277,11 +277,11 @@ class FreshRSS_Feed extends Minz_Model { $elinks[$elink] = '1'; $mime = strtolower($enclosure->get_type()); if (strpos($mime, 'image/') === 0) { - $content .= '
    '; + $content .= '
    '; } elseif (strpos($mime, 'audio/') === 0) { - $content .= '
    diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 7ca611b04..86c0a4ae4 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -121,10 +121,10 @@ function customSimplePie() { 'onmouseover', 'onmousemove', 'onmouseout', 'onfocus', 'onblur', 'onkeypress', 'onkeydown', 'onkeyup', 'onselect', 'onchange', 'seamless'))); $simplePie->add_attributes(array( - 'img' => array('lazyload' => ''), //http://www.w3.org/TR/resource-priorities/ - 'audio' => array('preload' => 'none'), - 'iframe' => array('postpone' => '', 'sandbox' => 'allow-scripts allow-same-origin'), - 'video' => array('postpone' => '', 'preload' => 'none'), + 'img' => array('lazyload' => '', 'postpone' => ''), //http://www.w3.org/TR/resource-priorities/ + 'audio' => array('lazyload' => '', 'postpone' => '', 'preload' => 'none'), + 'iframe' => array('lazyload' => '', 'postpone' => '', 'sandbox' => 'allow-scripts allow-same-origin'), + 'video' => array('lazyload' => '', 'postpone' => '', 'preload' => 'none'), )); $simplePie->set_url_replacements(array( 'a' => 'href', @@ -183,16 +183,8 @@ function get_content_by_parsing ($url, $path) { */ function lazyimg($content) { return preg_replace( - '/]+?)src=[\'"]([^"\']+)[\'"]([^>]*)>/i', - '', - $content - ); -} - -function lazyIframe($content) { - return preg_replace( - '/]+?)src=[\'"]([^"\']+)[\'"]([^>]*)>/i', - '', + '/<((?:img|iframe)[^>]+?)src=[\'"]([^"\']+)[\'"]([^>]*)>/i', + '<$1src="' . Minz_Url::display('/themes/icons/grey.gif') . '" data-original="$2"$3>', $content ); } diff --git a/p/scripts/jquery.lazyload.min.js b/p/scripts/jquery.lazyload.min.js deleted file mode 100644 index 8dd097dc3..000000000 --- a/p/scripts/jquery.lazyload.min.js +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Lazy Load - jQuery plugin for lazy loading images - * - * Copyright (c) 2007-2013 Mika Tuupola - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/mit-license.php - * - * Project home: - * http://www.appelsiini.net/projects/lazyload - * - * Version: 1.9.0 - * - */ -!function(a,b,c,d){var e=a(b);a.fn.lazyload=function(f){function g(){var b=0;i.each(function(){var c=a(this);if(!j.skip_invisible||c.is(":visible"))if(a.abovethetop(this,j)||a.leftofbegin(this,j));else if(a.belowthefold(this,j)||a.rightoffold(this,j)){if(++b>j.failure_limit)return!1}else c.trigger("appear"),b=0})}var h,i=this,j={threshold:0,failure_limit:0,event:"scroll",effect:"show",container:b,data_attribute:"original",skip_invisible:!0,appear:null,load:null,placeholder:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC"};return f&&(d!==f.failurelimit&&(f.failure_limit=f.failurelimit,delete f.failurelimit),d!==f.effectspeed&&(f.effect_speed=f.effectspeed,delete f.effectspeed),a.extend(j,f)),h=j.container===d||j.container===b?e:a(j.container),0===j.event.indexOf("scroll")&&h.bind(j.event,function(){return g()}),this.each(function(){var b=this,c=a(b);b.loaded=!1,(c.attr("src")===d||c.attr("src")===!1)&&c.attr("src",j.placeholder),c.one("appear",function(){if(!this.loaded){if(j.appear){var d=i.length;j.appear.call(b,d,j)}a("").bind("load",function(){var d=c.data(j.data_attribute);c.hide(),c.is("img")?c.attr("src",d):c.css("background-image","url('"+d+"')"),c[j.effect](j.effect_speed),b.loaded=!0;var e=a.grep(i,function(a){return!a.loaded});if(i=a(e),j.load){var f=i.length;j.load.call(b,f,j)}}).attr("src",c.data(j.data_attribute))}}),0!==j.event.indexOf("scroll")&&c.bind(j.event,function(){b.loaded||c.trigger("appear")})}),e.bind("resize",function(){g()}),/iphone|ipod|ipad.*os 5/gi.test(navigator.appVersion)&&e.bind("pageshow",function(b){b.originalEvent&&b.originalEvent.persisted&&i.each(function(){a(this).trigger("appear")})}),a(c).ready(function(){g()}),this},a.belowthefold=function(c,f){var g;return g=f.container===d||f.container===b?(b.innerHeight?b.innerHeight:e.height())+e.scrollTop():a(f.container).offset().top+a(f.container).height(),g<=a(c).offset().top-f.threshold},a.rightoffold=function(c,f){var g;return g=f.container===d||f.container===b?e.width()+e.scrollLeft():a(f.container).offset().left+a(f.container).width(),g<=a(c).offset().left-f.threshold},a.abovethetop=function(c,f){var g;return g=f.container===d||f.container===b?e.scrollTop():a(f.container).offset().top,g>=a(c).offset().top+f.threshold+a(c).height()},a.leftofbegin=function(c,f){var g;return g=f.container===d||f.container===b?e.scrollLeft():a(f.container).offset().left,g>=a(c).offset().left+f.threshold+a(c).width()},a.inviewport=function(b,c){return!(a.rightoffold(b,c)||a.leftofbegin(b,c)||a.belowthefold(b,c)||a.abovethetop(b,c))},a.extend(a.expr[":"],{"below-the-fold":function(b){return a.belowthefold(b,{threshold:0})},"above-the-top":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-screen":function(b){return a.rightoffold(b,{threshold:0})},"left-of-screen":function(b){return!a.rightoffold(b,{threshold:0})},"in-viewport":function(b){return a.inviewport(b,{threshold:0})},"above-the-fold":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-fold":function(b){return a.rightoffold(b,{threshold:0})},"left-of-fold":function(b){return!a.rightoffold(b,{threshold:0})}})}(jQuery,window,document); \ No newline at end of file diff --git a/p/scripts/main.js b/p/scripts/main.js index b6214e508..acb7bd527 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -421,21 +421,7 @@ function inMarkViewport(flux, box_to_follow, relative_follow) { return (windowBot >= begin && bot >= windowBot); } -function init_lazyload() { - if ($.fn.lazyload) { - if (is_global_mode()) { - $(".flux_content img").lazyload({ - container: $("#panel") - }); - } else { - $(".flux_content img").lazyload(); - } - } -} - function init_posts() { - init_lazyload(); - var box_to_follow = $(window), relative_follow = false; if (is_global_mode()) { @@ -827,7 +813,6 @@ function load_more_posts() { }); init_load_more(box_load_more); - init_lazyload(); $('#load_more').removeClass('loading'); load_more = false; @@ -841,6 +826,12 @@ function focus_search() { function init_load_more(box) { box_load_more = box; + if (!does_lazyload) { + $('img[postpone], audio[postpone], iframe[postpone], video[postpone]').each(function () { + this.removeAttribute('postpone'); + }); + } + var $next_link = $("#load_more"); if (!$next_link.length) { // no more article to load @@ -1093,7 +1084,7 @@ function faviconNbUnread(n) { ctx.fillStyle = 'rgba(255, 255, 255, 127)'; ctx.fillRect(0, 8, 1 + ctx.measureText(text).width, 7); ctx.fillStyle = '#F00'; - ctx.fillText(text, 0, 16); + ctx.fillText(text, 0, canvas.height); } link.href = canvas.toDataURL('image/png'); $('link[rel~=icon]').remove(); @@ -1104,7 +1095,7 @@ function faviconNbUnread(n) { } function init_all() { - if (!(window.$ && window.url_freshrss && ((!full_lazyload) || $.fn.lazyload))) { + if (!(window.$ && window.url_freshrss)) { if (window.console) { console.log('FreshRSS waiting for JS…'); } -- cgit v1.2.3 From a9a61929485279c819d42b7a2d65b8a1f5e2cfdd Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 2 Aug 2014 20:29:51 +0200 Subject: Unicode tick without colour https://github.com/marienfressinaud/FreshRSS/issues/560 The former was green in Firefox 32 beta --- app/views/helpers/pagination.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/views') diff --git a/app/views/helpers/pagination.phtml b/app/views/helpers/pagination.phtml index f38913c06..f237e1f3e 100755 --- a/app/views/helpers/pagination.phtml +++ b/app/views/helpers/pagination.phtml @@ -14,7 +14,7 @@ conf->reading_confirm) { echo ' class="confirm"';} ?>>
    -
    +
    -- cgit v1.2.3 From d289c5340f3b21f1561e57390e89b0b5c7881d17 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Fri, 8 Aug 2014 21:30:23 +0200 Subject: Add support of HTML5 notifications Show a notification if there are at least 1 new article to read. Support only window.Notification API. See https://github.com/marienfressinaud/FreshRSS/issues/399 --- app/i18n/en.php | 2 ++ app/i18n/fr.php | 2 ++ app/views/helpers/javascript_vars.phtml | 2 ++ p/scripts/main.js | 48 ++++++++++++++++++++++++++++++++- 4 files changed, 53 insertions(+), 1 deletion(-) (limited to 'app/views') diff --git a/app/i18n/en.php b/app/i18n/en.php index 10327c7f5..9e5bfb223 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -343,6 +343,8 @@ return array ( 'login_required' => 'Login required:', 'confirm_action' => 'Are you sure you want to perform this action? It cannot be cancelled!', + 'notif_title_new_articles' => 'FreshRSS: new articles!', + 'notif_body_new_articles' => 'There are \d new articles to read on FreshRSS.', // DATE 'january' => 'January', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 6ab3d7335..94d2e5f06 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -343,6 +343,8 @@ return array ( 'login_required' => 'Accès protégé par mot de passe :', 'confirm_action' => 'Êtes-vous sûr(e) de vouloir continuer ? Cette action ne peut être annulée !', + 'notif_title_new_articles' => 'FreshRSS : nouveaux articles !', + 'notif_body_new_articles' => 'Il y a \d nouveaux articles à lire sur FreshRSS.', // DATE 'january' => 'janvier', diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml index a04d3d527..7144c519a 100644 --- a/app/views/helpers/javascript_vars.phtml +++ b/app/views/helpers/javascript_vars.phtml @@ -49,6 +49,8 @@ echo 'authType="', $authType, '",', 'url_logout="', _url ('index', 'logout'), '",'; echo 'str_confirmation="', Minz_Translate::t('confirm_action'), '"', ",\n"; +echo 'str_notif_title_articles="', Minz_Translate::t('notif_title_new_articles'), '"', ",\n"; +echo 'str_notif_body_articles="', Minz_Translate::t('notif_body_new_articles'), '"', ",\n"; $autoActualise = Minz_Session::param('actualize_feeds', false); echo 'auto_actualize_feeds=', $autoActualise ? 'true' : 'false', ";\n"; diff --git a/p/scripts/main.js b/p/scripts/main.js index a82450e7f..bd412d6e9 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -768,6 +768,46 @@ function init_notifications() { } // +// +var notifs_html5_permission = 'denied', + notifs_html5_shown = false; + +function notifs_html5_is_supported() { + return window.Notification !== undefined; +} + +function notifs_html5_ask_permission() { + window.Notification.requestPermission(function () { + notifs_html5_permission = window.Notification.permission; + }); +} + +function notifs_html5_show(nb) { + if (notifs_html5_permission !== "granted" || notifs_html5_shown) { + return + } + + var notification = new window.Notification(str_notif_title_articles, { + icon: "../themes/icons/favicon-256.png", + body: str_notif_body_articles.replace("\d", nb) + }); + + notification.onclick = function() { + window.location.reload(); + } + + notifs_html5_shown = true; +} + +function init_notifs_html5() { + if (!notifs_html5_is_supported()) { + return; + } + + notifs_html5_permission = notifs_html5_ask_permission(); +} +// + function refreshUnreads() { $.getJSON('./?c=javascript&a=nbUnreadsPerFeed').done(function (data) { var isAll = $('.category.all > .active').length > 0; @@ -780,7 +820,12 @@ function refreshUnreads() { $('#new-article').show(); }; }); - faviconNbUnread(); + + var nb_unreads = str2int($('.category.all>a').attr('data-unread')); + if (nb_unreads > 0) { + faviconNbUnread(nb_unreads); + notifs_html5_show(nb_unreads); + } }); } @@ -1123,6 +1168,7 @@ function init_all() { init_shortcuts(); faviconNbUnread(); init_print_action(); + init_notifs_html5(); window.setInterval(refreshUnreads, 120000); } else { init_share_observers(); -- cgit v1.2.3 From 84826491a3fac3bde21c07ad69b114e5b56ed297 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Fri, 8 Aug 2014 23:53:16 +0200 Subject: Improve export function If there is only one file to export, we don't need of a .zip archive. So it is exported as a simple file (.json or .opml) See https://github.com/marienfressinaud/FreshRSS/issues/494 --- app/Controllers/importExportController.php | 74 ++++++++++++++++++++---------- app/views/importExport/export.phtml | 0 2 files changed, 51 insertions(+), 23 deletions(-) create mode 100644 app/views/importExport/export.phtml (limited to 'app/views') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index ba172cc6d..67c6eb867 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -316,39 +316,42 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $export_opml = Minz_Request::param('export_opml', false); $export_starred = Minz_Request::param('export_starred', false); - $export_feeds = Minz_Request::param('export_feeds', false); + $export_feeds = Minz_Request::param('export_feeds', array ()); - // From https://stackoverflow.com/questions/1061710/php-zip-files-on-the-fly - $file = tempnam('tmp', 'zip'); - $zip = new ZipArchive(); - $zip->open($file, ZipArchive::OVERWRITE); - - // Stuff with content + $export_files = array (); if ($export_opml) { - $zip->addFromString( - 'feeds.opml', $this->generateOpml() - ); + $export_files['feeds.opml'] = $this->generateOpml(); } + if ($export_starred) { - $zip->addFromString( - 'starred.json', $this->generateArticles('starred') - ); + $export_files['starred.json'] = $this->generateArticles('starred'); } + foreach ($export_feeds as $feed_id) { $feed = $this->feedDAO->searchById($feed_id); - $zip->addFromString( - 'feed_' . $feed->category() . '_' . $feed->id() . '.json', - $this->generateArticles('feed', $feed) + $filename = 'feed_' . $feed->category() . '_' + . $feed->id() . '.json'; + $export_files[$filename] = $this->generateArticles( + 'feed', $feed ); } - // Close and send to user - $zip->close(); - header('Content-Type: application/zip'); - header('Content-Length: ' . filesize($file)); - header('Content-Disposition: attachment; filename="freshrss_export.zip"'); - readfile($file); - unlink($file); + $nb_files = count($export_files); + if ($nb_files > 1) { + // If there are more than 1 file to export, we need an .zip + $this->exportZip($export_files); + } elseif ($nb_files === 1) { + // Only one file? Guess its type and export it. + $filename = key($export_files); + $type = null; + if (substr_compare($filename, '.opml', -5) === 0) { + $type = "text/xml"; + } elseif (substr_compare($filename, '.json', -5) === 0) { + $type = "text/json"; + } + + $this->exportFile($filename, $export_files[$filename], $type); + } } } @@ -388,4 +391,29 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { return $this->view->helperToString('export/articles'); } + + private function exportZip($files) { + // From https://stackoverflow.com/questions/1061710/php-zip-files-on-the-fly + $zip_file = tempnam('tmp', 'zip'); + $zip = new ZipArchive(); + $zip->open($zip_file, ZipArchive::OVERWRITE); + + foreach ($files as $filename => $content) { + $zip->addFromString($filename, $content); + } + + // Close and send to user + $zip->close(); + header('Content-Type: application/zip'); + header('Content-Length: ' . filesize($zip_file)); + header('Content-Disposition: attachment; filename="freshrss_export.zip"'); + readfile($zip_file); + unlink($zip_file); + } + + private function exportFile($filename, $content, $type) { + header('Content-Type: ' . $type . '; charset=utf-8'); + header('Content-disposition: attachment; filename=' . $filename); + print($content); + } } diff --git a/app/views/importExport/export.phtml b/app/views/importExport/export.phtml new file mode 100644 index 000000000..e69de29bb -- cgit v1.2.3 From d007b22beb701b78968db420c3be6336ee98a350 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sat, 9 Aug 2014 00:23:22 +0200 Subject: Change view import/export if no zip extension Show a select with only one choice is there is no zip extension on the server. Fix typo. See https://github.com/marienfressinaud/FreshRSS/issues/494 --- app/Controllers/importExportController.php | 12 +++++++----- app/views/importExport/index.phtml | 27 +++++++++++++++++---------- 2 files changed, 24 insertions(+), 15 deletions(-) (limited to 'app/views') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index ba4ca4eb0..2b3353d93 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -329,11 +329,13 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { foreach ($export_feeds as $feed_id) { $feed = $this->feedDAO->searchById($feed_id); - $filename = 'feed_' . $feed->category() . '_' - . $feed->id() . '.json'; - $export_files[$filename] = $this->generateArticles( - 'feed', $feed - ); + if ($feed) { + $filename = 'feed_' . $feed->category() . '_' + . $feed->id() . '.json'; + $export_files[$filename] = $this->generateArticles( + 'feed', $feed + ); + } } $nb_files = count($export_files); diff --git a/app/views/importExport/index.phtml b/app/views/importExport/index.phtml index 309058959..d7df1619d 100644 --- a/app/views/importExport/index.phtml +++ b/app/views/importExport/index.phtml @@ -1,12 +1,12 @@ -partial ('aside_feed'); ?> +partial('aside_feed'); ?>
    - +
    - +
    - +
    @@ -14,27 +14,34 @@
    - +
    feeds) > 0) { ?>
    - +
    - > + '; ?> feeds as $feed) { ?> @@ -44,7 +51,7 @@
    - +
    -- cgit v1.2.3 From 773d73314bf1f6e6c2ddec33a324d0aef6e8155b Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sat, 9 Aug 2014 00:35:06 +0200 Subject: Fix typo Oops? :) --- app/views/importExport/index.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/views') diff --git a/app/views/importExport/index.phtml b/app/views/importExport/index.phtml index d7df1619d..96cd2b4e7 100644 --- a/app/views/importExport/index.phtml +++ b/app/views/importExport/index.phtml @@ -41,7 +41,7 @@ } ?> + /> feeds)); ?>" multiple="multiple"'; + $select_args = ' size="' . min(10, count($this->feeds)) .'" multiple="multiple"'; } ?> - > + '; ?> feeds as $feed) { ?> -- cgit v1.2.3 From 2b25aa8f68dccf7001b965fc6c08c3a794ec6b04 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 9 Aug 2014 18:46:22 +0200 Subject: Option to hide (or not) feeds/categories with no unread article https://github.com/marienfressinaud/FreshRSS/issues/430 https://github.com/marienfressinaud/FreshRSS/issues/575 --- app/Controllers/configureController.php | 1 + app/Models/Configuration.php | 4 ++++ app/i18n/en.php | 1 + app/i18n/fr.php | 1 + app/layout/aside_flux.phtml | 2 +- app/views/configure/reading.phtml | 9 +++++++++ 6 files changed, 17 insertions(+), 1 deletion(-) (limited to 'app/views') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 79f40b30b..0bf58880f 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -184,6 +184,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->conf->_default_view((int)Minz_Request::param('default_view', FreshRSS_Entry::STATE_ALL)); $this->view->conf->_auto_load_more(Minz_Request::param('auto_load_more', false)); $this->view->conf->_display_posts(Minz_Request::param('display_posts', false)); + $this->view->conf->_hide_read_feeds(Minz_Request::param('hide_read_feeds', false)); $this->view->conf->_onread_jump_next(Minz_Request::param('onread_jump_next', false)); $this->view->conf->_lazyload(Minz_Request::param('lazyload', false)); $this->view->conf->_sticky_post(Minz_Request::param('sticky_post', false)); diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index 7596c54cd..2f47312c0 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -17,6 +17,7 @@ class FreshRSS_Configuration { 'default_view' => FreshRSS_Entry::STATE_NOT_READ, 'auto_load_more' => true, 'display_posts' => false, + 'hide_read_feeds' => true, 'onread_jump_next' => true, 'lazyload' => true, 'sticky_post' => true, @@ -141,6 +142,9 @@ class FreshRSS_Configuration { public function _display_posts ($value) { $this->data['display_posts'] = ((bool)$value) && $value !== 'no'; } + public function _hide_read_feeds($value) { + $this->data['hide_read_feeds'] = (bool)$value; + } public function _onread_jump_next ($value) { $this->data['onread_jump_next'] = ((bool)$value) && $value !== 'no'; } diff --git a/app/i18n/en.php b/app/i18n/en.php index 532327e4a..d80299b10 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -262,6 +262,7 @@ return array ( 'sort_order' => 'Sort order', 'auto_load_more' => 'Load next articles at the page bottom', 'display_articles_unfolded' => 'Show articles unfolded by default', + 'hide_read_feeds' => 'Hide categories & feeds with no unread article (only in “unread articles” display mode)', 'after_onread' => 'After “mark all as read”,', 'jump_next' => 'jump to next unread sibling (feed or category)', 'article_icons' => 'Article icons', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 0705d80b2..4be028ac3 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -262,6 +262,7 @@ return array ( 'sort_order' => 'Ordre de tri', 'auto_load_more' => 'Charger les articles suivants en bas de page', 'display_articles_unfolded' => 'Afficher les articles dépliés par défaut', + 'hide_read_feeds' => 'Cacher les catégories & flux sans article non-lu (uniquement en affichage “articles non lus”)', 'after_onread' => 'Après “marquer tout comme lu”,', 'jump_next' => 'sauter au prochain voisin non lu (flux ou catégorie)', 'article_icons' => 'Icônes d’article', diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml index cc04e757c..aee8f8754 100644 --- a/app/layout/aside_flux.phtml +++ b/app/layout/aside_flux.phtml @@ -1,4 +1,4 @@ -
    +
      diff --git a/app/views/configure/reading.phtml b/app/views/configure/reading.phtml index d56726730..c0525f1ed 100644 --- a/app/views/configure/reading.phtml +++ b/app/views/configure/reading.phtml @@ -62,6 +62,15 @@
    +
    +
    + +
    +
    +