From b4463cb69e64ff2dfe840ef69f1b825ecdbee43e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 15 Dec 2013 11:28:27 +0100 Subject: Problème casse renommage répertoire MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/indexController.php | 277 ++++++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100755 app/Controllers/indexController.php (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php new file mode 100755 index 000000000..92070590a --- /dev/null +++ b/app/Controllers/indexController.php @@ -0,0 +1,277 @@ +entryDAO = new FreshRSS_EntryDAO (); + $this->feedDAO = new FreshRSS_FeedDAO (); + $this->catDAO = new FreshRSS_CategoryDAO (); + } + + public function indexAction () { + $output = Minz_Request::param ('output'); + + $token = $this->view->conf->token(); + $token_param = Minz_Request::param ('token', ''); + $token_is_ok = ($token != '' && $token === $token_param); + + // check if user is log in + if(login_is_conf ($this->view->conf) && + !is_logged() && + $this->view->conf->anonAccess() === 'no' && + !($output === 'rss' && $token_is_ok)) { + return; + } + + // construction of RSS url of this feed + $params = Minz_Request::params (); + $params['output'] = 'rss'; + if (isset ($params['search'])) { + $params['search'] = urlencode ($params['search']); + } + if (login_is_conf($this->view->conf) && + $this->view->conf->anonAccess() === 'no' && + $token != '') { + $params['token'] = $token; + } + $this->view->rss_url = array ( + 'c' => 'index', + 'a' => 'index', + 'params' => $params + ); + + if ($output === 'rss') { + // no layout for RSS output + $this->view->_useLayout (false); + header('Content-Type: application/rss+xml; charset=utf-8'); + } else { + Minz_View::appendScript (Minz_Url::display ('/scripts/shortcut.js?' . @filemtime(PUBLIC_PATH . '/scripts/shortcut.js'))); + + if ($output === 'global') { + Minz_View::appendScript (Minz_Url::display ('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js'))); + } + } + + $this->view->cat_aside = $this->catDAO->listCategories (); + $this->view->nb_favorites = $this->entryDAO->countUnreadReadFavorites (); + $this->view->currentName = ''; + + $this->view->get_c = ''; + $this->view->get_f = ''; + + $get = Minz_Request::param ('get', 'a'); + $getType = $get[0]; + $getId = substr ($get, 2); + if (!$this->checkAndProcessType ($getType, $getId)) { + Minz_Log::record ('Not found [' . $getType . '][' . $getId . ']', Minz_Log::DEBUG); + Minz_Error::error ( + 404, + array ('error' => array (Minz_Translate::t ('page_not_found'))) + ); + return; + } + + $this->view->nb_not_read = HelperCategory::CountUnreads($this->view->cat_aside, 1); + + // mise à jour des titres + $this->view->rss_title = $this->view->currentName . ' | ' . Minz_View::title(); + if ($this->view->nb_not_read > 0) { + Minz_View::appendTitle (' (' . $this->view->nb_not_read . ')'); + } + Minz_View::prependTitle ( + $this->view->currentName . + ($this->nb_not_read_cat > 0 ? ' (' . $this->nb_not_read_cat . ')' : '') . + ' - ' + ); + + // On récupère les différents éléments de filtrage + $this->view->state = $state = Minz_Request::param ('state', $this->view->conf->defaultView ()); + $filter = Minz_Request::param ('search', ''); + if (!empty($filter)) { + $state = 'all'; //Search always in read and unread articles + } + $this->view->order = $order = Minz_Request::param ('order', $this->view->conf->sortOrder ()); + $nb = Minz_Request::param ('nb', $this->view->conf->postsPerPage ()); + $first = Minz_Request::param ('next', ''); + + if ($state === 'not_read') { //Any unread article in this category at all? + switch ($getType) { + case 'a': + $hasUnread = $this->view->nb_not_read > 0; + break; + case 's': + $hasUnread = $this->view->nb_favorites['unread'] > 0; + break; + case 'c': + $hasUnread = (!isset($this->view->cat_aside[$getId])) || ($this->view->cat_aside[$getId]->nbNotRead() > 0); + break; + case 'f': + $myFeed = HelperCategory::findFeed($this->view->cat_aside, $getId); + $hasUnread = ($myFeed === null) || ($myFeed->nbNotRead() > 0); + break; + default: + $hasUnread = true; + break; + } + if (!$hasUnread) { + $this->view->state = $state = 'all'; + } + } + + // on calcule la date des articles les plus anciens qu'on affiche + $nb_month_old = $this->view->conf->oldEntries (); + $date_min = time () - (3600 * 24 * 30 * $nb_month_old); + + try { + $entries = $this->entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min); + + // Si on a récupéré aucun article "non lus" + // on essaye de récupérer tous les articles + if ($state === 'not_read' && empty($entries)) { //TODO: Remove in v0.8 + Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG); + $this->view->state = 'all'; + $entries = $this->entryDAO->listWhere($getType, $getId, 'all', $order, $nb, $first, $filter, $date_min); + } + + if (count($entries) <= $nb) { + $this->view->nextId = ''; + } else { //We have more elements for pagination + $lastEntry = array_pop($entries); + $this->view->nextId = $lastEntry->id(); + } + + $this->view->entries = $entries; + } catch (FreshRSS_EntriesGetter_Exception $e) { + Minz_Log::record ($e->getMessage (), Minz_Log::NOTICE); + Minz_Error::error ( + 404, + array ('error' => array (Minz_Translate::t ('page_not_found'))) + ); + } + } + + /* + * Vérifie que la catégorie / flux sélectionné existe + * + Initialise correctement les variables de vue get_c et get_f + * + Met à jour la variable $this->nb_not_read_cat + */ + private function checkAndProcessType ($getType, $getId) { + switch ($getType) { + case 'a': + $this->view->currentName = Minz_Translate::t ('your_rss_feeds'); + $this->view->get_c = $getType; + return true; + case 's': + $this->view->currentName = Minz_Translate::t ('your_favorites'); + $this->view->get_c = $getType; + return true; + case 'c': + $cat = isset($this->view->cat_aside[$getId]) ? $this->view->cat_aside[$getId] : null; + if ($cat === null) { + $cat = $this->catDAO->searchById ($getId); + } + if ($cat) { + $this->view->currentName = $cat->name (); + $this->nb_not_read_cat = $cat->nbNotRead (); + $this->view->get_c = $getId; + return true; + } else { + return false; + } + case 'f': + $feed = HelperCategory::findFeed($this->view->cat_aside, $getId); + if (empty($feed)) { + $feed = $this->feedDAO->searchById ($getId); + } + if ($feed) { + $this->view->currentName = $feed->name (); + $this->nb_not_read_cat = $feed->nbNotRead (); + $this->view->get_f = $getId; + $this->view->get_c = $feed->category (); + return true; + } else { + return false; + } + default: + return false; + } + } + + public function aboutAction () { + Minz_View::prependTitle (Minz_Translate::t ('about') . ' - '); + } + + public function logsAction () { + if (login_is_conf ($this->view->conf) && !is_logged ()) { + Minz_Error::error ( + 403, + array ('error' => array (Minz_Translate::t ('access_denied'))) + ); + } + + Minz_View::prependTitle (Minz_Translate::t ('logs') . ' - '); + + if (Minz_Request::isPost ()) { + file_put_contents(LOG_PATH . '/application.log', ''); + } + + $logs = array(); + try { + $logDAO = new FreshRSS_LogDAO (); + $logs = $logDAO->lister (); + $logs = array_reverse ($logs); + } catch (Minz_FileNotExistException $e) { + + } + + //gestion pagination + $page = Minz_Request::param ('page', 1); + $this->view->logsPaginator = new Minz_Paginator ($logs); + $this->view->logsPaginator->_nbItemsPerPage (50); + $this->view->logsPaginator->_currentPage ($page); + } + + public function loginAction () { + $this->view->_useLayout (false); + + $url = 'https://verifier.login.persona.org/verify'; + $assert = Minz_Request::param ('assertion'); + $params = 'assertion=' . $assert . '&audience=' . + urlencode (Minz_Url::display (null, 'php', true)); + $ch = curl_init (); + $options = array ( + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => TRUE, + CURLOPT_POST => 2, + CURLOPT_POSTFIELDS => $params + ); + curl_setopt_array ($ch, $options); + $result = curl_exec ($ch); + curl_close ($ch); + + $res = json_decode ($result, true); + if ($res['status'] === 'okay' && $res['email'] === $this->view->conf->mailLogin ()) { + Minz_Session::_param ('mail', $res['email']); + invalidateHttpCache(); + } else { + $res = array (); + $res['status'] = 'failure'; + $res['reason'] = Minz_Translate::t ('invalid_login'); + } + + header('Content-Type: application/json; charset=UTF-8'); + $this->view->res = json_encode ($res); + } + + public function logoutAction () { + $this->view->_useLayout (false); + Minz_Session::_param ('mail'); + invalidateHttpCache(); + } +} -- cgit v1.2.3 From 529d6bcd15f7351cb7bdcf2f74c6a44930b0de55 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 16 Dec 2013 00:50:24 +0100 Subject: SQL : performances MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tentative de reformulation de la requête principale pour améliorer les performances. Utilisation d'une sous-jointure qui retourne uniquement e.id. Sur mon serveur avec 13000 articles, la requête de la page d'accueil sans article non lu mettait 1.38s avant le patch, contre 0.08s après (en désactivant bien sûr le cache SQL). Il faudra re-tester et tenter d'autres optimisations (notamment sur les index) avec un nombre d'articles plus important. Avant : SELECT SQL_NO_CACHE e.id, e.guid, e.title, e.author, UNCOMPRESS(e.content_bin) AS content, e.link, e.date, e.is_read, e.is_favorite, e.id_feed, e.tags FROM `freshrss_alex_entry` e INNER JOIN `freshrss_alex_feed` f ON e.id_feed = f.id WHERE f.priority > 0 AND (e.id >= 1371597014000000 OR e.is_favorite = 1 OR f.keep_history = 1) ORDER BY e.id DESC LIMIT 33; Après : SELECT SQL_NO_CACHE e.id, e.guid, e.title, e.author, UNCOMPRESS(e.content_bin) AS content, e.link, e.date, e.is_read, e.is_favorite, e.id_feed, e.tags FROM `freshrss_alex_entry` e INNER JOIN (SELECT e1.id FROM `freshrss_alex_entry` e1 INNER JOIN `freshrss_alex_feed` f ON e1.id_feed = f.id WHERE f.priority > 0 AND (e1.id >= 1371597014000000 OR e1.is_favorite = 1 OR f.keep_history = 1) ORDER BY e1.id DESC LIMIT 33) e2 ON e2.id = e.id ORDER BY e.id DESC; --- app/Controllers/indexController.php | 4 +++- app/Models/EntryDAO.php | 30 +++++++++++++++--------------- 2 files changed, 18 insertions(+), 16 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 92070590a..cc474302e 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -124,9 +124,11 @@ class FreshRSS_index_Controller extends Minz_ActionController { } } + $today = @strtotime('today'); + // on calcule la date des articles les plus anciens qu'on affiche $nb_month_old = $this->view->conf->oldEntries (); - $date_min = time () - (3600 * 24 * 30 * $nb_month_old); + $date_min = $today - (3600 * 24 * 30 * $nb_month_old); //Do not use a fast changing value such as time() to allow SQL caching try { $entries = $this->entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min); diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index f80fe9b77..5a34573db 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -269,7 +269,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $joinFeed = true; break; case 's': - $where .= 'e.is_favorite = 1 '; + $where .= 'e1.is_favorite = 1 '; break; case 'c': $where .= 'f.category = ? '; @@ -277,7 +277,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $joinFeed = true; break; case 'f': - $where .= 'e.id_feed = ? '; + $where .= 'e1.id_feed = ? '; $values[] = intval($id); break; default: @@ -287,10 +287,10 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { case 'all': break; case 'not_read': - $where .= 'AND e.is_read = 0 '; + $where .= 'AND e1.is_read = 0 '; break; case 'read': - $where .= 'AND e.is_read = 1 '; + $where .= 'AND e1.is_read = 1 '; break; default: throw new FreshRSS_EntriesGetter_Exception ('Bad state in Entry->listByType: [' . $state . ']!'); @@ -303,10 +303,10 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { throw new FreshRSS_EntriesGetter_Exception ('Bad order in Entry->listByType: [' . $order . ']!'); } if ($firstId !== '') { - $where .= 'AND e.id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' '; + $where .= 'AND e1.id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' '; } if (($date_min > 0) && ($type !== 's')) { - $where .= 'AND (e.id >= ' . $date_min . '000000 OR e.is_favorite = 1 OR f.keep_history = 1) '; + $where .= 'AND (e1.id >= ' . $date_min . '000000 OR e1.is_favorite = 1 OR f.keep_history = 1) '; $joinFeed = true; } $terms = array_unique(explode(' ', trim($filter))); @@ -315,10 +315,10 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { foreach ($terms as $word) { if (!empty($word)) { if ($word[0] === '#' && isset($word[1])) { - $having .= 'AND e.tags LIKE ? '; + $having .= 'AND e1.tags LIKE ? '; $values[] = '%' . $word .'%'; } elseif (!empty($word)) { - $having .= 'AND (e.title LIKE ? OR content LIKE ?) '; + $having .= 'AND (e1.title LIKE ? OR content LIKE ?) '; $values[] = '%' . $word .'%'; $values[] = '%' . $word .'%'; } @@ -327,15 +327,15 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $sql = 'SELECT e.id, e.guid, e.title, e.author, UNCOMPRESS(e.content_bin) AS content, e.link, e.date, e.is_read, e.is_favorite, e.id_feed, e.tags ' . 'FROM `' . $this->prefix . 'entry` e ' - . ($joinFeed ? 'INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' : '') - . 'WHERE ' . $where - . (empty($having) ? '' : 'HAVING' . substr($having, 3)) + . 'INNER JOIN (SELECT e1.id FROM `' . $this->prefix . 'entry` e1 ' + . ($joinFeed ? 'INNER JOIN `' . $this->prefix . 'feed` f ON e1.id_feed = f.id ' : '') + . 'WHERE ' . $where + . (empty($having) ? '' : 'HAVING' . substr($having, 3)) + . 'ORDER BY e1.id ' . $order + . ($limit > 0 ? ' LIMIT ' . $limit : '') //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ + . ') e2 ON e2.id = e.id ' . 'ORDER BY e.id ' . $order; - if ($limit > 0) { - $sql .= ' LIMIT ' . $limit; //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ - } - $stm = $this->bd->prepare ($sql); $stm->execute ($values); -- cgit v1.2.3 From 847de9b3292ad854b281d7e12cc36ac93e745139 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 16 Dec 2013 00:54:13 +0100 Subject: PHP : performances fonction isDay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Amélioration des performances de Entry->isDay() --- app/Controllers/indexController.php | 1 + app/Models/Entry.php | 27 +++++++++++++-------------- app/layout/nav_menu.phtml | 2 +- app/views/helpers/view/normal_view.phtml | 7 ++++--- 4 files changed, 19 insertions(+), 18 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index cc474302e..22c5683ea 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -125,6 +125,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { } $today = @strtotime('today'); + $this->view->today = $today; // on calcule la date des articles les plus anciens qu'on affiche $nb_month_old = $this->view->conf->oldEntries (); diff --git a/app/Models/Entry.php b/app/Models/Entry.php index ba0fb48f4..983f94727 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -134,21 +134,20 @@ class FreshRSS_Entry extends Minz_Model { $this->tags = $value; } - public function isDay ($day) { + public function isDay ($day, $today) { $date = $this->dateAdded(true); - $today = @strtotime('today'); - $yesterday = $today - 86400; - - if ($day === FreshRSS_Days::TODAY && - $date >= $today && $date < $today + 86400) { - return true; - } elseif ($day === FreshRSS_Days::YESTERDAY && - $date >= $yesterday && $date < $yesterday + 86400) { - return true; - } elseif ($day === FreshRSS_Days::BEFORE_YESTERDAY && $date < $yesterday) { - return true; - } else { - return false; + switch ($day) { + case FreshRSS_Days::TODAY: + $tomorrow = $today + 86400; + return $date >= $today && $date < $tomorrow; + case FreshRSS_Days::YESTERDAY: + $yesterday = $today - 86400; + return $date >= $yesterday && $date < $today; + case FreshRSS_Days::BEFORE_YESTERDAY: + $yesterday = $today - 86400; + return $date < $yesterday; + default: + return false; } } diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index 92a987aed..045f391f9 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -72,7 +72,7 @@
  • today; $one_week = $today - 604800; ?>
  • diff --git a/app/views/helpers/view/normal_view.phtml b/app/views/helpers/view/normal_view.phtml index 094017957..d5328651d 100644 --- a/app/views/helpers/view/normal_view.phtml +++ b/app/views/helpers/view/normal_view.phtml @@ -21,24 +21,25 @@ if (!empty($this->entries)) { $facebook = $this->conf->sharing ('facebook'); $email = $this->conf->sharing ('email'); $print = $this->conf->sharing ('print'); + $today = $this->today; ?> entries as $item) { ?> - isDay (FreshRSS_Days::TODAY)) { ?> + isDay (FreshRSS_Days::TODAY, $today)) { ?>
    - currentName; ?>
    - isDay (FreshRSS_Days::YESTERDAY)) { ?> + isDay (FreshRSS_Days::YESTERDAY, $today)) { ?>
    - currentName; ?>
    - isDay (FreshRSS_Days::BEFORE_YESTERDAY)) { ?> + isDay (FreshRSS_Days::BEFORE_YESTERDAY, $today)) { ?>
    currentName; ?> -- cgit v1.2.3 From 6b7d96d0ea97579720ee6d560224cd80c2329d07 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 19 Dec 2013 21:19:45 +0100 Subject: Refactorisation : correction classes oubliées MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/indexController.php | 6 +++--- app/Models/CategoryDAO.php | 16 +++++++--------- app/Models/EntryDAO.php | 8 +++----- app/Models/FeedDAO.php | 12 +++++------- app/views/helpers/view/normal_view.phtml | 2 +- app/views/helpers/view/reader_view.phtml | 2 +- 6 files changed, 20 insertions(+), 26 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 22c5683ea..e3c253518 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -77,7 +77,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { return; } - $this->view->nb_not_read = HelperCategory::CountUnreads($this->view->cat_aside, 1); + $this->view->nb_not_read = FreshRSS_CategoryDAO::CountUnreads($this->view->cat_aside, 1); // mise à jour des titres $this->view->rss_title = $this->view->currentName . ' | ' . Minz_View::title(); @@ -112,7 +112,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { $hasUnread = (!isset($this->view->cat_aside[$getId])) || ($this->view->cat_aside[$getId]->nbNotRead() > 0); break; case 'f': - $myFeed = HelperCategory::findFeed($this->view->cat_aside, $getId); + $myFeed = FreshRSS_CategoryDAO::findFeed($this->view->cat_aside, $getId); $hasUnread = ($myFeed === null) || ($myFeed->nbNotRead() > 0); break; default: @@ -188,7 +188,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { return false; } case 'f': - $feed = HelperCategory::findFeed($this->view->cat_aside, $getId); + $feed = FreshRSS_CategoryDAO::findFeed($this->view->cat_aside, $getId); if (empty($feed)) { $feed = $this->feedDAO->searchById ($getId); } diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index 793e593c3..3a810e9f0 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -60,7 +60,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { $stm->execute ($values); $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $cat = HelperCategory::daoToCategory ($res); + $cat = self::daoToCategory ($res); if (isset ($cat[0])) { return $cat[0]; @@ -76,7 +76,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { $stm->execute ($values); $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $cat = HelperCategory::daoToCategory ($res); + $cat = self::daoToCategory ($res); if (isset ($cat[0])) { return $cat[0]; @@ -96,12 +96,12 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { . 'ORDER BY c.name, f.name'; $stm = $this->bd->prepare ($sql); $stm->execute (); - return HelperCategory::daoToCategoryPrepopulated ($stm->fetchAll (PDO::FETCH_ASSOC)); + return self::daoToCategoryPrepopulated ($stm->fetchAll (PDO::FETCH_ASSOC)); } else { $sql = 'SELECT * FROM `' . $this->prefix . 'category` ORDER BY name'; $stm = $this->bd->prepare ($sql); $stm->execute (); - return HelperCategory::daoToCategory ($stm->fetchAll (PDO::FETCH_ASSOC)); + return self::daoToCategory ($stm->fetchAll (PDO::FETCH_ASSOC)); } } @@ -111,7 +111,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { $stm->execute (); $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $cat = HelperCategory::daoToCategory ($res); + $cat = self::daoToCategory ($res); if (isset ($cat[0])) { return $cat[0]; @@ -164,9 +164,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { return $res[0]['count']; } -} -class HelperCategory { public static function findFeed($categories, $feed_id) { foreach ($categories as $category) { foreach ($category->feeds () as $feed) { @@ -205,7 +203,7 @@ class HelperCategory { $cat = new FreshRSS_Category ( $previousLine['c_name'], isset($previousLine['c_color']) ? $previousLine['c_color'] : '', - HelperFeed::daoToFeed ($feedsDao, $previousLine['c_id']) + FreshRSS_FeedDAO::daoToFeed ($feedsDao, $previousLine['c_id']) ); $cat->_id ($previousLine['c_id']); $list[$previousLine['c_id']] = $cat; @@ -222,7 +220,7 @@ class HelperCategory { $cat = new FreshRSS_Category ( $previousLine['c_name'], isset($previousLine['c_color']) ? $previousLine['c_color'] : '', - HelperFeed::daoToFeed ($feedsDao, $previousLine['c_id']) + FreshRSS_FeedDAO::daoToFeed ($feedsDao, $previousLine['c_id']) ); $cat->_id ($previousLine['c_id']); $list[$previousLine['c_id']] = $cat; diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index c9989622a..d8bc869ae 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -242,7 +242,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $stm->execute ($values); $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $entries = HelperEntry::daoToEntry ($res); + $entries = self::daoToEntry ($res); return isset ($entries[0]) ? $entries[0] : false; } @@ -255,7 +255,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $stm->execute ($values); $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $entries = HelperEntry::daoToEntry ($res); + $entries = self::daoToEntry ($res); return isset ($entries[0]) ? $entries[0] : false; } @@ -372,7 +372,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $stm = $this->bd->prepare ($sql); $stm->execute ($values); - return HelperEntry::daoToEntry ($stm->fetchAll (PDO::FETCH_ASSOC)); + return self::daoToEntry ($stm->fetchAll (PDO::FETCH_ASSOC)); } public function listLastGuidsByFeed($id, $n) { @@ -430,9 +430,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $stm = $this->bd->prepare ($sql); $stm->execute (); } -} -class HelperEntry { public static function daoToEntry ($listDAO) { $list = array (); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 8f59b1c76..9ebea4a47 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -158,7 +158,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $stm->execute ($values); $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $feed = HelperFeed::daoToFeed ($res); + $feed = self::daoToFeed ($res); if (isset ($feed[$id])) { return $feed[$id]; @@ -174,7 +174,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $stm->execute ($values); $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $feed = current (HelperFeed::daoToFeed ($res)); + $feed = current (self::daoToFeed ($res)); if (isset ($feed)) { return $feed; @@ -188,7 +188,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $stm = $this->bd->prepare ($sql); $stm->execute (); - return HelperFeed::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); + return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); } public function listFeedsOrderUpdate () { @@ -196,7 +196,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $stm = $this->bd->prepare ($sql); $stm->execute (); - return HelperFeed::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); + return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); } public function listByCategory ($cat) { @@ -207,7 +207,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $stm->execute ($values); - return HelperFeed::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); + return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); } public function countEntries ($id) { @@ -299,9 +299,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return false; } } -} -class HelperFeed { public static function daoToFeed ($listDAO, $catID = null) { $list = array (); diff --git a/app/views/helpers/view/normal_view.phtml b/app/views/helpers/view/normal_view.phtml index d5328651d..f21a2bdd9 100644 --- a/app/views/helpers/view/normal_view.phtml +++ b/app/views/helpers/view/normal_view.phtml @@ -62,7 +62,7 @@ if (!empty($this->entries)) { ?>cat_aside, $item->feed ()); //We most likely already have the feed object in cache + $feed = FreshRSS_CategoryDAO::findFeed($this->cat_aside, $item->feed ()); //We most likely already have the feed object in cache if (empty($feed)) $feed = $item->feed (true); ?>
  • ✇ name(); ?>
  • diff --git a/app/views/helpers/view/reader_view.phtml b/app/views/helpers/view/reader_view.phtml index 29b2be04c..e28af6ade 100644 --- a/app/views/helpers/view/reader_view.phtml +++ b/app/views/helpers/view/reader_view.phtml @@ -11,7 +11,7 @@ if (!empty($this->entries)) {
    cat_aside, $item->feed ()); //We most likely already have the feed object in cache + $feed = FreshRSS_CategoryDAO::findFeed($this->cat_aside, $item->feed ()); //We most likely already have the feed object in cache if (empty($feed)) $feed = $item->feed (true); ?> -- cgit v1.2.3 From 06d4b8d10247146d9c6f7c78ff9fc584438dd8fe Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 25 Dec 2013 17:37:52 +0100 Subject: Option globale pour la taille minimale de l'historique par défaut MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plus une réorganisation des options --- app/Controllers/configureController.php | 5 ++- app/Controllers/feedController.php | 9 +++-- app/Controllers/indexController.php | 5 +-- app/Models/Configuration.php | 14 +++++++- app/Models/ConfigurationDAO.php | 8 +++-- app/Models/EntryDAO.php | 8 +++-- app/Models/Feed.php | 4 +-- app/Models/FeedDAO.php | 4 +-- app/i18n/en.php | 8 +++-- app/i18n/fr.php | 8 +++-- app/views/configure/display.phtml | 35 ++++++++++++------- app/views/configure/feed.phtml | 62 ++++++++++++++++++--------------- public/install.php | 4 +-- public/themes/default/freshrss.css | 4 +++ public/themes/default/global.css | 3 ++ public/themes/flat-design/freshrss.css | 4 +++ public/themes/flat-design/global.css | 3 ++ 17 files changed, 124 insertions(+), 64 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index b83501f0b..762134dd0 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -97,7 +97,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $description = sanitizeHTML(Minz_Request::param('description', '', true)); $website = Minz_Request::param('website', ''); $url = Minz_Request::param('url', ''); - $keep_history = intval(Minz_Request::param ('keep_history', 0)); + $keep_history = intval(Minz_Request::param ('keep_history', -2)); $cat = Minz_Request::param ('category', 0); $path = Minz_Request::param ('path_entries', ''); $priority = Minz_Request::param ('priority', 0); @@ -160,6 +160,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $lazyload = Minz_Request::param ('lazyload', 'no'); $sort = Minz_Request::param ('sort_order', 'DESC'); $old = Minz_Request::param ('old_entries', 3); + $keepHistoryDefault = Minz_Request::param('keep_history_default', 0); $mail = Minz_Request::param ('mail_login', false); $anon = Minz_Request::param ('anon_access', 'no'); $token = Minz_Request::param ('token', $current_token); @@ -189,6 +190,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->conf->_lazyload ($lazyload); $this->view->conf->_sortOrder ($sort); $this->view->conf->_oldEntries ($old); + $this->view->conf->_keepHistoryDefault($keepHistoryDefault); $this->view->conf->_mailLogin ($mail); $this->view->conf->_anonAccess ($anon); $this->view->conf->_token ($token); @@ -221,6 +223,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { 'lazyload' => $this->view->conf->lazyload (), 'sort_order' => $this->view->conf->sortOrder (), 'old_entries' => $this->view->conf->oldEntries (), + 'keep_history_default' => $this->view->conf->keepHistoryDefault(), 'mail_login' => $this->view->conf->mailLogin (), 'anon_access' => $this->view->conf->anonAccess (), 'token' => $this->view->conf->token (), diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index e7d9c97c3..836044da6 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -223,8 +223,13 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } - if (($feed->keepHistory() >= 0) && (rand(0, 30) === 1)) { - $nb = $feedDAO->cleanOldEntries ($feed->id (), $date_min, max($feed->keepHistory(), count($entries) + 10)); + $feedHistory = $feed->keepHistory(); + if ($feedHistory == -2) { //default + $feedHistory = $this->view->conf->keepHistoryDefault(); + } + + if (($feedHistory >= 0) && (rand(0, 30) === 1)) { + $nb = $feedDAO->cleanOldEntries ($feed->id (), $date_min, max($feedHistory, count($entries) + 10)); if ($nb > 0) { Minz_Log::record ($nb . ' old entries cleaned in feed ' . $feed->id (), Minz_Log::DEBUG); } diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index e3c253518..6c0ba9058 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -130,16 +130,17 @@ class FreshRSS_index_Controller extends Minz_ActionController { // on calcule la date des articles les plus anciens qu'on affiche $nb_month_old = $this->view->conf->oldEntries (); $date_min = $today - (3600 * 24 * 30 * $nb_month_old); //Do not use a fast changing value such as time() to allow SQL caching + $keepHistoryDefault = $this->view->conf->keepHistoryDefault(); try { - $entries = $this->entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min); + $entries = $this->entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min, $keepHistoryDefault); // Si on a récupéré aucun article "non lus" // on essaye de récupérer tous les articles if ($state === 'not_read' && empty($entries)) { //TODO: Remove in v0.8 Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG); $this->view->state = 'all'; - $entries = $this->entryDAO->listWhere($getType, $getId, 'all', $order, $nb, $first, $filter, $date_min); + $entries = $this->entryDAO->listWhere($getType, $getId, 'all', $order, $nb, $first, $filter, $date_min, $keepHistoryDefault); } if (count($entries) <= $nb) { diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index d5f69601f..47509636f 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -14,6 +14,7 @@ class FreshRSS_Configuration extends Minz_Model { private $lazyload; private $sort_order; private $old_entries; + private $keep_history_default; private $shortcuts = array (); private $mail_login = ''; private $mark_when = array (); @@ -44,6 +45,7 @@ class FreshRSS_Configuration extends Minz_Model { $this->_lazyload ($confDAO->lazyload); $this->_sortOrder ($confDAO->sort_order); $this->_oldEntries ($confDAO->old_entries); + $this->_keepHistoryDefault($confDAO->keep_history_default); $this->_shortcuts ($confDAO->shortcuts); $this->_mailLogin ($confDAO->mail_login); $this->_markWhen ($confDAO->mark_when); @@ -95,6 +97,9 @@ class FreshRSS_Configuration extends Minz_Model { public function oldEntries () { return $this->old_entries; } + public function keepHistoryDefault() { + return $this->keep_history_default; + } public function shortcuts () { return $this->shortcuts; } @@ -217,11 +222,18 @@ class FreshRSS_Configuration extends Minz_Model { } public function _oldEntries ($value) { if (ctype_digit ($value) && $value > 0) { - $this->old_entries = $value; + $this->old_entries = intval($value); } else { $this->old_entries = 3; } } + public function _keepHistoryDefault($value) { + if (ctype_digit($value) && $value >= -1) { + $this->keep_history_default = intval($value); + } else { + $this->keep_history_default = 0; + } + } public function _shortcuts ($values) { foreach ($values as $key => $value) { $this->shortcuts[$key] = $value; diff --git a/app/Models/ConfigurationDAO.php b/app/Models/ConfigurationDAO.php index 0eebf2d90..91210e701 100644 --- a/app/Models/ConfigurationDAO.php +++ b/app/Models/ConfigurationDAO.php @@ -10,6 +10,7 @@ class FreshRSS_ConfigurationDAO extends Minz_ModelArray { public $lazyload = 'yes'; public $sort_order = 'DESC'; public $old_entries = 3; + public $keep_history_default = 0; public $shortcuts = array ( 'mark_read' => 'r', 'mark_favorite' => 'f', @@ -62,7 +63,7 @@ class FreshRSS_ConfigurationDAO extends Minz_ModelArray { $this->language = $this->array['language']; } if (isset ($this->array['posts_per_page'])) { - $this->posts_per_page = $this->array['posts_per_page']; + $this->posts_per_page = intval($this->array['posts_per_page']); } if (isset ($this->array['view_mode'])) { $this->view_mode = $this->array['view_mode']; @@ -83,7 +84,10 @@ class FreshRSS_ConfigurationDAO extends Minz_ModelArray { $this->sort_order = $this->array['sort_order']; } if (isset ($this->array['old_entries'])) { - $this->old_entries = $this->array['old_entries']; + $this->old_entries = intval($this->array['old_entries']); + } + if (isset ($this->array['keep_history_default'])) { + $this->keep_history_default = intval($this->array['keep_history_default']); } if (isset ($this->array['shortcuts'])) { $this->shortcuts = array_merge ( diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index f0207e96d..14d3ddcff 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -260,7 +260,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return isset ($entries[0]) ? $entries[0] : false; } - public function listWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0) { + public function listWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $keepHistoryDefault = 0) { $where = ''; $joinFeed = false; $values = array(); @@ -307,7 +307,11 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $where .= 'AND e1.id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' '; } if (($date_min > 0) && ($type !== 's')) { - $where .= 'AND (e1.id >= ' . $date_min . '000000 OR e1.is_favorite = 1 OR f.keep_history <> 0) '; + $where .= 'AND (e1.id >= ' . $date_min . '000000 OR e1.is_favorite = 1 OR (f.keep_history <> 0'; + if (intval($keepHistoryDefault) === 0) { + $where .= ' AND f.keep_history <> -2'; //default + } + $where .= ')) '; $joinFeed = true; } $search = ''; diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 5bdf5e6d7..ef554e083 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -15,7 +15,7 @@ class FreshRSS_Feed extends Minz_Model { private $pathEntries = ''; private $httpAuth = ''; private $error = false; - private $keep_history = 0; + private $keep_history = -2; public function __construct ($url, $validate=true) { if ($validate) { @@ -168,7 +168,7 @@ class FreshRSS_Feed extends Minz_Model { public function _keepHistory ($value) { $value = intval($value); $value = min($value, 1000000); - $value = max($value, -1); + $value = max($value, -2); $this->keep_history = $value; } public function _nbNotRead ($value) { diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 7d91a032a..c1d1f24e8 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -2,7 +2,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { public function addFeed ($valuesTmp) { - $sql = 'INSERT INTO `' . $this->prefix . 'feed` (url, category, name, website, description, lastUpdate, priority, httpAuth, error, keep_history) VALUES(?, ?, ?, ?, ?, ?, 10, ?, 0, 0)'; + $sql = 'INSERT INTO `' . $this->prefix . 'feed` (url, category, name, website, description, lastUpdate, priority, httpAuth, error, keep_history) VALUES(?, ?, ?, ?, ?, ?, 10, ?, 0, -2)'; $stm = $this->bd->prepare ($sql); $values = array ( @@ -326,7 +326,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $myFeed->_pathEntries (isset($dao['pathEntries']) ? $dao['pathEntries'] : ''); $myFeed->_httpAuth (isset($dao['httpAuth']) ? base64_decode ($dao['httpAuth']) : ''); $myFeed->_error ($dao['error']); - $myFeed->_keepHistory (isset($dao['keep_history']) ? $dao['keep_history'] : 0); + $myFeed->_keepHistory(isset($dao['keep_history']) ? $dao['keep_history'] : -2); $myFeed->_nbNotRead ($dao['cache_nbUnreads']); $myFeed->_nbEntries ($dao['cache_nbEntries']); if (isset ($dao['id'])) { diff --git a/app/i18n/en.php b/app/i18n/en.php index b6417d8db..ca72d885c 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -137,8 +137,9 @@ return array ( 'feed_url' => 'Feed URL', 'articles' => 'articles', 'number_articles' => 'Number of articles', + 'by_feed' => 'by feed', + 'by_default' => 'By default', 'keep_history' => 'Minimum number of articles to keep', - 'keep_history_help' => 'Set to -1 to keep everything', 'categorize' => 'Store in a category', 'truncate' => 'Delete all articles', 'advanced' => 'Advanced', @@ -164,10 +165,11 @@ return array ( 'allow_anonymous' => 'Allow anonymous reading', 'auth_token' => 'Authentication token', 'explain_token' => 'Allows to access RSS output without authentication.
    %s?token=%s', - 'archiving_configuration' => 'Archiving configuration', + 'login_configuration' => 'Login', + 'archiving_configuration' => 'Archiving', 'delete_articles_every' => 'Remove articles after', 'archiving_configuration_help' => 'More options are available in the individual stream settings', - 'reading_configuration' => 'Reading configuration', + 'reading_configuration' => 'Reading', 'articles_per_page' => 'Number of articles per page', 'default_view' => 'Default view', 'sort_order' => 'Sort order', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index b27f29940..053a97c8a 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -137,8 +137,9 @@ return array ( 'feed_url' => 'URL du flux', 'articles' => 'articles', 'number_articles' => 'Nombre d’articles', + 'by_feed' => 'par flux', + 'by_default' => 'Par défaut', 'keep_history' => 'Nombre minimum d’articles à conserver', - 'keep_history_help' => 'Mettre à -1 pour tout conserver', 'categorize' => 'Ranger dans une catégorie', 'truncate' => 'Supprimer tous les articles', 'advanced' => 'Avancé', @@ -165,10 +166,11 @@ return array ( 'allow_anonymous' => 'Autoriser la lecture anonyme', 'auth_token' => 'Jeton d’identification', 'explain_token' => 'Permet d’accéder à la sortie RSS sans besoin de s’authentifier.
    %s?output=rss&token=%s', - 'archiving_configuration' => 'Configuration de l’archivage', + 'login_configuration' => 'Identification', + 'archiving_configuration' => 'Archivage', 'delete_articles_every' => 'Supprimer les articles après', 'archiving_configuration_help' => 'D’autres options sont disponibles dans la configuration individuelle des flux', - 'reading_configuration' => 'Configuration de lecture', + 'reading_configuration' => 'Lecture', 'articles_per_page' => 'Nombre d’articles par page', 'default_view' => 'Vue par défaut', 'sort_order' => 'Ordre de tri', diff --git a/app/views/configure/display.phtml b/app/views/configure/display.phtml index 8995dc839..3280f657f 100644 --- a/app/views/configure/display.phtml +++ b/app/views/configure/display.phtml @@ -31,6 +31,15 @@
    +
    +
    + + +
    +
    + + +
    conf->mailLogin (); ?> @@ -61,22 +70,22 @@
    - +

    +
    - - - - - - - - - - -
    - + +
    +
    +
    + +
    +
    diff --git a/app/views/configure/feed.phtml b/app/views/configure/feed.phtml index 5940055ed..e738ab64f 100644 --- a/app/views/configure/feed.phtml +++ b/app/views/configure/feed.phtml @@ -52,6 +52,15 @@
    +
    + +
    + +
    +
    @@ -78,16 +87,11 @@
    - - - - - - - - - - +
    @@ -97,24 +101,7 @@
    - -
    - -
    - -
    -
    -
    - -
    - - -
    -
    - + flux->httpAuth (false); ?>
    @@ -131,7 +118,24 @@
    - + + +
    +
    + + +
    + +
    + + +
    +
    + +
    +
    + +
    diff --git a/public/install.php b/public/install.php index 3885f143e..13abb010b 100644 --- a/public/install.php +++ b/public/install.php @@ -33,7 +33,7 @@ define ('SQL_FEED', 'CREATE TABLE IF NOT EXISTS `%1$sfeed` ( `pathEntries` varchar(511) DEFAULT NULL, `httpAuth` varchar(511) DEFAULT NULL, `error` boolean DEFAULT 0, - `keep_history` MEDIUMINT NOT NULL DEFAULT 0, + `keep_history` MEDIUMINT NOT NULL DEFAULT -2, -- v0.7, -2 = default `cache_nbEntries` int DEFAULT 0, -- v0.7 `cache_nbUnreads` int DEFAULT 0, -- v0.7 PRIMARY KEY (`id`), @@ -93,7 +93,7 @@ FROM `%1$scategory006` ORDER BY id2; INSERT IGNORE INTO `%2$sfeed` (url, category, name, website, description, priority, pathEntries, httpAuth, keep_history) -SELECT url, category2, name, website, description, priority, pathEntries, httpAuth, -1 * keep_history +SELECT url, category2, name, website, description, priority, pathEntries, httpAuth, IF(keep_history = 1, -1, -2) FROM `%1$sfeed006` ORDER BY id2; diff --git a/public/themes/default/freshrss.css b/public/themes/default/freshrss.css index e3c4c3c3b..2b157b27a 100644 --- a/public/themes/default/freshrss.css +++ b/public/themes/default/freshrss.css @@ -667,6 +667,10 @@ padding:.5em; } +select.number option { + text-align:right; +} + @media(max-width: 840px) { .header, .aside .btn-important, diff --git a/public/themes/default/global.css b/public/themes/default/global.css index 1c554d2dc..440fc6e41 100644 --- a/public/themes/default/global.css +++ b/public/themes/default/global.css @@ -99,6 +99,9 @@ input, select, textarea { vertical-align: middle; box-shadow: 0 2px 2px #eee inset; } + option { + padding:0 .5em 0 .5em; + } input[type="radio"], input[type="checkbox"] { width: 15px !important; diff --git a/public/themes/flat-design/freshrss.css b/public/themes/flat-design/freshrss.css index fa1ed13e6..7e3f4c81a 100644 --- a/public/themes/flat-design/freshrss.css +++ b/public/themes/flat-design/freshrss.css @@ -662,6 +662,10 @@ body { padding:.5em; } +select.number option { + text-align:right; +} + @media(max-width: 840px) { .header, .aside .btn-important, diff --git a/public/themes/flat-design/global.css b/public/themes/flat-design/global.css index 8cf6412b3..90b59d002 100644 --- a/public/themes/flat-design/global.css +++ b/public/themes/flat-design/global.css @@ -101,6 +101,9 @@ input, select, textarea { vertical-align: middle; border-radius: 5px; } + option { + padding:0 .5em 0 .5em; + } input[type="radio"], input[type="checkbox"] { width: 15px !important; -- cgit v1.2.3 From 9ac1496d63da32524a33696187342ce061e9ef28 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 28 Dec 2013 13:54:52 +0100 Subject: Bouge anon_access dans config.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L'accès anonyme n'est pas au niveau utilisateur mais au niveau global. Bouge FreshRSS_Configuration::conf->anonAccess() qui était stocké dans *_user.php vers Minz_Configuration::allowAnonymous() stocké dans config.php Contribue à https://github.com/marienfressinaud/FreshRSS/issues/126 + autres optimisations Contribue à https://github.com/marienfressinaud/FreshRSS/issues/260 --- app/Controllers/configureController.php | 11 ++++--- app/Controllers/indexController.php | 6 ++-- app/Models/Configuration.php | 12 ------- app/Models/ConfigurationDAO.php | 4 --- app/layout/header.phtml | 2 +- app/views/configure/users.phtml | 2 +- app/views/index/index.phtml | 2 +- data/.gitignore | 3 +- lib/Minz/Configuration.php | 58 ++++++++++++++++++++++++++------- 9 files changed, 61 insertions(+), 39 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 487f6e4ad..dd9674588 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -398,15 +398,13 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $current_token = $this->view->conf->token(); $mail = Minz_Request::param('mail_login', false); - $anon = Minz_Request::param('anon_access', 'no'); $token = Minz_Request::param('token', $current_token); + $this->view->conf->_mailLogin($mail); - $this->view->conf->_anonAccess($anon); $this->view->conf->_token($token); $values = array( 'mail_login' => $this->view->conf->mailLogin(), - 'anon_access' => $this->view->conf->anonAccess(), 'token' => $this->view->conf->token(), ); @@ -415,7 +413,12 @@ class FreshRSS_configure_Controller extends Minz_ActionController { Minz_Session::_param('conf', $this->view->conf); Minz_Session::_param('mail', $this->view->conf->mailLogin()); - // notif + $anon = (bool)(Minz_Request::param('anon_access', false)); + if ($anon != Minz_Configuration::allowAnonymous()) { + Minz_Configuration::_allowAnonymous($anon); + Minz_Configuration::writeFile(); + } + $notif = array( 'type' => 'good', 'content' => Minz_Translate::t('configuration_updated') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 6c0ba9058..0c229aedb 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -24,7 +24,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { // check if user is log in if(login_is_conf ($this->view->conf) && !is_logged() && - $this->view->conf->anonAccess() === 'no' && + !Minz_Configuration::allowAnonymous() && !($output === 'rss' && $token_is_ok)) { return; } @@ -36,8 +36,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { $params['search'] = urlencode ($params['search']); } if (login_is_conf($this->view->conf) && - $this->view->conf->anonAccess() === 'no' && - $token != '') { + !Minz_Configuration::allowAnonymous() && + $token !== '') { $params['token'] = $token; } $this->view->rss_url = array ( diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index cb2f90655..7f4be474d 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -20,7 +20,6 @@ class FreshRSS_Configuration extends Minz_Model { private $mark_when = array (); private $sharing = array (); private $theme; - private $anon_access; private $token; private $auto_load_more; private $topline_read; @@ -52,7 +51,6 @@ class FreshRSS_Configuration extends Minz_Model { $this->_sharing ($confDAO->sharing); $this->_theme ($confDAO->theme); FreshRSS_Themes::setThemeId ($confDAO->theme); - $this->_anonAccess ($confDAO->anon_access); $this->_token ($confDAO->token); $this->_autoLoadMore ($confDAO->auto_load_more); $this->_topline_read ($confDAO->topline_read); @@ -132,9 +130,6 @@ class FreshRSS_Configuration extends Minz_Model { public function theme () { return $this->theme; } - public function anonAccess () { - return $this->anon_access; - } public function token () { return $this->token; } @@ -283,13 +278,6 @@ class FreshRSS_Configuration extends Minz_Model { public function _theme ($value) { $this->theme = $value; } - public function _anonAccess ($value) { - if ($value == 'yes') { - $this->anon_access = 'yes'; - } else { - $this->anon_access = 'no'; - } - } public function _token ($value) { $this->token = $value; } diff --git a/app/Models/ConfigurationDAO.php b/app/Models/ConfigurationDAO.php index 91210e701..fa4d3338f 100644 --- a/app/Models/ConfigurationDAO.php +++ b/app/Models/ConfigurationDAO.php @@ -38,7 +38,6 @@ class FreshRSS_ConfigurationDAO extends Minz_ModelArray { 'print' => true ); public $theme = 'default'; - public $anon_access = 'no'; public $token = ''; public $auto_load_more = 'yes'; public $topline_read = 'yes'; @@ -108,9 +107,6 @@ class FreshRSS_ConfigurationDAO extends Minz_ModelArray { if (isset ($this->array['theme'])) { $this->theme = $this->array['theme']; } - if (isset ($this->array['anon_access'])) { - $this->anon_access = $this->array['anon_access']; - } if (isset ($this->array['token'])) { $this->token = $this->array['token']; } diff --git a/app/layout/header.phtml b/app/layout/header.phtml index bba22508e..aeb417a6e 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -21,7 +21,7 @@
    @@ -61,16 +61,16 @@
    @@ -79,9 +79,9 @@
    @@ -89,9 +89,9 @@
    @@ -99,9 +99,9 @@
    @@ -110,19 +110,19 @@
    @@ -132,7 +132,7 @@
    @@ -162,20 +162,20 @@ - conf->toplineRead () ? ' checked="checked"' : ''; ?> /> - conf->toplineFavorite () ? ' checked="checked"' : ''; ?> /> + conf->topline_read ? ' checked="checked"' : ''; ?> /> + conf->topline_favorite ? ' checked="checked"' : ''; ?> /> - conf->toplineDate () ? ' checked="checked"' : ''; ?> /> - conf->toplineLink () ? ' checked="checked"' : ''; ?> /> + conf->topline_date ? ' checked="checked"' : ''; ?> /> + conf->topline_link ? ' checked="checked"' : ''; ?> /> - conf->bottomlineRead () ? ' checked="checked"' : ''; ?> /> - conf->bottomlineFavorite () ? ' checked="checked"' : ''; ?> /> - conf->bottomlineSharing () ? ' checked="checked"' : ''; ?> /> - conf->bottomlineTags () ? ' checked="checked"' : ''; ?> /> - conf->bottomlineDate () ? ' checked="checked"' : ''; ?> /> - conf->bottomlineLink () ? ' checked="checked"' : ''; ?> /> + conf->bottomline_read ? ' checked="checked"' : ''; ?> /> + conf->bottomline_favorite ? ' checked="checked"' : ''; ?> /> + conf->bottomline_sharing ? ' checked="checked"' : ''; ?> /> + conf->bottomline_tags ? ' checked="checked"' : ''; ?> /> + conf->bottomline_date ? ' checked="checked"' : ''; ?> /> + conf->bottomline_link ? ' checked="checked"' : ''; ?> />
    diff --git a/app/views/configure/sharing.phtml b/app/views/configure/sharing.phtml index 825537fc9..c6a96b48a 100644 --- a/app/views/configure/sharing.phtml +++ b/app/views/configure/sharing.phtml @@ -47,7 +47,7 @@ foreach ($services as $service) { ?> diff --git a/app/views/configure/shortcut.phtml b/app/views/configure/shortcut.phtml index e78d91820..2e564a7b6 100644 --- a/app/views/configure/shortcut.phtml +++ b/app/views/configure/shortcut.phtml @@ -9,7 +9,7 @@ - conf->shortcuts (); ?> + conf->shortcuts; ?> diff --git a/app/views/configure/users.phtml b/app/views/configure/users.phtml index c57671ef3..4fd291ba3 100644 --- a/app/views/configure/users.phtml +++ b/app/views/configure/users.phtml @@ -20,7 +20,7 @@
    - conf->mailLogin(); ?> + conf->mail_login; ?>
    @@ -29,7 +29,7 @@
    - conf->token(); ?> + conf->token; ?>
    @@ -51,7 +51,7 @@
    diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml index d008e2e48..8f508487c 100644 --- a/app/views/helpers/javascript_vars.phtml +++ b/app/views/helpers/javascript_vars.phtml @@ -1,16 +1,16 @@ conf->markWhen (); + $mark = $this->conf->mark_when; echo 'var ', - 'hide_posts=', ($this->conf->displayPosts () === 'yes' || Minz_Request::param ('output') === 'reader') ? 'false' : 'true', - ',auto_mark_article=', $mark['article'] === 'yes' ? 'true' : 'false', - ',auto_mark_site=', $mark['site'] === 'yes' ? 'true' : 'false', - ',auto_mark_scroll=', $mark['scroll'] === 'yes' ? 'true' : 'false', - ',auto_load_more=', $this->conf->autoLoadMore () === 'yes' ? 'true' : 'false', - ',full_lazyload=', $this->conf->lazyload () === 'yes' && ($this->conf->displayPosts () === 'yes' || Minz_Request::param ('output') === 'reader') ? 'true' : 'false', - ',does_lazyload=', $this->conf->lazyload() === 'yes' ? 'true' : 'false'; + 'hide_posts=', ($this->conf->display_posts || Minz_Request::param('output') === 'reader') ? 'false' : 'true', + ',auto_mark_article=', $mark['article'] ? 'true' : 'false', + ',auto_mark_site=', $mark['site'] ? 'true' : 'false', + ',auto_mark_scroll=', $mark['scroll'] ? 'true' : 'false', + ',auto_load_more=', $this->conf->auto_load_more ? 'true' : 'false', + ',full_lazyload=', $this->conf->lazyload && ($this->conf->display_posts || Minz_Request::param('output') === 'reader') ? 'true' : 'false', + ',does_lazyload=', $this->conf->lazyload ? 'true' : 'false'; - $s = $this->conf->shortcuts (); + $s = $this->conf->shortcuts; echo ',shortcuts={', 'mark_read:"', $s['mark_read'], '",', 'mark_favorite:"', $s['mark_favorite'], '",', diff --git a/app/views/helpers/view/global_view.phtml b/app/views/helpers/view/global_view.phtml index bc6e24e37..58ff13d4e 100644 --- a/app/views/helpers/view/global_view.phtml +++ b/app/views/helpers/view/global_view.phtml @@ -31,6 +31,6 @@
    -
    conf->displayPosts () === 'no' ? ' class="hide_posts"' : ''; ?>> +
    conf->display_posts ? '' : ' class="hide_posts"'; ?>> -
    \ No newline at end of file +
    diff --git a/app/views/helpers/view/normal_view.phtml b/app/views/helpers/view/normal_view.phtml index 4307c2113..f59cae2b8 100644 --- a/app/views/helpers/view/normal_view.phtml +++ b/app/views/helpers/view/normal_view.phtml @@ -18,8 +18,8 @@ if (!empty($this->entries)) { $email = $this->conf->sharing ('email'); $print = $this->conf->sharing ('print'); $today = $this->today; - $hidePosts = $this->conf->displayPosts() === 'no'; - $lazyload = $this->conf->lazyload() === 'yes'; + $hidePosts = !$this->conf->display_posts; + $lazyload = $this->conf->lazyload; ?>
    @@ -49,13 +49,13 @@ if (!empty($this->entries)) {
    @@ -86,13 +86,13 @@ if (!empty($this->entries)) {
      conf->bottomlineRead ()) { + if ($this->conf->bottomline_read) { ?>
    • isRead () ? 'read' : 'unread'); ?>
    • conf->bottomlineFavorite ()) { + if ($this->conf->bottomline_favorite) { ?>
    • isFavorite () ? 'starred' : 'non-starred'); ?>entries)) { } ?>
    • conf->bottomlineSharing () && ( + if ($this->conf->bottomline_sharing && ( $shaarli || $poche || $diaspora || $twitter || $google_plus || $facebook || $email )) { @@ -171,7 +171,7 @@ if (!empty($this->entries)) {
    • conf->bottomlineTags () ? $item->tags() : null; + $tags = $this->conf->bottomline_tags ? $item->tags() : null; if (!empty($tags)) { ?>
    • @@ -190,8 +190,8 @@ if (!empty($this->entries)) {
    - conf->bottomlineDate ()) { ?>
  • date (); ?> 
  • - conf->bottomlineLink ()) { ?> + conf->bottomline_date) { ?>
  • date (); ?> 
  • + conf->bottomline_link) { ?>
    diff --git a/app/views/helpers/view/reader_view.phtml b/app/views/helpers/view/reader_view.phtml index 47254f74e..2f64e672a 100644 --- a/app/views/helpers/view/reader_view.phtml +++ b/app/views/helpers/view/reader_view.phtml @@ -2,7 +2,7 @@ $this->partial ('nav_menu'); if (!empty($this->entries)) { - $lazyload = $this->conf->lazyload() === 'yes'; + $lazyload = $this->conf->lazyload; ?>
    diff --git a/app/views/index/index.phtml b/app/views/index/index.phtml index 2d134ba4e..4db53e2a5 100644 --- a/app/views/index/index.phtml +++ b/app/views/index/index.phtml @@ -1,7 +1,7 @@ conf->token(); +$token = $this->conf->token; $token_param = Minz_Request::param ('token', ''); $token_is_ok = ($token != '' && $token == $token_param); diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index 306328904..3864a9335 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -225,14 +225,14 @@ class Minz_Configuration { } } if (isset ($general['delay_cache'])) { - self::$delay_cache = $general['delay_cache']; + self::$delay_cache = inval($general['delay_cache']); } if (isset ($general['default_user'])) { self::$default_user = $general['default_user']; self::$current_user = self::$default_user; } if (isset ($general['allow_anonymous'])) { - self::$allow_anonymous = (bool)($general['allow_anonymous']); + self::$allow_anonymous = ((bool)($general['allow_anonymous'])) && ($general['allow_anonymous'] !== 'no'); } // Base de données diff --git a/lib/Minz/ModelArray.php b/lib/Minz/ModelArray.php index 89d7f06c1..e3ec77dc9 100644 --- a/lib/Minz/ModelArray.php +++ b/lib/Minz/ModelArray.php @@ -8,11 +8,6 @@ * La classe Model_array représente le modèle interragissant avec les fichiers de type texte gérant des tableaux php */ class Minz_ModelArray { - /** - * $array Le tableau php contenu dans le fichier $filename - */ - protected $array = array (); - /** * $filename est le nom du fichier */ @@ -25,29 +20,32 @@ class Minz_ModelArray { */ public function __construct ($filename) { $this->filename = $filename; + } + protected function loadArray() { if (!file_exists($this->filename)) { throw new Minz_FileNotExistException($this->filename, Minz_Exception::WARNING); } elseif (($handle = $this->getLock()) === false) { throw new Minz_PermissionDeniedException($this->filename); } else { - $this->array = include($this->filename); + $data = include($this->filename); $this->releaseLock($handle); - if ($this->array === false) { + if ($data === false) { throw new Minz_PermissionDeniedException($this->filename); - } elseif (!is_array($this->array)) { - $this->array = array(); + } elseif (!is_array($data)) { + $data = array(); } + return $data; } } /** * Sauve le tableau $array dans le fichier $filename **/ - protected function writeFile() { - if (!file_put_contents($this->filename, "array, true) . ';', LOCK_EX)) { + protected function writeArray($array) { + if (!file_put_contents($this->filename, "filename); } return true; diff --git a/lib/Minz/Session.php b/lib/Minz/Session.php index f527322f5..6e45fd226 100644 --- a/lib/Minz/Session.php +++ b/lib/Minz/Session.php @@ -55,11 +55,6 @@ class Minz_Session { } else { $_SESSION[$p] = $v; self::$session[$p] = $v; - - if($p == 'language') { - // reset pour remettre à jour le fichier de langue à utiliser - Minz_Translate::reset (); - } } } @@ -76,6 +71,7 @@ class Minz_Session { if (!$force) { self::_param ('language', $language); + Minz_Translate::reset (); } } } diff --git a/lib/lib_rss.php b/lib/lib_rss.php index a27ef171a..3f55c7d58 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -63,7 +63,7 @@ function is_logged () { // vérifie que le système d'authentification est configuré function login_is_conf ($conf) { - return $conf->mailLogin () != false; + return $conf->mail_login != ''; } // tiré de Shaarli de Seb Sauvage //Format RFC 4648 base64url -- cgit v1.2.3 From 92efd68a3a13e49fe7bbfb8441611c0dcd639415 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 30 Dec 2013 01:03:32 +0100 Subject: Début de mode multi-utilisateur avec http_auth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit + Légère optimisation de Minz_View. + Encore plus de tests de bibliothèques dans install.php Contribue à https://github.com/marienfressinaud/FreshRSS/issues/126 et https://github.com/marienfressinaud/FreshRSS/issues/303 --- README.md | 2 +- app/Controllers/configureController.php | 5 +-- app/Controllers/entryController.php | 4 +-- app/Controllers/feedController.php | 27 +++++++------- app/Controllers/indexController.php | 31 ++++++++--------- app/FreshRSS.php | 56 ++++++++++++++++++++++------- app/Models/Configuration.php | 15 ++++---- app/actualize_script.php | 15 +++++--- app/i18n/en.php | 5 +-- app/i18n/fr.php | 5 +-- app/layout/aside_flux.phtml | 6 ++-- app/layout/header.phtml | 21 +++++------ app/layout/nav_menu.phtml | 2 +- app/views/configure/users.phtml | 51 +++++++++++++++------------ app/views/helpers/javascript_vars.phtml | 2 +- app/views/helpers/view/normal_view.phtml | 60 ++++++++++++++++++-------------- app/views/index/index.phtml | 45 ++++++++++++++---------- lib/Minz/Configuration.php | 12 ++++++- lib/Minz/View.php | 29 ++++++--------- lib/lib_rss.php | 16 +++------ p/i/install.php | 24 ++++++++++--- 21 files changed, 246 insertions(+), 187 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/README.md b/README.md index f20f870dd..cfef89781 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Privilégiez pour cela des demandes sur GitHub # Pré-requis * Serveur Apache2 ou Nginx (non testé sur les autres) * PHP 5.2+ (PHP 5.3.3+ recommandé) - * Requis : [LibXML](http://php.net/xml), [PCRE](http://php.net/pcre), [cURL](http://php.net/curl), [PDO_MySQL](http://php.net/pdo-mysql) + * Requis : [PDO_MySQL](http://php.net/pdo-mysql), [cURL](http://php.net/curl), [LibXML](http://php.net/xml), [PCRE](http://php.net/pcre), [ctype](http://php.net/ctype) * Recommandés : [JSON](http://php.net/json), [zlib](http://php.net/zlib), [mbstring](http://php.net/mbstring), [iconv](http://php.net/iconv) * MySQL 5.0.3+ (ou SQLite 3.7.4+ à venir) * Un navigateur Web récent tel Firefox, Chrome, Opera, Safari, Internet Explorer 9+ diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 0c0b4951d..656e2ac89 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -2,7 +2,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { public function firstAction () { - if (login_is_conf ($this->view->conf) && !is_logged ()) { + if (!$this->view->loginOk) { Minz_Error::error ( 403, array ('error' => array (Minz_Translate::t ('access_denied'))) @@ -16,7 +16,6 @@ class FreshRSS_configure_Controller extends Minz_ActionController { public function categorizeAction () { $feedDAO = new FreshRSS_FeedDAO (); $catDAO = new FreshRSS_CategoryDAO (); - $catDAO->checkDefault (); $defaultCategory = $catDAO->getDefault (); $defaultId = $defaultCategory->id (); @@ -167,8 +166,6 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->conf->_bottomline_link(Minz_Request::param('bottomline_link', false)); $this->view->conf->save(); - Minz_Session::_param ('mail', $this->view->conf->mail_login); - Minz_Session::_param ('language', $this->view->conf->language); Minz_Translate::reset (); diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index b0fc37cdf..da4ab5ecc 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -2,7 +2,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { public function firstAction () { - if (login_is_conf ($this->view->conf) && !is_logged ()) { + if (!$this->view->loginOk) { Minz_Error::error ( 403, array ('error' => array (Minz_Translate::t ('access_denied'))) @@ -38,7 +38,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $nextGet = Minz_Request::param ('nextGet', $get); $idMax = Minz_Request::param ('idMax', 0); - $is_read = !!$is_read; + $is_read = (bool)$is_read; $entryDAO = new FreshRSS_EntryDAO (); if ($id == false) { diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 42a0dcb11..2d7c0ab43 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -2,18 +2,17 @@ class FreshRSS_feed_Controller extends Minz_ActionController { public function firstAction () { - $token = $this->view->conf->token; - $token_param = Minz_Request::param ('token', ''); - $token_is_ok = ($token != '' && $token == $token_param); - $action = Minz_Request::actionName (); - - if (login_is_conf ($this->view->conf) && - !is_logged () && - !($token_is_ok && $action == 'actualize')) { - Minz_Error::error ( - 403, - array ('error' => array (Minz_Translate::t ('access_denied'))) - ); + if (!$this->view->loginOk) { + $token = $this->view->conf->token; //TODO: check the token logic again, and if it is still needed + $token_param = Minz_Request::param ('token', ''); + $token_is_ok = ($token != '' && $token == $token_param); + $action = Minz_Request::actionName (); + if (!($token_is_ok && $action === 'actualize')) { + Minz_Error::error ( + 403, + array ('error' => array (Minz_Translate::t ('access_denied'))) + ); + } } $this->catDAO = new FreshRSS_CategoryDAO (); @@ -411,10 +410,8 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } private function addCategories ($categories) { - $catDAO = new FreshRSS_CategoryDAO (); - foreach ($categories as $cat) { - if (!$catDAO->searchByName ($cat->name ())) { + if (!$this->catDAO->searchByName ($cat->name ())) { $values = array ( 'id' => $cat->id (), 'name' => $cat->name (), diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 54826636f..66809964d 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -16,17 +16,18 @@ class FreshRSS_index_Controller extends Minz_ActionController { public function indexAction () { $output = Minz_Request::param ('output'); - - $token = $this->view->conf->token; - $token_param = Minz_Request::param ('token', ''); - $token_is_ok = ($token != '' && $token === $token_param); - - // check if user is log in - if(login_is_conf ($this->view->conf) && - !is_logged() && - !Minz_Configuration::allowAnonymous() && - !($output === 'rss' && $token_is_ok)) { - return; + $token = ''; + + // check if user is logged in + if (!$this->view->loginOk && !Minz_Configuration::allowAnonymous()) + { + $token = $this->view->conf->token; + $token_param = Minz_Request::param ('token', ''); + $token_is_ok = ($token != '' && $token === $token_param); + if (!($output === 'rss' && $token_is_ok)) { + return; + } + $params['token'] = $token; } // construction of RSS url of this feed @@ -35,11 +36,6 @@ class FreshRSS_index_Controller extends Minz_ActionController { if (isset ($params['search'])) { $params['search'] = urlencode ($params['search']); } - if (login_is_conf($this->view->conf) && - !Minz_Configuration::allowAnonymous() && - $token !== '') { - $params['token'] = $token; - } $this->view->rss_url = array ( 'c' => 'index', 'a' => 'index', @@ -212,7 +208,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { } public function logsAction () { - if (login_is_conf ($this->view->conf) && !is_logged ()) { + if (!$this->view->loginOk) { Minz_Error::error ( 403, array ('error' => array (Minz_Translate::t ('access_denied'))) @@ -255,6 +251,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { $res = json_decode ($result, true); if ($res['status'] === 'okay' && $res['email'] === $this->view->conf->mail_login) { Minz_Session::_param ('mail', $res['email']); + $this->view->loginOk = true; invalidateHttpCache(); } else { $res = array (); diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 05c8ec8e0..10f362717 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -1,26 +1,56 @@ loadParamsView (); - $this->loadStylesAndScripts (); - $this->loadNotifications (); + public function init($currentUser = null) { + Minz_Session::init('FreshRSS'); + $this->accessControl($currentUser); + $this->loadParamsView(); + $this->loadStylesAndScripts(); + $this->loadNotifications(); } - private function loadParamsView () { + private function accessControl($currentUser) { + if ($currentUser === null) { + switch (Minz_Configuration::authType()) { + case 'http_auth': + $currentUser = httpAuthUser(); + $loginOk = $currentUser != ''; + break; + case 'persona': + $currentUser = Minz_Configuration::defaultUser(); + $loginOk = Minz_Session::param('mail') != ''; + break; + case 'none': + $currentUser = Minz_Configuration::defaultUser(); + $loginOk = true; + break; + default: + $loginOk = false; + break; + } + } elseif ((PHP_SAPI === 'cli') && (Minz_Request::actionName() === 'actualize')) { //Command line + Minz_Configuration::_authType('none'); + $loginOk = true; + } + + if (!$loginOk || !isValidUser($currentUser)) { + $currentUser = Minz_Configuration::defaultUser(); + $loginOk = false; + } + Minz_Configuration::_currentUser($currentUser); + Minz_View::_param ('loginOk', $loginOk); + try { - $this->conf = new FreshRSS_Configuration(); + $this->conf = new FreshRSS_Configuration($currentUser); } catch (Minz_Exception $e) { // Permission denied or conf file does not exist - // it's critical! die($e->getMessage()); } - Minz_View::_param ('conf', $this->conf); + } + + private function loadParamsView () { Minz_Session::_param ('language', $this->conf->language); Minz_Translate::init(); - $output = Minz_Request::param ('output'); if (!$output) { $output = $this->conf->view_mode; @@ -31,12 +61,12 @@ class FreshRSS extends Minz_FrontController { private function loadStylesAndScripts () { $theme = FreshRSS_Themes::get_infos($this->conf->theme); if ($theme) { - foreach($theme["files"] as $file) { + foreach($theme['files'] as $file) { Minz_View::appendStyle (Minz_Url::display ('/themes/' . $theme['path'] . '/' . $file . '?' . @filemtime(PUBLIC_PATH . '/themes/' . $theme['path'] . '/' . $file))); } } - if (login_is_conf ($this->conf)) { + if (Minz_Configuration::authType() === 'persona') { Minz_View::appendScript ('https://login.persona.org/include.js'); } $includeLazyLoad = $this->conf->lazyload && ($this->conf->display_posts || Minz_Request::param ('output') === 'reader'); diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index b0a5d9940..ec7daaa7d 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -59,10 +59,9 @@ class FreshRSS_Configuration extends Minz_ModelArray { 'fr' => 'Français', ); - public function __construct ($filename = '') { - if (empty($filename)) { - $filename = DATA_PATH . '/' . Minz_Configuration::currentUser () . '_user.php'; - } + public function __construct ($user) { + $filename = DATA_PATH . '/' . $user . '_user.php'; + parent::__construct($filename); $data = parent::loadArray(); @@ -72,6 +71,7 @@ class FreshRSS_Configuration extends Minz_ModelArray { $this->$function($value); } } + $this->data['user'] = $user; } public function save() { @@ -151,10 +151,11 @@ class FreshRSS_Configuration extends Minz_ModelArray { } } public function _mail_login ($value) { - if (filter_var($value, FILTER_VALIDATE_EMAIL)) { - $this->mail_login = $value; + $value = filter_var($value, FILTER_VALIDATE_EMAIL); + if ($value) { + $this->data['mail_login'] = $value; } else { - $this->mail_login = ''; + $this->data['mail_login'] = ''; } } public function _anon_access ($value) { diff --git a/app/actualize_script.php b/app/actualize_script.php index 20438128a..e0c560ff7 100755 --- a/app/actualize_script.php +++ b/app/actualize_script.php @@ -1,6 +1,8 @@ init (); -Minz_Session::_param('mail', true); // permet de se passer de la phase de connexion -$front_controller->run (); -invalidateHttpCache(); + +$users = listUsers(); +shuffle($users); + +foreach ($users as $user) { + $front_controller->init($user); + $front_controller->run(); + invalidateHttpCache($user); +} diff --git a/app/i18n/en.php b/app/i18n/en.php index 65afc11e5..8b9eee548 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -158,13 +158,14 @@ return array ( 'current_user' => 'Current user', 'default_user' => 'Username of the default user (maximum 16 alphanumeric characters)', - 'persona_connection_email' => 'Login mail address (use Mozilla Persona)', + 'persona_connection_email' => 'Login mail address (for Mozilla Persona)', 'allow_anonymous' => 'Allow anonymous reading for the default user (%s)', 'auth_token' => 'Authentication token', - 'explain_token' => 'Allows to access RSS output without authentication.
    %s?token=%s', + 'explain_token' => 'Allows to access RSS output of the default user without authentication.
    %s?token=%s', 'login_configuration' => 'Login', 'is_admin' => 'is administrator', 'auth_type' => 'Authentication method', + 'auth_none' => 'None (dangerous)', 'users_list' => 'List of users', 'language' => 'Language', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index adc38acbe..cad156d47 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -158,13 +158,14 @@ return array ( 'current_user' => 'Utilisateur actuel', 'default_user' => 'Nom de l’utilisateur par défaut (16 caractères alphanumériques maximum)', - 'persona_connection_email' => 'Adresse courriel de connexion (utilise Mozilla Persona)', + 'persona_connection_email' => 'Adresse courriel de connexion (pour Mozilla Persona)', 'allow_anonymous' => 'Autoriser la lecture anonyme pour l’utilisateur par défaut (%s)', 'auth_token' => 'Jeton d’identification', - 'explain_token' => 'Permet d’accéder à la sortie RSS sans besoin de s’authentifier.
    %s?output=rss&token=%s', + 'explain_token' => 'Permet d’accéder à la sortie RSS de l’utilisateur par défaut sans besoin de s’authentifier.
    %s?output=rss&token=%s', 'login_configuration' => 'Identification', 'is_admin' => 'est administrateur', 'auth_type' => 'Méthode d’authentification', + 'auth_none' => 'Aucune (dangereux)', 'users_list' => 'Liste des utilisateurs', 'language' => 'Langue', diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml index 9a6b16d58..8730baf0e 100644 --- a/app/layout/aside_flux.phtml +++ b/app/layout/aside_flux.phtml @@ -2,14 +2,14 @@
      - conf) || is_logged ()) { ?> + loginOk) { ?>
    • - conf)) { ?> +
    • @@ -69,7 +69,7 @@
    • - conf) || is_logged ()) { ?> + loginOk) { ?>
    • diff --git a/app/layout/header.phtml b/app/layout/header.phtml index aeb417a6e..0f2c524c4 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -1,9 +1,9 @@ -conf)) { ?> + @@ -19,9 +19,7 @@
    @@ -91,7 +91,7 @@
    @@ -101,7 +101,7 @@ diff --git a/app/views/helpers/view/normal_view.phtml b/app/views/helpers/view/normal_view.phtml index 5e46f3c8e..7e4457ecc 100644 --- a/app/views/helpers/view/normal_view.phtml +++ b/app/views/helpers/view/normal_view.phtml @@ -43,14 +43,14 @@ if (!empty($this->entries)) { isDay (FreshRSS_Days::TODAY, $this->today)) { ?>
    - - + currentName; ?>
    isDay (FreshRSS_Days::YESTERDAY, $this->today)) { ?>
    - - + currentName; ?>
    @@ -118,7 +118,7 @@ if (!empty($this->entries)) { link ()); - $title = urlencode ($item->title () . ' - ' . $feed->name ()); + $title = urlencode ($item->title () . ' · ' . $feed->name ()); ?> diff --git a/app/views/configure/users.phtml b/app/views/configure/users.phtml index 68111bdbe..1597004e1 100644 --- a/app/views/configure/users.phtml +++ b/app/views/configure/users.phtml @@ -20,7 +20,7 @@
    - +
    @@ -52,11 +52,11 @@
    - $_SERVER['REMOTE_USER'] = ``
    @@ -141,6 +141,14 @@ +
    + +
    + + +
    +
    +
    conf->mail_login; ?> diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml index 92c068f7e..935294e60 100644 --- a/app/views/helpers/javascript_vars.phtml +++ b/app/views/helpers/javascript_vars.phtml @@ -30,8 +30,8 @@ if ($mail != 'null') { $mail = '"' . $mail . '"'; } - echo 'use_persona=', Minz_Configuration::authType() === 'persona' ? 'true' : 'false', - ',url_freshrss="', _url ('index', 'index'), '",', + echo 'authType="', Minz_Configuration::authType(), '",', + 'url_freshrss="', _url ('index', 'index'), '",', 'url_login="', _url ('index', 'login'), '",', 'url_logout="', _url ('index', 'logout'), '",', 'current_user_mail=', $mail, ",\n"; diff --git a/app/views/helpers/view/login.phtml b/app/views/helpers/view/login.phtml new file mode 100644 index 000000000..e4a24f9ed --- /dev/null +++ b/app/views/helpers/view/login.phtml @@ -0,0 +1,43 @@ +
    + +

    +
    + +
    + +
    +
    +
    + +
    + + + +
    +
    +
    +
    + +
    +
    +

    FreshRSS

    +

    + +

    +
    diff --git a/app/views/index/index.phtml b/app/views/index/index.phtml index 549d0b61e..9b69233e9 100644 --- a/app/views/index/index.phtml +++ b/app/views/index/index.phtml @@ -1,15 +1,5 @@
    -

    -

    -

    -
    loginOk || Minz_Configuration::allowAnonymous()) { @@ -31,8 +21,8 @@ if ($this->loginOk || Minz_Configuration::allowAnonymous()) { if ($token_is_ok) { $this->renderHelper ('view/rss_view'); } else { - showForbidden(); + $this->renderHelper ('view/login'); } } else { - showForbidden(); + $this->renderHelper ('view/login'); } diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index 2c30661ed..433992e0d 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -109,7 +109,7 @@ class Minz_Configuration { return self::$auth_type !== 'none'; } public static function canLogIn() { - return self::$auth_type === 'persona'; + return self::$auth_type === 'form' || self::$auth_type === 'persona'; } public static function _allowAnonymous($allow = false) { @@ -118,6 +118,7 @@ class Minz_Configuration { public static function _authType($value) { $value = strtolower($value); switch ($value) { + case 'form': case 'http_auth': case 'persona': case 'none': diff --git a/lib/Minz/FrontController.php b/lib/Minz/FrontController.php index 7b8526bc8..80eda8877 100644 --- a/lib/Minz/FrontController.php +++ b/lib/Minz/FrontController.php @@ -34,7 +34,7 @@ class Minz_FrontController { */ public function __construct () { if (LOG_PATH === false) { - $this->killApp ('Path doesn’t exist : LOG_PATH'); + $this->killApp ('Path not found: LOG_PATH'); } try { diff --git a/p/scripts/main.js b/p/scripts/main.js index 24af1b210..0c4c3f1b2 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -587,6 +587,54 @@ function init_load_more(box) { } // +// +function poormanSalt() { //If crypto.getRandomValues is not available + var text = '$2a$04$', + base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789/abcdefghijklmnopqrstuvwxyz'; + for (var i = 22; i > 0; i--) { + text += base.charAt(Math.floor(Math.random() * 64)); + } + return text; +} + +function init_loginForm() { + var $loginForm = $('#loginForm'); + if ($loginForm.length === 0) { + return; + } + if (!(window.dcodeIO)) { + if (window.console) { + console.log('FreshRSS waiting for bcrypt.js…'); + } + window.setTimeout(init_loginForm, 100); + return; + } + $loginForm.on('submit', function() { + $('#loginButton').attr('disabled', ''); + var success = false; + $.ajax({ + url: './?c=javascript&a=nonce&user=' + $('#username').val(), + dataType: 'json', + async: false + }).done(function (data) { + if (data.salt1 == '' || data.nonce == '') { + alert('Invalid user!'); + } else { + var strong = window.Uint32Array && window.crypto && (typeof window.crypto.getRandomValues === 'function'), + s = dcodeIO.bcrypt.hashSync($('#passwordPlain').val(), data.salt1), + c = dcodeIO.bcrypt.hashSync(data.nonce + s, strong ? 4 : poormanSalt()); + $('#challenge').val(c); + success = true; + } + }).fail(function() { + alert('Communication error!'); + }); + $('#loginButton').removeAttr('disabled'); + return success; + }); +} +// + // function init_persona() { if (!(navigator.id)) { @@ -696,8 +744,13 @@ function init_all() { init_notifications(); init_actualize(); init_load_more($stream); - if (use_persona) { - init_persona(); + switch (authType) { + case 'form': + init_loginForm(); + break; + case 'persona': + init_persona(); + break; } init_confirm_action(); init_print_action(); -- cgit v1.2.3 From dda7b002def51405bab82bd4a31fc3ad8c33c572 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 18 Jan 2014 01:38:11 +0100 Subject: Corrections mode anonyme avec formulaire MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contribue à https://github.com/marienfressinaud/FreshRSS/issues/361 --- app/Controllers/indexController.php | 4 ++-- app/layout/header.phtml | 17 ++++++++++----- app/views/helpers/view/login.phtml | 43 ------------------------------------- app/views/index/formLogin.phtml | 43 +++++++++++++++++++++++++++++++++++++ app/views/index/index.phtml | 4 ++-- 5 files changed, 59 insertions(+), 52 deletions(-) delete mode 100644 app/views/helpers/view/login.phtml create mode 100644 app/views/index/formLogin.phtml (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 772a08f30..d05a106bb 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -296,7 +296,6 @@ class FreshRSS_index_Controller extends Minz_ActionController { } public function formLoginAction () { - $this->view->_useLayout (false); if (Minz_Request::isPost()) { $ok = false; $nonce = Minz_Session::param('nonce'); @@ -327,9 +326,10 @@ class FreshRSS_index_Controller extends Minz_ActionController { ); Minz_Session::_param('notification', $notif); } + $this->view->_useLayout(false); + Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } invalidateHttpCache(); - Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } public function formLogoutAction () { diff --git a/app/layout/header.phtml b/app/layout/header.phtml index e90da6196..d43f682b0 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -90,9 +90,16 @@ if (Minz_Configuration::canLogIn()) {
    - -
    - -
    - +
    diff --git a/app/views/helpers/view/login.phtml b/app/views/helpers/view/login.phtml deleted file mode 100644 index e4a24f9ed..000000000 --- a/app/views/helpers/view/login.phtml +++ /dev/null @@ -1,43 +0,0 @@ -
    - -

    -
    - -
    - -
    -
    -
    - -
    - - - -
    -
    -
    -
    - -
    -
    -

    FreshRSS

    -

    - -

    -
    diff --git a/app/views/index/formLogin.phtml b/app/views/index/formLogin.phtml new file mode 100644 index 000000000..e4a24f9ed --- /dev/null +++ b/app/views/index/formLogin.phtml @@ -0,0 +1,43 @@ +
    + +

    +
    + +
    + +
    +
    +
    + +
    + + + +
    +
    +
    +
    + +
    +
    +

    FreshRSS

    +

    + +

    +
    diff --git a/app/views/index/index.phtml b/app/views/index/index.phtml index 9b69233e9..9a7c9f3b9 100644 --- a/app/views/index/index.phtml +++ b/app/views/index/index.phtml @@ -21,8 +21,8 @@ if ($this->loginOk || Minz_Configuration::allowAnonymous()) { if ($token_is_ok) { $this->renderHelper ('view/rss_view'); } else { - $this->renderHelper ('view/login'); + Minz_Request::forward(array('c' => 'index', 'a' => 'formLogin'), true); } } else { - $this->renderHelper ('view/login'); + Minz_Request::forward(array('c' => 'index', 'a' => 'formLogin'), true); } -- cgit v1.2.3 From fdd179d344dbe8734480b83bb7a0fba189275d5a Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 18 Jan 2014 19:29:44 +0100 Subject: Corrections vue globale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contribue à https://github.com/marienfressinaud/FreshRSS/issues/353 --- app/Controllers/entryController.php | 2 +- app/Controllers/indexController.php | 6 ++---- app/views/helpers/view/global_view.phtml | 16 ++++++++++------ lib/Minz/View.php | 6 +++--- p/scripts/global_view.js | 10 ++++++++++ p/scripts/main.js | 8 +++++--- 6 files changed, 31 insertions(+), 17 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index ded7598d9..1756c91e5 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -11,7 +11,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $this->params = array (); $output = Minz_Request::param('output', ''); - if (($output != '') && ($this->conf->view_mode !== $output)) { + if (($output != '') && ($this->view->conf->view_mode !== $output)) { $this->params['output'] = $output; } diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index d05a106bb..45ded6fd4 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -46,10 +46,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { // no layout for RSS output $this->view->_useLayout (false); header('Content-Type: application/rss+xml; charset=utf-8'); - } else { - if ($output === 'global') { - Minz_View::appendScript (Minz_Url::display ('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js'))); - } + } elseif ($output === 'global') { + Minz_View::appendScript (Minz_Url::display ('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js'))); } $this->view->cat_aside = $this->catDAO->listCategories (); diff --git a/app/views/helpers/view/global_view.phtml b/app/views/helpers/view/global_view.phtml index ca7b66e0c..4fb807649 100644 --- a/app/views/helpers/view/global_view.phtml +++ b/app/views/helpers/view/global_view.phtml @@ -2,18 +2,22 @@
    'index', 'a' => 'index', 'params' => array()); + if ($this->conf->view_mode !== 'normal') { + $arUrl['params']['output'] = 'normal'; } + $p = Minz_Request::param('state', ''); + if (($p != '') && ($this->conf->default_view !== $p)) { + $arUrl['params']['state'] = $p; + } + foreach ($this->cat_aside as $cat) { $feeds = $cat->feeds (); if (!empty ($feeds)) { ?>
    @@ -22,7 +26,7 @@ nbNotRead (); ?>
  • ✇ - + name(); ?>
  • diff --git a/lib/Minz/View.php b/lib/Minz/View.php index ba9555cd7..e170bd406 100644 --- a/lib/Minz/View.php +++ b/lib/Minz/View.php @@ -63,7 +63,7 @@ class Minz_View { * Affiche la Vue en elle-même */ public function render () { - if ((@include($this->view_filename)) === false) { + if ((include($this->view_filename)) === false) { Minz_Log::record ('File not found: `' . $this->view_filename . '`', Minz_Log::NOTICE); @@ -79,7 +79,7 @@ class Minz_View { . self::LAYOUT_PATH_NAME . '/' . $part . '.phtml'; - if ((@include($fic_partial)) === false) { + if ((include($fic_partial)) === false) { Minz_Log::record ('File not found: `' . $fic_partial . '`', Minz_Log::WARNING); @@ -95,7 +95,7 @@ class Minz_View { . '/views/helpers/' . $helper . '.phtml'; - if ((@include($fic_helper)) === false) {; + if ((include($fic_helper)) === false) {; Minz_Log::record ('File not found: `' . $fic_helper . '`', Minz_Log::WARNING); diff --git a/p/scripts/global_view.js b/p/scripts/global_view.js index c34fbf1a7..3973cb2ec 100644 --- a/p/scripts/global_view.js +++ b/p/scripts/global_view.js @@ -24,6 +24,16 @@ function load_panel(link) { // en en ouvrant une autre ensuite, on se retrouve au même point de scroll $("#panel").scrollTop(0); + $('#nav_menu_read_all a, #bigMarkAsRead').click(function () { + $.ajax({ + url: $(this).attr("href"), + async: false + }); + //$("#panel .close").first().click(); + window.location.reload(false); + return false; + }); + panel_loading = false; }); } diff --git a/p/scripts/main.js b/p/scripts/main.js index 5bdc316db..3f6352baf 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -137,7 +137,9 @@ function mark_favorite(active) { if (active.closest('div').hasClass('not_read')) { var elem = $('#aside_flux .favorites').children(':first').get(0), feed_unreads = elem ? (parseInt(elem.getAttribute('data-unread'), 10) || 0) : 0; - elem.setAttribute('data-unread', Math.max(0, feed_unreads + inc)); + if (elem) { + elem.setAttribute('data-unread', Math.max(0, feed_unreads + inc)); + } } }); } @@ -559,6 +561,8 @@ function load_more_posts() { } function init_load_more(box) { + box_load_more = box; + var $next_link = $("#load_more"); if (!$next_link.length) { // no more article to load @@ -566,8 +570,6 @@ function init_load_more(box) { return; } - box_load_more = box; - url_load_more = $next_link.attr("href"); var $prefetch = $('#prefetch'); if ($prefetch.attr('href') !== url_load_more) { -- cgit v1.2.3 From e98ac32716dfe9343baa5bce7ad2c1e98c40540c Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Wed, 22 Jan 2014 19:06:46 -0500 Subject: Ajout de statistiques de l'application Conflicts: app/i18n/en.php app/i18n/fr.php --- app/Controllers/indexController.php | 10 ++ app/Models/StatsDAO.php | 178 ++++++++++++++++++++++++++++++++++++ app/i18n/en.php | 14 ++- app/i18n/fr.php | 14 ++- app/layout/aside_flux.phtml | 2 +- app/layout/header.phtml | 1 + app/views/index/stats.phtml | 92 +++++++++++++++++++ p/scripts/flotr2.min.js | 27 ++++++ p/themes/Origine/freshrss.css | 43 +++++++++ 9 files changed, 378 insertions(+), 3 deletions(-) create mode 100644 app/Models/StatsDAO.php create mode 100644 app/views/index/stats.phtml create mode 100644 p/scripts/flotr2.min.js (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 45ded6fd4..84ed992f9 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -6,12 +6,14 @@ class FreshRSS_index_Controller extends Minz_ActionController { private $entryDAO; private $feedDAO; private $catDAO; + private $statsDAO; function __construct($router) { parent::__construct($router); $this->entryDAO = new FreshRSS_EntryDAO (); $this->feedDAO = new FreshRSS_FeedDAO (); $this->catDAO = new FreshRSS_CategoryDAO (); + $this->statsDAO = new FreshRSS_StatsDAO (); } public function indexAction () { @@ -198,6 +200,14 @@ class FreshRSS_index_Controller extends Minz_ActionController { return false; } } + + public function statsAction () { + Minz_View::appendScript (Minz_Url::display ('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js'))); + $this->view->repartition = $this->statsDAO->calculateEntryRepartition(); + $this->view->count = ($this->statsDAO->calculateEntryCount()); + $this->view->feedByCategory = $this->statsDAO->calculateFeedByCategory(); + $this->view->entryByCategory = $this->statsDAO->calculateEntryByCategory(); + } public function aboutAction () { Minz_View::prependTitle (Minz_Translate::t ('about') . ' · '); diff --git a/app/Models/StatsDAO.php b/app/Models/StatsDAO.php new file mode 100644 index 000000000..a317c8f27 --- /dev/null +++ b/app/Models/StatsDAO.php @@ -0,0 +1,178 @@ +prefix}entry AS e +, {$this->prefix}feed AS f +WHERE e.id_feed = f.id +AND f.priority = 10 +SQL; + $stm = $this->bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + $repartition['main_stream'] = $res[0]; + + // Generates the repartition for all entries + $sql = <<prefix}entry AS e +SQL; + $stm = $this->bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + $repartition['all_feeds'] = $res[0]; + + return $repartition; + } + + /** + * Calculates entry count per day on a 30 days period. + * Returns the result as a JSON string. + * + * @return string + */ + public function calculateEntryCount() { + $count = array(); + + // Generates a list of 30 last day to be sure we always have 30 days. + // If we do not do that kind of thing, we'll end up with holes in the + // days if the user do not have a lot of feeds. + $sql = <<bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + foreach ($res as $value) { + $count[$value['day']] = 0; + } + + // Get stats per day for the last 30 days and applies the result on + // the array created with the last query. + $sql = <<prefix}entry AS e +WHERE FROM_UNIXTIME(e.date, '%Y%m%d') BETWEEN DATE_FORMAT(DATE_ADD(NOW(), INTERVAL -30 DAY), '%Y%m%d') AND DATE_FORMAT(DATE_ADD(NOW(), INTERVAL -1 DAY), '%Y%m%d') +GROUP BY day +ORDER BY day ASC +SQL; + $stm = $this->bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + + foreach ($res as $value) { + $count[$value['day']] = (int) $value['count']; + } + + return $this->convertToSerie($count); + } + + /** + * Calculates feed count per category. + * Returns the result as a JSON string. + * + * @return string + */ + public function calculateFeedByCategory() { + $sql = <<prefix}category AS c, +{$this->prefix}feed AS f +WHERE c.id = f.category +GROUP BY label +SQL; + $stm = $this->bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + + return $this->convertToPieSerie($res); + } + + /** + * Calculates entry count per category. + * Returns the result as a JSON string. + * + * @return string + */ + public function calculateEntryByCategory() { + $sql = <<prefix}category AS c, +{$this->prefix}feed AS f, +{$this->prefix}entry AS e +WHERE c.id = f.category +AND f.id = e.id_feed +GROUP BY label +SQL; + $stm = $this->bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + + return $this->convertToPieSerie($res); + } + + private function convertToSerie($data) { + $serie = array(); + + foreach ($data as $key => $value) { + $serie[] = array($key, $value); + } + + return json_encode($serie); + } + + private function convertToPieSerie($data) { + $serie = array(); + + foreach ($data as $value) { + $value['data'] = array(array(0, (int)$value['data'])); + $serie[] = $value; + } + + return json_encode($serie); + } + +} diff --git a/app/i18n/en.php b/app/i18n/en.php index 5d9b01f0b..963105f35 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -13,6 +13,7 @@ return array ( 'category' => 'Category', 'shortcuts' => 'Shortcuts', 'about' => 'About', + 'stats' => 'Statistics', 'your_rss_feeds' => 'Your RSS feeds', 'add_rss_feed' => 'Add a RSS feed', @@ -20,7 +21,8 @@ return array ( 'import_export_opml' => 'Import / export (OPML)', 'subscription_management' => 'Subscriptions management', - 'all_feeds' => 'Main stream', + 'main_stream' => 'Main stream', + 'all_feeds' => 'All feeds', 'favorite_feeds' => 'Favourites (%d)', 'not_read' => '%d unread', 'not_reads' => '%d unread', @@ -297,4 +299,14 @@ return array ( // format for date() function, %s allows to indicate month in letter 'format_date' => '%s j\<\s\u\p\>S\<\/\s\u\p\> Y', 'format_date_hour' => '%s j\<\s\u\p\>S\<\/\s\u\p\> Y \a\t H\:i', + + 'status_favorites' => 'favourites', + 'status_read' => 'read', + 'status_unread' => 'unread', + 'status_total' => 'total', + + 'stats_entry_repartition' => 'Entry repartition', + 'stats_entry_per_day' => 'Entry per day (last 30 days)', + 'stats_feed_per_category' => 'Feed per category', + 'stats_entry_per_category' => 'Entry per category', ); diff --git a/app/i18n/fr.php b/app/i18n/fr.php index fd2257c70..c8b03ef8b 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -13,6 +13,7 @@ return array ( 'category' => 'Catégorie', 'shortcuts' => 'Raccourcis', 'about' => 'À propos', + 'stats' => 'Statistiques', 'your_rss_feeds' => 'Vos flux RSS', 'add_rss_feed' => 'Ajouter un flux RSS', @@ -20,7 +21,8 @@ return array ( 'import_export_opml' => 'Importer / exporter (OPML)', 'subscription_management' => 'Gestion des abonnements', - 'all_feeds' => 'Flux principal', + 'main_stream' => 'Flux principal', + 'all_feeds' => 'Tous les flux', 'favorite_feeds' => 'Favoris (%d)', 'not_read' => '%d non lu', 'not_reads' => '%d non lus', @@ -297,4 +299,14 @@ return array ( // format pour la fonction date(), %s permet d'indiquer le mois en toutes lettres 'format_date' => 'j %s Y', 'format_date_hour' => 'j %s Y \à H\:i', + + 'status_favorites' => 'favoris', + 'status_read' => 'lus', + 'status_unread' => 'non lus', + 'status_total' => 'total', + + 'stats_entry_repartition' => 'Répartition des articles', + 'stats_entry_per_day' => 'Nombre d’articles par jour (30 derniers jours)', + 'stats_feed_per_category' => 'Flux par categorie', + 'stats_entry_per_category' => 'Article par categorie', ); diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml index f7d8b12b9..1e3211886 100644 --- a/app/layout/aside_flux.phtml +++ b/app/layout/aside_flux.phtml @@ -23,7 +23,7 @@ diff --git a/app/layout/header.phtml b/app/layout/header.phtml index b00c30e71..eef53a3fd 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -74,6 +74,7 @@ if (Minz_Configuration::canLogIn()) {
  • +
  • + + +

    + +
    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    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']?>
    +
    + +
    +

    +
    +
    + +
    +

    +
    +
    +
    + +
    +

    +
    +
    +
    + +
    + + \ No newline at end of file diff --git a/p/scripts/flotr2.min.js b/p/scripts/flotr2.min.js new file mode 100644 index 000000000..c3601e827 --- /dev/null +++ b/p/scripts/flotr2.min.js @@ -0,0 +1,27 @@ +/*! + * bean.js - copyright Jacob Thornton 2011 + * https://github.com/fat/bean + * MIT License + * special thanks to: + * dean edwards: http://dean.edwards.name/ + * dperini: https://github.com/dperini/nwevents + * the entire mootools team: github.com/mootools/mootools-core + *//*global module:true, define:true*/ +!function(e,t,n){typeof module!="undefined"?module.exports=n(e,t):typeof define=="function"&&typeof define.amd=="object"?define(n):t[e]=n(e,t)}("bean",this,function(e,t){var n=window,r=t[e],i=/over|out/,s=/[^\.]*(?=\..*)\.|.*/,o=/\..*/,u="addEventListener",a="attachEvent",f="removeEventListener",l="detachEvent",c=document||{},h=c.documentElement||{},p=h[u],d=p?u:a,v=Array.prototype.slice,m=/click|mouse|menu|drag|drop/i,g=/^touch|^gesture/i,y={one:1},b=function(e,t,n){for(n=0;n0){t=t.split(" ");for(f=t.length;f--;)_(e,t[f],n);return e}u=c&&t.replace(o,""),u&&w[u]&&(u=w[u].type);if(!t||c){if(a=c&&t.replace(s,""))a=a.split(".");l(e,u,n,a)}else if(typeof t=="function")l(e,null,t);else for(r in t)t.hasOwnProperty(r)&&_(e,r,t[r]);return e},D=function(e,t,n,r,i){var s,o,u,a,f=n,l=n&&typeof n=="string";if(t&&!n&&typeof t=="object")for(s in t)t.hasOwnProperty(s)&&D.apply(this,[e,s,t[s]]);else{a=arguments.length>3?v.call(arguments,3):[],o=(l?n:t).split(" "),l&&(n=M(t,f=r,i))&&(a=v.call(a,1)),this===y&&(n=L(_,e,t,n,f));for(u=o.length;u--;)O(e,o[u],n,f,a)}return e},P=function(){return D.apply(y,arguments)},H=p?function(e,t,r){var i=c.createEvent(e?"HTMLEvents":"UIEvents");i[e?"initEvent":"initUIEvent"](t,!0,!0,n,1),r.dispatchEvent(i)}:function(e,t,n){n=S(n,e),e?n.fireEvent("on"+t,c.createEventObject()):n["_on"+t]++},B=function(e,t,n){var r,i,u,a,f,l=t.split(" ");for(r=l.length;r--;){t=l[r].replace(o,"");if(a=l[r].replace(s,""))a=a.split(".");if(!a&&!n&&e[d])H(b[t],t,e);else{f=T.get(e,t),n=[!1].concat(n);for(i=0,u=f.length;i=r.computed&&(r={value:e,computed:o})}),r.value},S.min=function(e,t,n){if(!t&&S.isArray(e))return Math.min.apply(Math,e);var r={computed:Infinity};return x(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;or?1:0}),"value")},S.groupBy=function(e,t){var n={};return x(e,function(e,r){var i=t(e,r);(n[i]||(n[i]=[])).push(e)}),n},S.sortedIndex=function(e,t,n){n||(n=S.identity);var r=0,i=e.length;while(r>1;n(e[s])=0})})},S.difference=function(e,t){return S.filter(e,function(e){return!S.include(t,e)})},S.zip=function(){var e=o.call(arguments),t=S.max(S.pluck(e,"length")),n=new Array(t);for(var r=0;r=0;n--)t=[e[n].apply(this,t)];return t[0]}},S.after=function(e,t){return function(){if(--e<1)return t.apply(this,arguments)}},S.keys=w||function(e){if(e!==Object(e))throw new TypeError("Invalid object");var t=[];for(var n in e)f.call(e,n)&&(t[t.length]=n);return t},S.values=function(e){return S.map(e,S.identity)},S.functions=S.methods=function(e){var t=[];for(var n in e)S.isFunction(e[n])&&t.push(n);return t.sort()},S.extend=function(e){return x(o.call(arguments,1),function(t){for(var n in t)t[n]!==void 0&&(e[n]=t[n])}),e},S.defaults=function(e){return x(o.call(arguments,1),function(t){for(var n in t)e[n]==null&&(e[n]=t[n])}),e},S.clone=function(e){return S.isArray(e)?e.slice():S.extend({},e)},S.tap=function(e,t){return t(e),e},S.isEqual=function(e,t){if(e===t)return!0;var n=typeof e,r=typeof t;if(n!=r)return!1;if(e==t)return!0;if(!e&&t||e&&!t)return!1;e._chain&&(e=e._wrapped),t._chain&&(t=t._wrapped);if(e.isEqual)return e.isEqual(t);if(t.isEqual)return t.isEqual(e);if(S.isDate(e)&&S.isDate(t))return e.getTime()===t.getTime();if(S.isNaN(e)&&S.isNaN(t))return!1;if(S.isRegExp(e)&&S.isRegExp(t))return e.source===t.source&&e.global===t.global&&e.ignoreCase===t.ignoreCase&&e.multiline===t.multiline;if(n!=="object")return!1;if(e.length&&e.length!==t.length)return!1;var i=S.keys(e),s=S.keys(t);if(i.length!=s.length)return!1;for(var o in e)if(!(o in t)||!S.isEqual(e[o],t[o]))return!1;return!0},S.isEmpty=function(e){if(S.isArray(e)||S.isString(e))return e.length===0;for(var t in e)if(f.call(e,t))return!1;return!0},S.isElement=function(e){return!!e&&e.nodeType==1},S.isArray=b||function(e){return a.call(e)==="[object Array]"},S.isObject=function(e){return e===Object(e)},S.isArguments=function(e){return!!e&&!!f.call(e,"callee")},S.isFunction=function(e){return!!(e&&e.constructor&&e.call&&e.apply)},S.isString=function(e){return!!(e===""||e&&e.charCodeAt&&e.substr)},S.isNumber=function(e){return!!(e===0||e&&e.toExponential&&e.toFixed)},S.isNaN=function(e){return e!==e},S.isBoolean=function(e){return e===!0||e===!1},S.isDate=function(e){return!!(e&&e.getTimezoneOffset&&e.setUTCFullYear)},S.isRegExp=function(e){return!(!(e&&e.test&&e.exec)||!e.ignoreCase&&e.ignoreCase!==!1)},S.isNull=function(e){return e===null},S.isUndefined=function(e){return e===void 0},S.noConflict=function(){return e._=t,this},S.identity=function(e){return e},S.times=function(e,t,n){for(var r=0;r/g,interpolate:/<%=([\s\S]+?)%>/g},S.template=function(e,t){var n=S.templateSettings,r="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+e.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(n.interpolate,function(e,t){return"',"+t.replace(/\\'/g,"'")+",'"}).replace(n.evaluate||null,function(e,t){return"');"+t.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+"__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",i=new Function("obj",r);return t?i(t):i};var k=function(e){this._wrapped=e};S.prototype=k.prototype;var L=function(e,t){return t?S(e).chain():e},A=function(e,t){k.prototype[e]=function(){var e=o.call(arguments);return u.call(e,this._wrapped),L(t.apply(S,e),this._chain)}};S.mixin(S),x(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=r[e];k.prototype[e]=function(){return t.apply(this._wrapped,arguments),L(this._wrapped,this._chain)}}),x(["concat","join","slice"],function(e){var t=r[e];k.prototype[e]=function(){return L(t.apply(this._wrapped,arguments),this._chain)}}),k.prototype.chain=function(){return this._chain=!0,this},k.prototype.value=function(){return this._wrapped}})(); +/** + * Flotr2 (c) 2012 Carl Sutherland + * MIT License + * Special thanks to: + * Flotr: http://code.google.com/p/flotr/ (fork) + * Flot: https://github.com/flot/flot (original fork) + */ +(function(){var e=this,t=this.Flotr,n;n={_:_,bean:bean,isIphone:/iphone/i.test(navigator.userAgent),isIE:navigator.appVersion.indexOf("MSIE")!=-1?parseFloat(navigator.appVersion.split("MSIE")[1]):!1,graphTypes:{},plugins:{},addType:function(e,t){n.graphTypes[e]=t,n.defaultOptions[e]=t.options||{},n.defaultOptions.defaultType=n.defaultOptions.defaultType||e},addPlugin:function(e,t){n.plugins[e]=t,n.defaultOptions[e]=t.options||{}},draw:function(e,t,r,i){return i=i||n.Graph,new i(e,t,r)},merge:function(e,t){var r,i,s=t||{};for(r in e)i=e[r],i&&typeof i=="object"?i.constructor===Array?s[r]=this._.clone(i):i.constructor!==RegExp&&!this._.isElement(i)&&!i.jquery?s[r]=n.merge(i,t?t[r]:undefined):s[r]=i:s[r]=i;return s},clone:function(e){return n.merge(e,{})},getTickSize:function(e,t,r,i){var s=(r-t)/e,o=n.getMagnitude(s),u=10,a=s/o;return a<1.5?u=1:a<2.25?u=2:a<3?u=i===0?2:2.5:a<7.5&&(u=5),u*o},defaultTickFormatter:function(e,t){return e+""},defaultTrackFormatter:function(e){return"("+e.x+", "+e.y+")"},engineeringNotation:function(e,t,n){var r=["Y","Z","E","P","T","G","M","k",""],i=["y","z","a","f","p","n","µ","m",""],s=r.length;n=n||1e3,t=Math.pow(10,t||2);if(e===0)return 0;if(e>1)while(s--&&e>=n)e/=n;else{r=i,s=r.length;while(s--&&e<1)e*=n}return Math.round(e*t)/t+r[s]},getMagnitude:function(e){return Math.pow(10,Math.floor(Math.log(e)/Math.LN10))},toPixel:function(e){return Math.floor(e)+.5},toRad:function(e){return-e*(Math.PI/180)},floorInBase:function(e,t){return t*Math.floor(e/t)},drawText:function(e,t,r,i,s){if(!e.fillText){e.drawText(t,r,i,s);return}s=this._.extend({size:n.defaultOptions.fontSize,color:"#000000",textAlign:"left",textBaseline:"bottom",weight:1,angle:0},s),e.save(),e.translate(r,i),e.rotate(s.angle),e.fillStyle=s.color,e.font=(s.weight>1?"bold ":"")+s.size*1.3+"px sans-serif",e.textAlign=s.textAlign,e.textBaseline=s.textBaseline,e.fillText(t,0,0),e.restore()},getBestTextAlign:function(e,t){return t=t||{textAlign:"center",textBaseline:"middle"},e+=n.getTextAngleFromAlign(t),Math.abs(Math.cos(e))>.01&&(t.textAlign=Math.cos(e)>0?"right":"left"),Math.abs(Math.sin(e))>.01&&(t.textBaseline=Math.sin(e)>0?"top":"bottom"),t},alignTable:{"right middle":0,"right top":Math.PI/4,"center top":Math.PI/2,"left top":3*(Math.PI/4),"left middle":Math.PI,"left bottom":-3*(Math.PI/4),"center bottom":-Math.PI/2,"right bottom":-Math.PI/4,"center middle":0},getTextAngleFromAlign:function(e){return n.alignTable[e.textAlign+" "+e.textBaseline]||0},noConflict:function(){return e.Flotr=t,this}},e.Flotr=n})(),Flotr.defaultOptions={colors:["#00A8F0","#C0D800","#CB4B4B","#4DA74D","#9440ED"],ieBackgroundColor:"#FFFFFF",title:null,subtitle:null,shadowSize:4,defaultType:null,HtmlText:!0,fontColor:"#545454",fontSize:7.5,resolution:1,parseFloat:!0,preventDefault:!0,xaxis:{ticks:null,minorTicks:null,showLabels:!0,showMinorLabels:!1,labelsAngle:0,title:null,titleAngle:0,noTicks:5,minorTickFreq:null,tickFormatter:Flotr.defaultTickFormatter,tickDecimals:null,min:null,max:null,autoscale:!1,autoscaleMargin:0,color:null,mode:"normal",timeFormat:null,timeMode:"UTC",timeUnit:"millisecond",scaling:"linear",base:Math.E,titleAlign:"center",margin:!0},x2axis:{},yaxis:{ticks:null,minorTicks:null,showLabels:!0,showMinorLabels:!1,labelsAngle:0,title:null,titleAngle:90,noTicks:5,minorTickFreq:null,tickFormatter:Flotr.defaultTickFormatter,tickDecimals:null,min:null,max:null,autoscale:!1,autoscaleMargin:0,color:null,scaling:"linear",base:Math.E,titleAlign:"center",margin:!0},y2axis:{titleAngle:270},grid:{color:"#545454",backgroundColor:null,backgroundImage:null,watermarkAlpha:.4,tickColor:"#DDDDDD",labelMargin:3,verticalLines:!0,minorVerticalLines:null,horizontalLines:!0,minorHorizontalLines:null,outlineWidth:1,outline:"nsew",circular:!1},mouse:{track:!1,trackAll:!1,position:"se",relative:!1,trackFormatter:Flotr.defaultTrackFormatter,margin:5,lineColor:"#FF3F19",trackDecimals:1,sensibility:2,trackY:!0,radius:3,fillColor:null,fillOpacity:.4}},function(){function t(e,t,n,r){this.rgba=["r","g","b","a"];var i=4;while(-1<--i)this[this.rgba[i]]=arguments[i]||(i==3?1:0);this.normalize()}var e=Flotr._,n={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]};t.prototype={scale:function(t,n,r,i){var s=4;while(-1<--s)e.isUndefined(arguments[s])||(this[this.rgba[s]]*=arguments[s]);return this.normalize()},alpha:function(t){return!e.isUndefined(t)&&!e.isNull(t)&&(this.a=t),this.normalize()},clone:function(){return new t(this.r,this.b,this.g,this.a)},limit:function(e,t,n){return Math.max(Math.min(e,n),t)},normalize:function(){var e=this.limit;return this.r=e(parseInt(this.r,10),0,255),this.g=e(parseInt(this.g,10),0,255),this.b=e(parseInt(this.b,10),0,255),this.a=e(this.a,0,1),this},distance:function(e){if(!e)return;e=new t.parse(e);var n=0,r=3;while(-1<--r)n+=Math.abs(this[this.rgba[r]]-e[this.rgba[r]]);return n},toString:function(){return this.a>=1?"rgb("+[this.r,this.g,this.b].join(",")+")":"rgba("+[this.r,this.g,this.b,this.a].join(",")+")"},contrast:function(){var e=1-(.299*this.r+.587*this.g+.114*this.b)/255;return e<.5?"#000000":"#ffffff"}},e.extend(t,{parse:function(e){if(e instanceof t)return e;var r;if(r=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(e))return new t(parseInt(r[1],16),parseInt(r[2],16),parseInt(r[3],16));if(r=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(e))return new t(parseInt(r[1],10),parseInt(r[2],10),parseInt(r[3],10));if(r=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(e))return new t(parseInt(r[1]+r[1],16),parseInt(r[2]+r[2],16),parseInt(r[3]+r[3],16));if(r=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(e))return new t(parseInt(r[1],10),parseInt(r[2],10),parseInt(r[3],10),parseFloat(r[4]));if(r=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(e))return new t(parseFloat(r[1])*2.55,parseFloat(r[2])*2.55,parseFloat(r[3])*2.55);if(r=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(e))return new t(parseFloat(r[1])*2.55,parseFloat(r[2])*2.55,parseFloat(r[3])*2.55,parseFloat(r[4]));var i=(e+"").replace(/^\s*([\S\s]*?)\s*$/,"$1").toLowerCase();return i=="transparent"?new t(255,255,255,0):(r=n[i])?new t(r[0],r[1],r[2]):new t(0,0,0,0)},processColor:function(n,r){var i=r.opacity;if(!n)return"rgba(0, 0, 0, 0)";if(n instanceof t)return n.alpha(i).toString();if(e.isString(n))return t.parse(n).alpha(i).toString();var s=n.colors?n:{colors:n};if(!r.ctx)return e.isArray(s.colors)?t.parse(e.isArray(s.colors[0])?s.colors[0][1]:s.colors[0]).alpha(i).toString():"rgba(0, 0, 0, 0)";s=e.extend({start:"top",end:"bottom"},s),/top/i.test(s.start)&&(r.x1=0),/left/i.test(s.start)&&(r.y1=0),/bottom/i.test(s.end)&&(r.x2=0),/right/i.test(s.end)&&(r.y2=0);var o,u,a,f=r.ctx.createLinearGradient(r.x1,r.y1,r.x2,r.y2);for(o=0;o=h)break}h=i[v][0],p=i[v][1],p=="year"&&(h=Flotr.getTickSize(s.noTicks*r.year,a,f,0),h==.5&&(p="month",h=6)),e.tickUnit=p,e.tickSize=h;var g=h*r[p];m=new Date(a);switch(p){case"millisecond":y("Milliseconds");break;case"second":y("Seconds");break;case"minute":y("Minutes");break;case"hour":y("Hours");break;case"month":y("Month");break;case"year":y("FullYear")}g>=r.second&&t(m,"Milliseconds",o,0),g>=r.minute&&t(m,"Seconds",o,0),g>=r.hour&&t(m,"Minutes",o,0),g>=r.day&&t(m,"Hours",o,0),g>=r.day*4&&t(m,"Date",o,1),g>=r.year&&t(m,"Month",o,0);var b=0,w=NaN,E;do{E=w,w=m.getTime(),c.push({v:w/u,label:d(w/u,e)});if(p=="month")if(h<1){t(m,"Date",o,1);var S=m.getTime();t(m,"Month",o,n(m,"Month",o)+1);var x=m.getTime();m.setTime(w+b*r.hour+(x-S)*h),b=n(m,"Hours",o),t(m,"Hours",o,0)}else t(m,"Month",o,n(m,"Month",o)+h);else p=="year"?t(m,"FullYear",o,n(m,"FullYear",o)+h):m.setTime(w+g)}while(w0)return{x:t.touches[0].pageX,y:t.touches[0].pageY};if(!e._.isUndefined(t.changedTouches)&&t.changedTouches.length>0)return{x:t.changedTouches[0].pageX,y:t.changedTouches[0].pageY};if(t.pageX||t.pageY)return{x:t.pageX,y:t.pageY};if(t.clientX||t.clientY){var n=document,r=n.body,i=n.documentElement;return{x:t.clientX+r.scrollLeft+i.scrollLeft,y:t.clientY+r.scrollTop+i.scrollTop}}}}}(),function(){var e=Flotr,t=e.DOM,n=e._,r=function(e){this.o=e};r.prototype={dimensions:function(e,t,n,r){return e?this.o.html?this.html(e,this.o.element,n,r):this.canvas(e,t):{width:0,height:0}},canvas:function(t,n){if(!this.o.textEnabled)return;n=n||{};var r=this.measureText(t,n),i=r.width,s=n.size||e.defaultOptions.fontSize,o=n.angle||0,u=Math.cos(o),a=Math.sin(o),f=2,l=6,c;return c={width:Math.abs(u*i)+Math.abs(a*s)+f,height:Math.abs(a*i)+Math.abs(u*s)+l},c},html:function(e,n,r,i){var s=t.create("div");return t.setStyles(s,{position:"absolute",top:"-10000px"}),t.insert(s,'
    '+e+"
    "),t.insert(this.o.element,s),t.size(s)},measureText:function(t,r){var i=this.o.ctx,s;return!i.fillText||e.isIphone&&i.measure?{width:i.measure(t,r)}:(r=n.extend({size:e.defaultOptions.fontSize,weight:1,angle:0},r),i.save(),i.font=(r.weight>1?"bold ":"")+r.size*1.3+"px sans-serif",s=i.measureText(t),i.restore(),s)}},Flotr.Text=r}(),function(){function i(e,n,r){return t.observe.apply(this,arguments),this._handles.push(arguments),this}var e=Flotr.DOM,t=Flotr.EventAdapter,n=Flotr._,r=Flotr;Graph=function(e,i,s){this._setEl(e),this._initMembers(),this._initPlugins(),t.fire(this.el,"flotr:beforeinit",[this]),this.data=i,this.series=r.Series.getSeries(i),this._initOptions(s),this._initGraphTypes(),this._initCanvas(),this._text=new r.Text({element:this.el,ctx:this.ctx,html:this.options.HtmlText,textEnabled:this.textEnabled}),t.fire(this.el,"flotr:afterconstruct",[this]),this._initEvents(),this.findDataRanges(),this.calculateSpacing(),this.draw(n.bind(function(){t.fire(this.el,"flotr:afterinit",[this])},this))},Graph.prototype={destroy:function(){t.fire(this.el,"flotr:destroy"),n.each(this._handles,function(e){t.stopObserving.apply(this,e)}),this._handles=[],this.el.graph=null},observe:i,_observe:i,processColor:function(e,t){var i={x1:0,y1:0,x2:this.plotWidth,y2:this.plotHeight,opacity:1,ctx:this.ctx};return n.extend(i,t),r.Color.processColor(e,i)},findDataRanges:function(){var e=this.axes,t,i,s;n.each(this.series,function(e){s=e.getRange(),s&&(t=e.xaxis,i=e.yaxis,t.datamin=Math.min(s.xmin,t.datamin),t.datamax=Math.max(s.xmax,t.datamax),i.datamin=Math.min(s.ymin,i.datamin),i.datamax=Math.max(s.ymax,i.datamax),t.used=t.used||s.xused,i.used=i.used||s.yused)},this),!e.x.used&&!e.x2.used&&(e.x.used=!0),!e.y.used&&!e.y2.used&&(e.y.used=!0),n.each(e,function(e){e.calculateRange()});var o=n.keys(r.graphTypes),u=!1;n.each(this.series,function(e){if(e.hide)return;n.each(o,function(t){e[t]&&e[t].show&&(this.extendRange(t,e),u=!0)},this),u||this.extendRange(this.options.defaultType,e)},this)},extendRange:function(e,t){this[e].extendRange&&this[e].extendRange(t,t.data,t[e],this[e]),this[e].extendYRange&&this[e].extendYRange(t.yaxis,t.data,t[e],this[e]),this[e].extendXRange&&this[e].extendXRange(t.xaxis,t.data,t[e],this[e])},calculateSpacing:function(){var e=this.axes,t=this.options,r=this.series,i=t.grid.labelMargin,s=this._text,o=e.x,u=e.x2,a=e.y,f=e.y2,l=t.grid.outlineWidth,c,h,p,d;n.each(e,function(e){e.calculateTicks(),e.calculateTextDimensions(s,t)}),d=s.dimensions(t.title,{size:t.fontSize*1.5},"font-size:1em;font-weight:bold;","flotr-title"),this.titleHeight=d.height,d=s.dimensions(t.subtitle,{size:t.fontSize},"font-size:smaller;","flotr-subtitle"),this.subtitleHeight=d.height;for(h=0;h1&&(this.multitouches=n.touches),t.fire(e,"flotr:mousedown",[event,this]),this.observe(document,"touchend",r)},this)),this.observe(this.overlay,"touchmove",n.bind(function(n){var r=this.getEventPosition(n);this.options.preventDefault&&n.preventDefault(),i=!0,this.multitouches||n.touches&&n.touches.length>1?this.multitouches=n.touches:s||t.fire(e,"flotr:mousemove",[event,r,this]),this.lastMousePos=r},this))):this.observe(this.overlay,"mousedown",n.bind(this.mouseDownHandler,this)).observe(e,"mousemove",n.bind(this.mouseMoveHandler,this)).observe(this.overlay,"click",n.bind(this.clickHandler,this)).observe(e,"mouseout",function(n){t.fire(e,"flotr:mouseout",n)})},_initCanvas:function(){function l(i,s){return i||(i=e.create("canvas"),typeof FlashCanvas!="undefined"&&typeof i.getContext=="function"&&(FlashCanvas.initElement(i),this.isFlashCanvas=!0),i.className="flotr-"+s,i.style.cssText="position:absolute;left:0px;top:0px;",e.insert(t,i)),n.each(a,function(t,n){e.show(i);if(s=="canvas"&&i.getAttribute(n)===t)return;i.setAttribute(n,t*r.resolution),i.style[n]=t+"px"}),i.context_=null,i}function c(e){window.G_vmlCanvasManager&&window.G_vmlCanvasManager.initElement(e);var t=e.getContext("2d");return window.G_vmlCanvasManager||t.scale(r.resolution,r.resolution),t}var t=this.el,r=this.options,i=t.children,s=[],o,u,a,f;for(u=i.length;u--;)o=i[u],!this.canvas&&o.className==="flotr-canvas"?this.canvas=o:!this.overlay&&o.className==="flotr-overlay"?this.overlay=o:s.push(o);for(u=s.length;u--;)t.removeChild(s[u]);e.setStyles(t,{position:"relative"}),a={},a.width=t.clientWidth,a.height=t.clientHeight;if(a.width<=0||a.height<=0||r.resolution<=0)throw"Invalid dimensions for plot, width = "+a.width+", height = "+a.height+", resolution = "+r.resolution;this.canvas=l(this.canvas,"canvas"),this.overlay=l(this.overlay,"overlay"),this.ctx=c(this.canvas),this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.octx=c(this.overlay),this.octx.clearRect(0,0,this.overlay.width,this.overlay.height),this.canvasHeight=a.height,this.canvasWidth=a.width,this.textEnabled=!!this.ctx.drawText||!!this.ctx.fillText},_initPlugins:function(){n.each(r.plugins,function(e,t){n.each(e.callbacks,function(e,t){this.observe(this.el,t,n.bind(e,this))},this),this[t]=r.clone(e),n.each(this[t],function(e,r){n.isFunction(e)&&(this[t][r]=n.bind(e,this))},this)},this)},_initOptions:function(e){var i=r.clone(r.defaultOptions);i.x2axis=n.extend(n.clone(i.xaxis),i.x2axis),i.y2axis=n.extend(n.clone(i.yaxis),i.y2axis),this.options=r.merge(e||{},i),this.options.grid.minorVerticalLines===null&&this.options.xaxis.scaling==="logarithmic"&&(this.options.grid.minorVerticalLines=!0),this.options.grid.minorHorizontalLines===null&&this.options.yaxis.scaling==="logarithmic"&&(this.options.grid.minorHorizontalLines=!0),t.fire(this.el,"flotr:afterinitoptions",[this]),this.axes=r.Axis.getAxes(this.options);var s=[],o=[],u=this.series.length,a=this.series.length,f=this.options.colors,l=[],c=0,h,p,d,v;for(p=a-1;p>-1;--p)h=this.series[p].color,h&&(--a,n.isNumber(h)?s.push(h):l.push(r.Color.parse(h)));for(p=s.length-1;p>-1;--p)a=Math.max(a,s[p]+1);for(p=0;o.length=f.length&&(p=0,++c)}for(p=0,d=0;p10?t.minorTickFreq=0:o-u>5?t.minorTickFreq=2:t.minorTickFreq=5)}else e.tickSize=Flotr.getTickSize(t.noTicks,n,r,t.tickDecimals);e.min=n,e.max=r,t.min===null&&t.autoscale&&(e.min-=e.tickSize*i,e.min<0&&e.datamin>=0&&(e.min=0),e.min=e.tickSize*Math.floor(e.min/e.tickSize)),t.max===null&&t.autoscale&&(e.max+=e.tickSize*i,e.max>0&&e.datamax<=0&&e.datamax!=e.datamin&&(e.max=0),e.max=e.tickSize*Math.ceil(e.max/e.tickSize)),e.min==e.max&&(e.max=e.min+1)},calculateTextDimensions:function(e,t){var n="",r,i;if(this.options.showLabels)for(i=0;in.length&&(n=this.ticks[i].label);this.maxLabel=e.dimensions(n,{size:t.fontSize,angle:Flotr.toRad(this.options.labelsAngle)},"font-size:smaller;","flotr-grid-label"),this.titleSize=e.dimensions(this.options.title,{size:t.fontSize*1.2,angle:Flotr.toRad(this.options.titleAngle)},"font-weight:bold;","flotr-axis-title")},_cleanUserTicks:function(t,n){var r=this,i=this.options,s,o,u,a;e.isFunction(t)&&(t=t({min:r.min,max:r.max}));for(o=0;o1?a[1]:i.tickFormatter(s,{min:r.min,max:r.max})):(s=a,u=i.tickFormatter(s,{min:this.min,max:this.max})),n[o]={v:s,label:u}},_calculateTimeTicks:function(){this.ticks=Flotr.Date.generator(this)},_calculateLogTicks:function(){var e=this,t=e.options,n,r,s=Math.log(e.max);t.base!=Math.E&&(s/=Math.log(t.base)),s=Math.ceil(s);var o=Math.log(e.min);t.base!=Math.E&&(o/=Math.log(t.base)),o=Math.ceil(o);for(i=o;ii&&(i=a,o=!0)),f!==null&&(fs&&(s=f,u=!0));return{xmin:n,xmax:i,ymin:r,ymax:s,xused:o,yused:u}}},e.extend(t,{getSeries:function(n){return e.map(n,function(n){var r;return n.data?(r=new t,e.extend(r,n)):r=new t({data:n}),r})}}),Flotr.Series=t}(),Flotr.addType("lines",{options:{show:!1,lineWidth:2,fill:!1,fillBorder:!1,fillColor:null,fillOpacity:.4,steps:!1,stacked:!1},stack:{values:[]},draw:function(e){var t=e.context,n=e.lineWidth,r=e.shadowSize,i;t.save(),t.lineJoin="round",r&&(t.lineWidth=r/2,i=n/2+t.lineWidth/2,t.strokeStyle="rgba(0,0,0,0.1)",this.plot(e,i+r/2,!1),t.strokeStyle="rgba(0,0,0,0.2)",this.plot(e,i,!1)),t.lineWidth=n,t.strokeStyle=e.color,this.plot(e,0,!0),t.restore()},plot:function(e,t,n){function S(){!t&&e.fill&&d&&(v=o(d[0]),r.fillStyle=e.fillStyle,r.lineTo(m,p),r.lineTo(v,p),r.lineTo(v,u(d[1])),r.fill(),e.fillBorder&&r.stroke())}var r=e.context,i=e.width,s=e.height,o=e.xScale,u=e.yScale,a=e.data,f=e.stacked?this.stack:!1,l=a.length-1,c=null,h=null,p=u(0),d=null,v,m,g,y,b,w,E;if(l<1)return;r.beginPath();for(E=0;E0&&a[E][1]!==null&&(r.stroke(),S(),d=null,r.closePath(),r.beginPath());continue}v=o(a[E][0]),m=o(a[E+1][0]),d===null&&(d=a[E]),f?(b=f.values[a[E][0]]||0,w=f.values[a[E+1][0]]||f.values[a[E][0]]||0,g=u(a[E][1]+b),y=u(a[E+1][1]+w),n&&(a[E].y0=b,f.values[a[E][0]]=a[E][1]+b,E==l-1&&(a[E+1].y0=w,f.values[a[E+1][0]]=a[E+1][1]+w))):(g=u(a[E][1]),y=u(a[E+1][1]));if(g>s&&y>s||g<0&&y<0||v<0&&m<0||v>i&&m>i)continue;(c!=v||h!=g+t)&&r.moveTo(v,g+t),c=m,h=y+t,e.steps?(r.lineTo(c+t/2,g+t),r.lineTo(c+t/2,h)):r.lineTo(c,h)}(!e.fill||e.fill&&!e.fillBorder)&&r.stroke(),S(),r.closePath()},extendYRange:function(e,t,n,r){var i=e.options;if(n.stacked&&(!i.max&&i.max!==0||!i.min&&i.min!==0)){var s=e.max,o=e.min,u=r.positiveSums||{},a=r.negativeSums||{},f,l;for(l=0;l0?(u[f]=(u[f]||0)+t[l][1],s=Math.max(s,u[f])):(a[f]=(a[f]||0)+t[l][1],o=Math.min(o,a[f]));r.negativeSums=a,r.positiveSums=u,e.max=s,e.min=o}n.steps&&(this.hit=function(e){var t=e.data,n=e.args,r=e.yScale,i=n[0],s=t.length,o=n[1],u=e.xInverse(i.relX),a=i.relY,f;for(f=0;f=t[f][0]&&u<=t[f+1][0]){Math.abs(r(t[f][1])-a)<8&&(o.x=t[f][0],o.y=t[f][1],o.index=f,o.seriesIndex=e.index);break}},this.drawHit=function(e){var t=e.context,n=e.args,r=e.data,i=e.xScale,s=n.index,o=i(n.x),u=e.yScale(n.y),a;r.length-1>s&&(a=e.xScale(r[s+1][0]),t.save(),t.strokeStyle=e.color,t.lineWidth=e.lineWidth,t.beginPath(),t.moveTo(o,u),t.lineTo(a,u),t.stroke(),t.closePath(),t.restore())},this.clearHit=function(e){var t=e.context,n=e.args,r=e.data,i=e.xScale,s=e.lineWidth,o=n.index,u=i(n.x),a=e.yScale(n.y),f;r.length-1>o&&(f=e.xScale(r[o+1][0]),t.clearRect(u-s,a-s,f-u+2*s,2*s))})}}),Flotr.addType("bars",{options:{show:!1,lineWidth:2,barWidth:1,fill:!0,fillColor:null,fillOpacity:.4,horizontal:!1,stacked:!1,centered:!0,topPadding:.1,grouped:!1},stack:{positive:[],negative:[],_positive:[],_negative:[]},draw:function(e){var t=e.context;this.current+=1,t.save(),t.lineJoin="miter",t.lineWidth=e.lineWidth,t.strokeStyle=e.color,e.fill&&(t.fillStyle=e.fillStyle),this.plot(e),t.restore()},plot:function(e){var t=e.data,n=e.context,r=e.shadowSize,i,s,o,u,a,f;if(t.length<1)return;this.translate(n,e.horizontal);for(i=0;i0?o.positive:o.negative,p=d[c]||p,d[c]=p+h),v=f(c-a),m=f(c+i-a),g=l(h+p),y=l(p),y<0&&(y=0),e===null||t===null?null:{x:c,y:h,xScale:f,yScale:l,top:g,left:Math.min(v,m)-u/2,width:Math.abs(m-v)-u,height:y-g}},hit:function(e){var t=e.data,n=e.args,r=n[0],i=n[1],s=e.xInverse(r.relX),o=e.yInverse(r.relY),u=this.getBarGeometry(s,o,e),a=u.width/2,f=u.left,l=u.y,c,h;for(h=t.length;h--;)c=this.getBarGeometry(t[h][0],t[h][1],e),(l>0&&lc.y)&&Math.abs(f-c.left)0?(f[c]=(f[c]||0)+h,o=Math.max(o,f[c])):(l[c]=(l[c]||0)+h,s=Math.min(s,l[c]));(a==1&&u||a==-1&&!u)&&n.topPadding&&(e.max===e.datamax||n.stacked&&this.stackMax!==o)&&(o+=n.topPadding*(o-s)),this.stackMin=s,this.stackMax=o,this.negativeSums=l,this.positiveSums=f,e.max=o,e.min=s}}),Flotr.addType("bubbles",{options:{show:!1,lineWidth:2,fill:!0,fillOpacity:.4,baseRadius:2},draw:function(e){var t=e.context,n=e.shadowSize;t.save(),t.lineWidth=e.lineWidth,t.fillStyle="rgba(0,0,0,0.05)",t.strokeStyle="rgba(0,0,0,0.05)",this.plot(e,n/2),t.strokeStyle="rgba(0,0,0,0.1)",this.plot(e,n/4),t.strokeStyle=e.color,t.fillStyle=e.fillStyle,this.plot(e),t.restore()},plot:function(e,t){var n=e.data,r=e.context,i,s,o,u,a;t=t||0;for(s=0;sg?"downFillColor":"upFillColor"],e.fill&&!e.barcharts&&(n.fillStyle="rgba(0,0,0,0.05)",n.fillRect(y+o,x+o,b-y,S-x),n.save(),n.globalAlpha=e.fillOpacity,n.fillStyle=l,n.fillRect(y,x+u,b-y,S-x),n.restore());if(u||a)h=Math.floor((y+b)/2)+f,n.strokeStyle=l,n.beginPath(),e.barcharts?(n.moveTo(h,Math.floor(E+u)),n.lineTo(h,Math.floor(w+u)),T=dv&&ag&&fs.max||yo.max)continue;vs.max&&(m=s.max,s.lastSerie!=e&&(p=!1)),go.max&&(y=o.max,o.lastSerie!=e&&(p=!1)),r&&(u.beginPath(),u.moveTo(s.d2p(v),o.d2p(g)+n),u.lineTo(s.d2p(v),o.d2p(y)+n),u.lineTo(s.d2p(m),o.d2p(y)+n),u.lineTo(s.d2p(m),o.d2p(g)+n),u.fill(),u.closePath()),e.gantt.lineWidth&&(h||d||p)&&(u.beginPath(),u.moveTo(s.d2p(v),o.d2p(g)+n),u[h?"lineTo":"moveTo"](s.d2p(v),o.d2p(y)+n),u[p?"lineTo":"moveTo"](s.d2p(m),o.d2p(y)+n),u[d?"lineTo":"moveTo"](s.d2p(m),o.d2p(g)+n),u.stroke(),u.closePath())}},plotShadows:function(e,t,n){var r=e.data;if(r.length<1)return;var i,s,o,u,a=e.xaxis,f=e.yaxis,l=this.ctx,c=this.options.shadowSize;for(i=0;ia.max||vf.max)continue;ha.max&&(p=a.max),df.max&&(v=f.max);var m=a.d2p(p)-a.d2p(h)-(a.d2p(p)+c<=this.plotWidth?0:c),g=f.d2p(d)-f.d2p(v)-(f.d2p(d)+c<=this.plotHeight?0:c);l.fillStyle="rgba(0,0,0,0.05)",l.fillRect(Math.min(a.d2p(h)+c,this.plotWidth),Math.min(f.d2p(v)+c,this.plotHeight),m,g)}},extendXRange:function(e){if(e.options.max===null){var t=e.min,n=e.max,r,i,s,o,u,a={},f={},l=null;for(r=0;rt&&(t=e.max+o.barWidth)}}e.lastSerie=f,e.max=t,e.min=n,e.tickSize=Flotr.getTickSize(e.options.noTicks,n,t,e.options.tickDecimals)}}}),function(){function e(e){return typeof e=="object"&&e.constructor&&(Image?!0:e.constructor===Image)}Flotr.defaultMarkerFormatter=function(e){return Math.round(e.y*100)/100+""},Flotr.addType("markers",{options:{show:!1,lineWidth:1,color:"#000000",fill:!1,fillColor:"#FFFFFF",fillOpacity:.4,stroke:!1,position:"ct",verticalMargin:0,labelFormatter:Flotr.defaultMarkerFormatter,fontSize:Flotr.defaultOptions.fontSize,stacked:!1,stackingType:"b",horizontal:!1},stack:{positive:[],negative:[],values:[]},draw:function(e){function h(e,t){return o=r.negative[e]||0,s=r.positive[e]||0,t>0?(r.positive[e]=o+t,o+t):(r.negative[e]=s+t,s+t)}var t=e.data,n=e.context,r=e.stacked?e.stack:!1,i=e.stackingType,s,o,u,a,f,l,c;n.save(),n.lineJoin="round",n.lineWidth=e.lineWidth,n.strokeStyle="rgba(0,0,0,0.5)",n.fillStyle=e.fillStyle;for(a=0;a0?"top":"bottom",C,k,L;n.save(),n.translate(u/2,o/2),n.scale(1,v),k=Math.cos(b)*a,L=Math.sin(b)*a,i>0&&(this.plotSlice(k+i,L+i,h,g,y,n),l&&(n.fillStyle="rgba(0,0,0,0.1)",n.fill())),this.plotSlice(k,L,h,g,y,n),l&&(n.fillStyle=c,n.fill()),n.lineWidth=r,n.strokeStyle=f,n.stroke(),C={size:e.fontSize*1.2,color:e.fontColor,weight:1.5},w&&(e.htmlText||!e.textEnabled?(divStyle="position:absolute;"+N+":"+(o/2+(N==="top"?x:-x))+"px;",divStyle+=T+":"+(u/2+(T==="right"?-S:S))+"px;",d.push('
    ',w,"
    ")):(C.textAlign=T,C.textBaseline=N,Flotr.drawText(n,w,S,x,C)));if(e.htmlText||!e.textEnabled){var A=Flotr.DOM.node('
    ');Flotr.DOM.insert(A,d.join("")),Flotr.DOM.insert(e.element,A)}n.restore(),this.startAngle=y,this.slices=this.slices||[],this.slices.push({radius:h,x:k,y:L,explode:a,start:g,end:y})},plotSlice:function(e,t,n,r,i,s){s.beginPath(),s.moveTo(e,t),s.arc(e,t,n,r,i,!1),s.lineTo(e,t),s.closePath()},hit:function(e){var t=e.data[0],n=e.args,r=e.index,i=n[0],s=n[1],o=this.slices[r],u=i.relX-e.width/2,a=i.relY-e.height/2,f=Math.sqrt(u*u+a*a),l=Math.atan(a/u),c=Math.PI*2,h=o.explode||e.explode,p=o.start%c,d=o.end%c,v=e.epsilon;u<0?l+=Math.PI:u>0&&a<0&&(l+=c),fh&&(l>p&&ld&&(lp)||p===d&&(o.start===o.end&&Math.abs(l-p)v))&&(s.x=t[0],s.y=t[1],s.sAngle=p,s.eAngle=d,s.index=0,s.seriesIndex=r,s.fraction=t[1]/this.total)},drawHit:function(e){var t=e.context,n=this.slices[e.args.seriesIndex];t.save(),t.translate(e.width/2,e.height/2),this.plotSlice(n.x,n.y,n.radius,n.start,n.end,t),t.stroke(),t.restore()},clearHit:function(e){var t=e.context,n=this.slices[e.args.seriesIndex],r=2*e.lineWidth,i=n.radius+r;t.save(),t.translate(e.width/2,e.height/2),t.clearRect(n.x-i,n.y-i,2*i+r,2*i+r),t.restore()},extendYRange:function(e,t){this.total=(this.total||0)+t[0][1]}})}(),Flotr.addType("points",{options:{show:!1,radius:3,lineWidth:2,fill:!0,fillColor:"#FFFFFF",fillOpacity:1,hitRadius:null},draw:function(e){var t=e.context,n=e.lineWidth,r=e.shadowSize;t.save(),r>0&&(t.lineWidth=r/2,t.strokeStyle="rgba(0,0,0,0.1)",this.plot(e,r/2+t.lineWidth/2),t.strokeStyle="rgba(0,0,0,0.2)",this.plot(e,t.lineWidth/2)),t.lineWidth=e.lineWidth,t.strokeStyle=e.color,e.fill&&(t.fillStyle=e.fillStyle),this.plot(e),t.restore()},plot:function(e,t){var n=e.data,r=e.context,i=e.xScale,s=e.yScale,o,u,a;for(o=n.length-1;o>-1;--o){a=n[o][1];if(a===null)continue;u=i(n[o][0]),a=s(a);if(u<0||u>e.width||a<0||a>e.height)continue;r.beginPath(),t?r.arc(u,a+t,e.radius,0,Math.PI,!1):(r.arc(u,a,e.radius,0,2*Math.PI,!0),e.fill&&r.fill()),r.stroke(),r.closePath()}}}),Flotr.addType("radar",{options:{show:!1,lineWidth:2,fill:!0,fillOpacity:.4,radiusRatio:.9,sensibility:2},draw:function(e){var t=e.context,n=e.shadowSize;t.save(),t.translate(e.width/2,e.height/2),t.lineWidth=e.lineWidth,t.fillStyle="rgba(0,0,0,0.05)",t.strokeStyle="rgba(0,0,0,0.05)",this.plot(e,n/2),t.strokeStyle="rgba(0,0,0,0.1)",this.plot(e,n/4),t.strokeStyle=e.color,t.fillStyle=e.fillStyle,this.plot(e),t.restore()},plot:function(e,t){var n=e.data,r=e.context,i=Math.min(e.height,e.width)*e.radiusRatio/2,s=2*Math.PI/n.length,o=-Math.PI/2,u,a;t=t||0,r.beginPath();for(u=0;uthis.plotWidth||t.relY>this.plotHeight){this.el.style.cursor=null,e.removeClass(this.el,"flotr-crosshair");return}r.hideCursor&&(this.el.style.cursor="none",e.addClass(this.el,"flotr-crosshair")),n.save(),n.strokeStyle=r.color,n.lineWidth=1,n.beginPath(),r.mode.indexOf("x")!=-1&&(n.moveTo(s,i.top),n.lineTo(s,i.top+this.plotHeight)),r.mode.indexOf("y")!=-1&&(n.moveTo(i.left,o),n.lineTo(i.left+this.plotWidth,o)),n.stroke(),n.restore()},clearCrosshair:function(){var e=this.plotOffset,t=this.lastMousePos,n=this.octx;t&&(n.clearRect(Math.round(t.relX)+e.left,e.top,1,this.plotHeight+1),n.clearRect(e.left,Math.round(t.relY)+e.top,this.plotWidth+1,1))}})}(),function(){function n(e,t,n,r,i,s){var o="image/"+e,u=n.getImageData(0,0,r,i),a=new Image;return n.save(),n.globalCompositeOperation="destination-over",n.fillStyle=s,n.fillRect(0,0,r,i),a.src=t.toDataURL(o),n.restore(),n.clearRect(0,0,r,i),n.putImageData(u,0,0),a}var e=Flotr.DOM,t=Flotr._;Flotr.addPlugin("download",{saveImage:function(r,i,s,o){var u=this.options.grid,a;if(Flotr.isIE&&Flotr.isIE<9)return a=""+this.canvas.firstChild.innerHTML+"",window.open().document.write(a);if(r!=="jpeg"&&r!=="png")return;a=n(r,this.canvas,this.ctx,this.canvasWidth,this.canvasHeight,u&&u.backgroundColor||"#ffffff");if(!t.isElement(a)||!o)return window.open(a.src);this.download.restoreCanvas(),e.hide(this.canvas),e.hide(this.overlay),e.setStyles({position:"absolute"}),e.insert(this.el,a),this.saveImageElement=a},restoreCanvas:function(){e.show(this.canvas),e.show(this.overlay),this.saveImageElement&&this.el.removeChild(this.saveImageElement),this.saveImageElement=null}})}(),function(){var e=Flotr.EventAdapter,t=Flotr._;Flotr.addPlugin("graphGrid",{callbacks:{"flotr:beforedraw":function(){this.graphGrid.drawGrid()},"flotr:afterdraw":function(){this.graphGrid.drawOutline()}},drawGrid:function(){function v(e){for(p=0;p=c.max||(e==c.min||e==c.max)&&i.outlineWidth)return;r(Math.floor(c.d2p(e))+n.lineWidth/2)})}function g(e){n.moveTo(e,0),n.lineTo(e,f)}function y(e){n.moveTo(0,e),n.lineTo(l,e)}var n=this.ctx,r=this.options,i=r.grid,s=i.verticalLines,o=i.horizontalLines,u=i.minorVerticalLines,a=i.minorHorizontalLines,f=this.plotHeight,l=this.plotWidth,c,h,p,d;(s||u||o||a)&&e.fire(this.el,"flotr:beforegrid",[this.axes.x,this.axes.y,r,this]),n.save(),n.lineWidth=1,n.strokeStyle=i.tickColor;if(i.circular){n.translate(this.plotOffset.left+l/2,this.plotOffset.top+f/2);var b=Math.min(f,l)*r.radar.radiusRatio/2,w=this.axes.x.ticks.length,S=2*(Math.PI/w),x=-Math.PI/2;n.beginPath(),c=this.axes.y,o&&v(c.ticks),a&&v(c.minorTicks),s&&t.times(w,function(e){n.moveTo(0,0),n.lineTo(Math.cos(e*S+x)*b,Math.sin(e*S+x)*b)}),n.stroke()}else n.translate(this.plotOffset.left,this.plotOffset.top),i.backgroundColor&&(n.fillStyle=this.processColor(i.backgroundColor,{x1:0,y1:0,x2:l,y2:f}),n.fillRect(0,0,l,f)),n.beginPath(),c=this.axes.x,s&&m(c.ticks,g),u&&m(c.minorTicks,g),c=this.axes.y,o&&m(c.ticks,y),a&&m(c.minorTicks,y),n.stroke();n.restore(),(s||u||o||a)&&e.fire(this.el,"flotr:aftergrid",[this.axes.x,this.axes.y,r,this])},drawOutline:function(){var e=this,t=e.options,n=t.grid,r=n.outline,s=e.ctx,o=n.backgroundImage,u=e.plotOffset,a=u.left,f=u.top,l=e.plotWidth,c=e.plotHeight,h,p,d,v,m,g;if(!n.outlineWidth)return;s.save();if(n.circular){s.translate(a+l/2,f+c/2);var y=Math.min(c,l)*t.radar.radiusRatio/2,b=this.axes.x.ticks.length,w=2*(Math.PI/b),E=-Math.PI/2;s.beginPath(),s.lineWidth=n.outlineWidth,s.strokeStyle=n.color,s.lineJoin="round";for(i=0;i<=b;++i)s[i===0?"moveTo":"lineTo"](Math.cos(i*w+E)*y,Math.sin(i*w+E)*y);s.stroke()}else{s.translate(a,f);var S=n.outlineWidth,x=.5-S+(S+1)%2/2,T="lineTo",N="moveTo";s.lineWidth=S,s.strokeStyle=n.color,s.lineJoin="miter",s.beginPath(),s.moveTo(x,x),l-=S/2%1,c+=S/2,s[r.indexOf("n")!==-1?T:N](l,x),s[r.indexOf("e")!==-1?T:N](l,c),s[r.indexOf("s")!==-1?T:N](x,c),s[r.indexOf("w")!==-1?T:N](x,x),s.stroke(),s.closePath()}s.restore(),o&&(d=o.src||o,v=(parseInt(o.left,10)||0)+u.left,m=(parseInt(o.top,10)||0)+u.top,p=new Image,p.onload=function(){s.save(),o.alpha&&(s.globalAlpha=o.alpha),s.globalCompositeOperation="destination-over",s.drawImage(p,0,0,p.width,p.height,v,m,l,c),s.restore()},p.src=d)}})}(),function(){var e=Flotr.DOM,t=Flotr._,n=Flotr,r="opacity:0.7;background-color:#000;color:#fff;position:absolute;padding:2px 8px;-moz-border-radius:4px;border-radius:4px;white-space:nowrap;";Flotr.addPlugin("hit",{callbacks:{"flotr:mousemove":function(e,t){this.hit.track(t)},"flotr:click":function(e){var n=this.hit.track(e);n&&!t.isUndefined(n.index)&&(e.hit=n)},"flotr:mouseout":function(e){e.relatedTarget!==this.mouseTrack&&this.hit.clearHit()},"flotr:destroy":function(){this.options.mouse.container&&e.remove(this.mouseTrack),this.mouseTrack=null}},track:function(e){if(this.options.mouse.track||t.any(this.series,function(e){return e.mouse&&e.mouse.track}))return this.hit.hit(e)},executeOnType:function(e,r,i){function u(e,u){t.each(t.keys(n.graphTypes),function(t){e[t]&&e[t].show&&!e.hide&&this[t][r]&&(o=this.getOptions(e,t),o.fill=!!e.mouse.fillColor,o.fillStyle=this.processColor(e.mouse.fillColor||"#ffffff",{opacity:e.mouse.fillOpacity}),o.color=e.mouse.lineColor,o.context=this.octx,o.index=u,i&&(o.args=i),this[t][r].call(this[t],o),s=!0)},this)}var s=!1,o;return t.isArray(e)||(e=[e]),t.each(e,u,this),s},drawHit:function(e){var t=this.octx,n=e.series;if(n.mouse.lineColor){t.save(),t.lineWidth=n.points?n.points.lineWidth:1,t.strokeStyle=n.mouse.lineColor,t.fillStyle=this.processColor(n.mouse.fillColor||"#ffffff",{opacity:n.mouse.fillOpacity}),t.translate(this.plotOffset.left,this.plotOffset.top);if(!this.hit.executeOnType(n,"drawHit",e)){var r=e.xaxis,i=e.yaxis;t.beginPath(),t.arc(r.d2p(e.x),i.d2p(e.y),n.points.hitRadius||n.points.radius||n.mouse.radius,0,2*Math.PI,!0),t.fill(),t.stroke(),t.closePath()}t.restore(),this.clip(t)}this.prevHit=e},clearHit:function(){var t=this.prevHit,n=this.octx,r=this.plotOffset;n.save(),n.translate(r.left,r.top);if(t){if(!this.hit.executeOnType(t.series,"clearHit",this.prevHit)){var i=t.series,s=i.points?i.points.lineWidth:1;offset=(i.points.hitRadius||i.points.radius||i.mouse.radius)+s,n.clearRect(t.xaxis.d2p(t.x)-offset,t.yaxis.d2p(t.y)-offset,offset*2,offset*2)}e.hide(this.mouseTrack),this.prevHit=null}n.restore()},hit:function(e){var n=this.options,r=this.prevHit,i,s,o,u,a,f,l,c,h;if(this.series.length===0)return;h={relX:e.relX,relY:e.relY,absX:e.absX,absY:e.absY,series:this.series};if(n.mouse.trackY&&!n.mouse.trackAll&&this.hit.executeOnType(this.series,"hit",[e,h])&&!t.isUndefined(h.seriesIndex))a=this.series[h.seriesIndex],h.series=a,h.mouse=a.mouse,h.xaxis=a.xaxis,h.yaxis=a.yaxis;else{i=this.hit.closest(e);if(i){i=n.mouse.trackY?i.point:i.x,u=i.seriesIndex,a=this.series[u],l=a.xaxis,c=a.yaxis,s=2*a.mouse.sensibility;if(n.mouse.trackAll||i.distanceXl.xaxis.max)continue;p=Math.abs(g-v),d=Math.abs(y-m),h=p*p+d*d,h
    '),this.mouseTrack=c,e.insert(b||this.el,c));if(!g||g<0)g=0;a&&a.toFixed&&(a=a.toFixed(g)),f&&f.toFixed&&(f=f.toFixed(g)),T=n.mouse.trackFormatter({x:a,y:f,series:n.series,index:n.index,nearest:n,fraction:n.fraction});if(t.isNull(T)||t.isUndefined(T)){e.hide(c);return}c.innerHTML=T,e.show(c);if(!o)return;x=e.size(c),b&&(S=e.position(this.el),w=S.top,E=S.left);if(!n.mouse.relative)i+="top:",o.charAt(0)=="n"?i+=w+u+m:o.charAt(0)=="s"&&(i+=w-u+m+this.plotHeight-x.height),i+="px;bottom:auto;left:",o.charAt(1)=="e"?i+=E-u+p+this.plotWidth-x.width:o.charAt(1)=="w"&&(i+=E+u+p),i+="px;right:auto;";else if(s.pie&&s.pie.show){var N={x:this.plotWidth/2,y:this.plotHeight/2},C=Math.min(this.canvasWidth,this.canvasHeight)*s.pie.sizeRatio/2,k=n.sAngle=5||Math.abs(e.second.y-e.first.y)>=5}})}(),function(){var e=Flotr.DOM;Flotr.addPlugin("labels",{callbacks:{"flotr:afterdraw":function(){this.labels.draw()}},draw:function(){function b(e,t,r){var i=r?t.minorTicks:t.ticks,s=t.orientation===1,u=t.n===1,l,h;l={color:t.options.color||d.grid.color,angle:Flotr.toRad(t.options.labelsAngle),textBaseline:"middle"};for(c=0;c(s?e.plotWidth:e.plotHeight))continue;Flotr.drawText(v,n.label,l(e,s,o,a),h(e,s,o,a),u),!s&&!o&&(v.save(),v.strokeStyle=u.color,v.beginPath(),v.moveTo(e.plotOffset.left+e.plotWidth-8,e.plotOffset.top+t.d2p(n.v)),v.lineTo(e.plotOffset.left+e.plotWidth,e.plotOffset.top+t.d2p(n.v)),v.stroke(),v.restore())}}function E(e,t){var r=t.orientation===1,i=t.n===1,o="",u,a,f,l=e.plotOffset;!r&&!i&&(v.save(),v.strokeStyle=t.options.color||d.grid.color,v.beginPath());if(t.options.showLabels&&(i?!0:t.used))for(c=0;c(r?e.canvasWidth:e.canvasHeight))continue;f=l.top+(r?(i?1:-1)*(e.plotHeight+d.grid.labelMargin):t.d2p(n.v)-t.maxLabel.height/2),u=r?l.left+t.d2p(n.v)-s/2:0,o="",c===0?o=" first":c===t.ticks.length-1&&(o=" last"),o+=r?" flotr-grid-label-x":" flotr-grid-label-y",h+=['
    '+n.label+"
    "].join(" "),!r&&!i&&(v.moveTo(l.left+e.plotWidth-8,l.top+t.d2p(n.v)),v.lineTo(l.left+e.plotWidth,l.top+t.d2p(n.v)))}}var t,n,r,i,s,o,u,a,f,l,c,h="",p=0,d=this.options,v=this.ctx,m=this.axes,g={size:d.fontSize};for(c=0;c-1;--p){if(!n[p].label||n[p].hide)continue;d=s.labelFormatter(n[p].label),E=Math.max(E,this._text.measureText(d,S).width)}var x=Math.round(m+y*3+E),T=Math.round(f*(y+g)+y);!h&&h!==0&&(h=.1);if(!i.HtmlText&&this.textEnabled&&!s.container){l.charAt(0)=="s"&&(w=r.top+this.plotHeight-(c+T)),l.charAt(0)=="c"&&(w=r.top+this.plotHeight/2-(c+T/2)),l.charAt(1)=="e"&&(b=r.left+this.plotWidth-(c+x)),v=this.processColor(s.backgroundColor,{opacity:h}),a.fillStyle=v,a.fillRect(b,w,x,T),a.strokeStyle=s.labelBoxBorderColor,a.strokeRect(Flotr.toPixel(b),Flotr.toPixel(w),x,T);var N=b+y,C=w+y;for(p=0;p":""),u=!0);var k=n[p],L=s.labelBoxWidth,A=s.labelBoxHeight;d=s.labelFormatter(k.label),v="background-color:"+(k.bars&&k.bars.show&&k.bars.fillColor&&k.bars.fill?k.bars.fillColor:k.color)+";",o.push('','
    ','
    ','
    ',"
    ","
    ","",'',d,"")}u&&o.push("");if(o.length>0){var O=''+o.join("")+"
    ";if(s.container)O=e.node(O),this.legend.markup=O,e.insert(s.container,O);else{var M={position:"absolute",zIndex:"2",border:"1px solid "+s.labelBoxBorderColor};l.charAt(0)=="n"?(M.top=c+r.top+"px",M.bottom="auto"):l.charAt(0)=="c"?(M.top=c+(this.plotHeight-T)/2+"px",M.bottom="auto"):l.charAt(0)=="s"&&(M.bottom=c+r.bottom+"px",M.top="auto"),l.charAt(1)=="e"?(M.right=c+r.right+"px",M.left="auto"):l.charAt(1)=="w"&&(M.left=c+r.left+"px",M.right="auto");var P=e.create("div"),H;P.className="flotr-legend",e.setStyles(P,M),e.insert(P,O),e.insert(this.el,P);if(!h)return;var B=s.backgroundColor||i.grid.backgroundColor||"#ffffff";t.extend(M,e.size(P),{backgroundColor:B,zIndex:"",border:""}),M.width+="px",M.height+="px",P=e.create("div"),P.className="flotr-legend-bg",e.setStyles(P,M),e.opacity(P,h),e.insert(P," "),e.insert(this.el,P)}}}}}})}(),function(){function e(e){if(this.options.spreadsheet.tickFormatter)return this.options.spreadsheet.tickFormatter(e);var t=n.find(this.axes.x.ticks,function(t){return t.v==e});return t?t.label:e}var t=Flotr.DOM,n=Flotr._;Flotr.addPlugin("spreadsheet",{options:{show:!1,tabGraphLabel:"Graph",tabDataLabel:"Data",toolbarDownload:"Download CSV",toolbarSelectAll:"Select all",csvFileSeparator:",",decimalSeparator:".",tickFormatter:null,initialTab:"graph"},callbacks:{"flotr:afterconstruct":function(){if(!this.options.spreadsheet.show)return;var e=this.spreadsheet,n=t.node('
    '),r=t.node('
    '+this.options.spreadsheet.tabGraphLabel+"
    "),i=t.node('
    '+this.options.spreadsheet.tabDataLabel+"
    "),s;e.tabsContainer=n,e.tabs={graph:r,data:i},t.insert(n,r),t.insert(n,i),t.insert(this.el,n),s=t.size(i).height+2,this.plotOffset.bottom+=s,t.setStyles(n,{top:this.canvasHeight-s+"px"}),this.observe(r,"click",function(){e.showTab("graph")}).observe(i,"click",function(){e.showTab("data")}),this.options.spreadsheet.initialTab!=="graph"&&e.showTab(this.options.spreadsheet.initialTab)}},loadDataGrid:function(){if(this.seriesData)return this.seriesData;var e=this.series,t={};return n.each(e,function(e,r){n.each(e.data,function(e){var n=e[0],s=e[1],o=t[n];if(o)o[r+1]=s;else{var u=[];u[0]=n,u[r+1]=s,t[n]=u}})}),this.seriesData=n.sortBy(t,function(e,t){return parseInt(t,10)}),this.seriesData},constructDataGrid:function(){if(this.spreadsheet.datagrid)return this.spreadsheet.datagrid;var r=this.series,i=this.spreadsheet.loadDataGrid(),s=[""],o,u,a,f=[''];f.push(""),n.each(r,function(e,t){f.push('"),s.push("")}),f.push(""),n.each(i,function(t){f.push(""),n.times(r.length+1,function(r){var i="td",s=t[r],o=n.isUndefined(s)?"":Math.round(s*1e5)/1e5;if(r===0){i="th";var u=e.call(this,o);u&&(o=u)}f.push("<"+i+(i=="th"?' scope="row"':"")+">"+o+"")},this),f.push("")},this),s.push(""),a=t.node(f.join("")),o=t.node('"),u=t.node('"),this.observe(o,"click",n.bind(this.spreadsheet.downloadCSV,this)).observe(u,"click",n.bind(this.spreadsheet.selectAllData,this));var l=t.node('
    ');t.insert(l,o),t.insert(l,u);var c=this.canvasHeight-t.size(this.spreadsheet.tabsContainer).height-2,h=t.node('
    ');return t.insert(h,l),t.insert(h,a),t.insert(this.el,h),this.spreadsheet.datagrid=a,this.spreadsheet.container=h,a},showTab:function(e){if(this.spreadsheet.activeTab===e)return;switch(e){case"graph":t.hide(this.spreadsheet.container),t.removeClass(this.spreadsheet.tabs.data,"selected"),t.addClass(this.spreadsheet.tabs.graph,"selected");break;case"data":this.spreadsheet.datagrid||this.spreadsheet.constructDataGrid(),t.show(this.spreadsheet.container),t.addClass(this.spreadsheet.tabs.data,"selected"),t.removeClass(this.spreadsheet.tabs.graph,"selected");break;default:throw"Illegal tab name: "+e}this.spreadsheet.activeTab=e},selectAllData:function(){if(this.spreadsheet.tabs){var e,t,n,r,i=this.spreadsheet.constructDataGrid();return this.spreadsheet.showTab("data"),setTimeout(function(){(n=i.ownerDocument)&&(r=n.defaultView)&&r.getSelection&&n.createRange&&(e=window.getSelection())&&e.removeAllRanges?(t=n.createRange(),t.selectNode(i),e.removeAllRanges(),e.addRange(t)):document.body&&document.body.createTextRange&&(t=document.body.createTextRange())&&(t.moveToElementText(i),t.select())},0),!0}return!1},downloadCSV:function(){var t="",r=this.series,i=this.options,s=this.spreadsheet.loadDataGrid(),o=encodeURIComponent(i.spreadsheet.csvFileSeparator);if(i.spreadsheet.decimalSeparator===i.spreadsheet.csvFileSeparator)throw"The decimal separator is the same as the column separator ("+i.spreadsheet.decimalSeparator+")";n.each(r,function(e,n){t+=o+'"'+(e.label||String.fromCharCode(65+n)).replace(/\"/g,'\\"')+'"'}),t+="%0D%0A",t+=n.reduce(s,function(t,n){var r=e.call(this,n[0])||"";r='"'+(r+"").replace(/\"/g,'\\"')+'"';var s=n.slice(1).join(o);return i.spreadsheet.decimalSeparator!=="."&&(s=s.replace(/\./g,i.spreadsheet.decimalSeparator)),t+r+o+s+"%0D%0A"},"",this),Flotr.isIE&&Flotr.isIE<9?(t=t.replace(new RegExp(o,"g"),decodeURIComponent(o)).replace(/%0A/g,"\n").replace(/%0D/g,"\r"),window.open().document.write(t)):window.open("data:text/csv,"+t)}})}(),function(){var e=Flotr.DOM;Flotr.addPlugin("titles",{callbacks:{"flotr:afterdraw":function(){this.titles.drawTitles()}},drawTitles:function(){var t,n=this.options,r=n.grid.labelMargin,i=this.ctx,s=this.axes;if(!n.HtmlText&&this.textEnabled){var o={size:n.fontSize,color:n.grid.color,textAlign:"center"};n.subtitle&&Flotr.drawText(i,n.subtitle,this.plotOffset.left+this.plotWidth/2,this.titleHeight+this.subtitleHeight-2,o),o.weight=1.5,o.size*=1.5,n.title&&Flotr.drawText(i,n.title,this.plotOffset.left+this.plotWidth/2,this.titleHeight-2,o),o.weight=1.8,o.size*=.8,s.x.options.title&&s.x.used&&(o.textAlign=s.x.options.titleAlign||"center",o.textBaseline="top",o.angle=Flotr.toRad(s.x.options.titleAngle),o=Flotr.getBestTextAlign(o.angle,o),Flotr.drawText(i,s.x.options.title,this.plotOffset.left+this.plotWidth/2,this.plotOffset.top+s.x.maxLabel.height+this.plotHeight+2*r,o)),s.x2.options.title&&s.x2.used&&(o.textAlign=s.x2.options.titleAlign||"center",o.textBaseline="bottom",o.angle=Flotr.toRad(s.x2.options.titleAngle),o=Flotr.getBestTextAlign(o.angle,o),Flotr.drawText(i,s.x2.options.title,this.plotOffset.left+this.plotWidth/2,this.plotOffset.top-s.x2.maxLabel.height-2*r,o)),s.y.options.title&&s.y.used&&(o.textAlign=s.y.options.titleAlign||"right",o.textBaseline="middle",o.angle=Flotr.toRad(s.y.options.titleAngle),o=Flotr.getBestTextAlign(o.angle,o),Flotr.drawText(i,s.y.options.title,this.plotOffset.left-s.y.maxLabel.width-2*r,this.plotOffset.top+this.plotHeight/2,o)),s.y2.options.title&&s.y2.used&&(o.textAlign=s.y2.options.titleAlign||"left",o.textBaseline="middle",o.angle=Flotr.toRad(s.y2.options.titleAngle),o=Flotr.getBestTextAlign(o.angle,o),Flotr.drawText(i,s.y2.options.title,this.plotOffset.left+this.plotWidth+s.y2.maxLabel.width+2*r,this.plotOffset.top+this.plotHeight/2,o))}else{t=[],n.title&&t.push('
    ',n.title,"
    "),n.subtitle&&t.push('
    ',n.subtitle,"
    "),t.push(""),t.push('
    '),s.x.options.title&&s.x.used&&t.push('
    ',s.x.options.title,"
    "),s.x2.options.title&&s.x2.used&&t.push('
    ',s.x2.options.title,"
    "),s.y.options.title&&s.y.used&&t.push('
    ',s.y.options.title,"
    "),s.y2.options.title&&s.y2.used&&t.push('
    ',s.y2.options.title,"
    "),t=t.join("");var u=e.create("div");e.setStyles({color:n.grid.color}),u.className="flotr-titles",e.insert(this.el,u),e.insert(u,t)}}})}(); diff --git a/p/themes/Origine/freshrss.css b/p/themes/Origine/freshrss.css index 495477a86..78adbeff0 100644 --- a/p/themes/Origine/freshrss.css +++ b/p/themes/Origine/freshrss.css @@ -898,3 +898,46 @@ input.extend { text-decoration: underline; } } + +.stat{ + border:1px solid #aaa; + border-radius:10px; + box-shadow:2px 2px 5px #aaa; + margin: 10px 0; +} +.stat h2{ + border-bottom:1px solid #aaa; + margin:0; + padding-left:5px; +} +.stat h2 + *{ + margin:5px; +} +.stat h2 + div + div{ + margin:0 5px 5px 5px; +} +.stat h2 + table{ + border-collapse:collapse; + width:calc(100% - 10px); +} +.stat h2 + table th{ + text-transform:capitalize; +} +.stat h2 + table td{ + text-align:center; +} +.stat h2 + table thead th{ + border-bottom:2px solid #aaa; +} +.stat h2 + table tbody tr *{ + border-bottom:1px solid #aaa; +} +.stat h2 + table tbody tr:last-child *{ + border-bottom:0; +} +.stat h2 + table tr *{ + border-left:2px solid #aaa; +} +.stat h2 + table tr *:first-child{ + border-left:0; +} \ No newline at end of file -- cgit v1.2.3 From bd6a7606c0d2364fd472f7903260a72f37b31f3b Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 25 Jan 2014 23:21:01 +0100 Subject: Intégration statistiques MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quelques corrections https://github.com/marienfressinaud/FreshRSS/pull/390 en particulier correction bug chargement asynchrone JavaScript Pour https://github.com/marienfressinaud/FreshRSS/issues/90 --- app/Controllers/indexController.php | 11 +++++------ app/i18n/en.php | 10 +++++----- app/i18n/fr.php | 10 +++++----- app/views/index/stats.phtml | 15 +++++++++++++-- 4 files changed, 28 insertions(+), 18 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 84ed992f9..898f2577c 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -6,14 +6,12 @@ class FreshRSS_index_Controller extends Minz_ActionController { private $entryDAO; private $feedDAO; private $catDAO; - private $statsDAO; function __construct($router) { parent::__construct($router); $this->entryDAO = new FreshRSS_EntryDAO (); $this->feedDAO = new FreshRSS_FeedDAO (); $this->catDAO = new FreshRSS_CategoryDAO (); - $this->statsDAO = new FreshRSS_StatsDAO (); } public function indexAction () { @@ -202,11 +200,12 @@ class FreshRSS_index_Controller extends Minz_ActionController { } public function statsAction () { + $statsDAO = new FreshRSS_StatsDAO (); Minz_View::appendScript (Minz_Url::display ('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js'))); - $this->view->repartition = $this->statsDAO->calculateEntryRepartition(); - $this->view->count = ($this->statsDAO->calculateEntryCount()); - $this->view->feedByCategory = $this->statsDAO->calculateFeedByCategory(); - $this->view->entryByCategory = $this->statsDAO->calculateEntryByCategory(); + $this->view->repartition = $statsDAO->calculateEntryRepartition(); + $this->view->count = ($statsDAO->calculateEntryCount()); + $this->view->feedByCategory = $statsDAO->calculateFeedByCategory(); + $this->view->entryByCategory = $statsDAO->calculateEntryByCategory(); } public function aboutAction () { diff --git a/app/i18n/en.php b/app/i18n/en.php index 963105f35..518c14bc8 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -22,7 +22,7 @@ return array ( 'subscription_management' => 'Subscriptions management', 'main_stream' => 'Main stream', - 'all_feeds' => 'All feeds', + 'all_feeds' => 'All feeds', 'favorite_feeds' => 'Favourites (%d)', 'not_read' => '%d unread', 'not_reads' => '%d unread', @@ -300,10 +300,10 @@ return array ( 'format_date' => '%s j\<\s\u\p\>S\<\/\s\u\p\> Y', 'format_date_hour' => '%s j\<\s\u\p\>S\<\/\s\u\p\> Y \a\t H\:i', - 'status_favorites' => 'favourites', - 'status_read' => 'read', - 'status_unread' => 'unread', - 'status_total' => 'total', + 'status_favorites' => 'favourites', + 'status_read' => 'read', + 'status_unread' => 'unread', + 'status_total' => 'total', 'stats_entry_repartition' => 'Entry repartition', 'stats_entry_per_day' => 'Entry per day (last 30 days)', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index c8b03ef8b..efe4a08c4 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -22,7 +22,7 @@ return array ( 'subscription_management' => 'Gestion des abonnements', 'main_stream' => 'Flux principal', - 'all_feeds' => 'Tous les flux', + 'all_feeds' => 'Tous les flux', 'favorite_feeds' => 'Favoris (%d)', 'not_read' => '%d non lu', 'not_reads' => '%d non lus', @@ -300,10 +300,10 @@ return array ( 'format_date' => 'j %s Y', 'format_date_hour' => 'j %s Y \à H\:i', - 'status_favorites' => 'favoris', - 'status_read' => 'lus', - 'status_unread' => 'non lus', - 'status_total' => 'total', + 'status_favorites' => 'favoris', + 'status_read' => 'lus', + 'status_unread' => 'non lus', + 'status_total' => 'total', 'stats_entry_repartition' => 'Répartition des articles', 'stats_entry_per_day' => 'Nombre d’articles par jour (30 derniers jours)', diff --git a/app/views/index/stats.phtml b/app/views/index/stats.phtml index ab9d0a881..b1f0b8eb3 100644 --- a/app/views/index/stats.phtml +++ b/app/views/index/stats.phtml @@ -58,6 +58,15 @@
    \ No newline at end of file +} +initStats(); + -- cgit v1.2.3 From 3930235b1868a10aeefb88e9e860cec0a4a604d3 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 25 Jan 2014 23:44:36 +0100 Subject: indexController : Charge les DAO uniquement lorsque nécessaire MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Poursuit le raisonnement de https://github.com/marienfressinaud/FreshRSS/commit/bd6a7606c0d2364fd472f7903260a72f37b31f3b Petites corrections https://github.com/marienfressinaud/FreshRSS/issues/90 --- app/Controllers/indexController.php | 28 +++++++++++----------------- app/i18n/en.php | 8 ++++---- app/i18n/fr.php | 4 ++-- app/views/index/stats.phtml | 2 +- 4 files changed, 18 insertions(+), 24 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 898f2577c..74883962d 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -1,18 +1,7 @@ entryDAO = new FreshRSS_EntryDAO (); - $this->feedDAO = new FreshRSS_FeedDAO (); - $this->catDAO = new FreshRSS_CategoryDAO (); - } public function indexAction () { $output = Minz_Request::param ('output'); @@ -50,8 +39,11 @@ class FreshRSS_index_Controller extends Minz_ActionController { Minz_View::appendScript (Minz_Url::display ('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js'))); } - $this->view->cat_aside = $this->catDAO->listCategories (); - $this->view->nb_favorites = $this->entryDAO->countUnreadReadFavorites (); + $catDAO = new FreshRSS_CategoryDAO(); + $entryDAO = new FreshRSS_EntryDAO(); + + $this->view->cat_aside = $catDAO->listCategories (); + $this->view->nb_favorites = $entryDAO->countUnreadReadFavorites (); $this->view->currentName = ''; $this->view->get_c = ''; @@ -125,14 +117,14 @@ class FreshRSS_index_Controller extends Minz_ActionController { $keepHistoryDefault = $this->view->conf->keep_history_default; try { - $entries = $this->entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min, $keepHistoryDefault); + $entries = $entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min, $keepHistoryDefault); // Si on a récupéré aucun article "non lus" // on essaye de récupérer tous les articles if ($state === 'not_read' && empty($entries)) { Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG); $this->view->state = 'all'; - $entries = $this->entryDAO->listWhere($getType, $getId, 'all', $order, $nb, $first, $filter, $date_min, $keepHistoryDefault); + $entries = $entryDAO->listWhere($getType, $getId, 'all', $order, $nb, $first, $filter, $date_min, $keepHistoryDefault); } if (count($entries) <= $nb) { @@ -170,7 +162,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { case 'c': $cat = isset($this->view->cat_aside[$getId]) ? $this->view->cat_aside[$getId] : null; if ($cat === null) { - $cat = $this->catDAO->searchById ($getId); + $catDAO = new FreshRSS_CategoryDAO(); + $cat = $catDAO->searchById($getId); } if ($cat) { $this->view->currentName = $cat->name (); @@ -183,7 +176,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { case 'f': $feed = FreshRSS_CategoryDAO::findFeed($this->view->cat_aside, $getId); if (empty($feed)) { - $feed = $this->feedDAO->searchById ($getId); + $feedDAO = new FreshRSS_FeedDAO(); + $feed = $feedDAO->searchById($getId); } if ($feed) { $this->view->currentName = $feed->name (); diff --git a/app/i18n/en.php b/app/i18n/en.php index 518c14bc8..70fd8a74b 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -305,8 +305,8 @@ return array ( 'status_unread' => 'unread', 'status_total' => 'total', - 'stats_entry_repartition' => 'Entry repartition', - 'stats_entry_per_day' => 'Entry per day (last 30 days)', - 'stats_feed_per_category' => 'Feed per category', - 'stats_entry_per_category' => 'Entry per category', + 'stats_entry_repartition' => 'Entries repartition', + 'stats_entry_per_day' => 'Entries per day (last 30 days)', + 'stats_feed_per_category' => 'Feeds per category', + 'stats_entry_per_category' => 'Entries per category', ); diff --git a/app/i18n/fr.php b/app/i18n/fr.php index efe4a08c4..53645477b 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -307,6 +307,6 @@ return array ( 'stats_entry_repartition' => 'Répartition des articles', 'stats_entry_per_day' => 'Nombre d’articles par jour (30 derniers jours)', - 'stats_feed_per_category' => 'Flux par categorie', - 'stats_entry_per_category' => 'Article par categorie', + 'stats_feed_per_category' => 'Flux par catégorie', + 'stats_entry_per_category' => 'Articles par catégorie', ); diff --git a/app/views/index/stats.phtml b/app/views/index/stats.phtml index b1f0b8eb3..e531a31dc 100644 --- a/app/views/index/stats.phtml +++ b/app/views/index/stats.phtml @@ -8,7 +8,7 @@
     '+(e.label||String.fromCharCode(65+t))+"
    - + -- cgit v1.2.3 From b0641fd0ebcaa02dcbeb2983703bff6516f93487 Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Sun, 26 Jan 2014 08:54:52 -0500 Subject: Modification des statistiques. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ajout de la liste des 10 plus gros contributeurs Ajout de règles CSS pour les thèmes Flat et Dark Modification de l'affichage des camemberts (tri par nombre qui remplace le tri alphabétique) --- app/Controllers/indexController.php | 1 + app/Models/StatsDAO.php | 29 +++++++++++++++++++++++- app/i18n/en.php | 4 ++++ app/i18n/fr.php | 4 ++++ app/views/index/stats.phtml | 37 +++++++++++++++++++++++------- p/themes/Dark/freshrss.css | 45 +++++++++++++++++++++++++++++++++++++ p/themes/Flat/freshrss.css | 45 +++++++++++++++++++++++++++++++++++++ p/themes/Origine/freshrss.css | 5 ++++- 8 files changed, 160 insertions(+), 10 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 74883962d..cb6be6049 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -200,6 +200,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { $this->view->count = ($statsDAO->calculateEntryCount()); $this->view->feedByCategory = $statsDAO->calculateFeedByCategory(); $this->view->entryByCategory = $statsDAO->calculateEntryByCategory(); + $this->view->topFeed = $statsDAO->calculateTopFeed(); } public function aboutAction () { diff --git a/app/Models/StatsDAO.php b/app/Models/StatsDAO.php index a317c8f27..60cec7847 100644 --- a/app/Models/StatsDAO.php +++ b/app/Models/StatsDAO.php @@ -122,6 +122,7 @@ FROM {$this->prefix}category AS c, {$this->prefix}feed AS f WHERE c.id = f.category GROUP BY label +ORDER BY data DESC SQL; $stm = $this->bd->prepare($sql); $stm->execute(); @@ -146,6 +147,7 @@ FROM {$this->prefix}category AS c, WHERE c.id = f.category AND f.id = e.id_feed GROUP BY label +ORDER BY data DESC SQL; $stm = $this->bd->prepare($sql); $stm->execute(); @@ -154,6 +156,31 @@ SQL; return $this->convertToPieSerie($res); } + /** + * Calculates the 10 top feeds based on their number of entries + * + * @return array + */ + public function calculateTopFeed() { + $sql = <<prefix}category AS c, +{$this->prefix}feed AS f, +{$this->prefix}entry AS e +WHERE c.id = f.category +AND f.id = e.id_feed +GROUP BY id +ORDER BY count DESC +LIMIT 10 +SQL; + $stm = $this->bd->prepare($sql); + $stm->execute(); + return $stm->fetchAll(PDO::FETCH_ASSOC); + } + private function convertToSerie($data) { $serie = array(); @@ -168,7 +195,7 @@ SQL; $serie = array(); foreach ($data as $value) { - $value['data'] = array(array(0, (int)$value['data'])); + $value['data'] = array(array(0, (int) $value['data'])); $serie[] = $value; } diff --git a/app/i18n/en.php b/app/i18n/en.php index 70fd8a74b..e5cf623c7 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -11,6 +11,8 @@ return array ( 'users' => 'Users', 'categories' => 'Categories', 'category' => 'Category', + 'feed' => 'Feed', + 'feeds' => 'Feeds', 'shortcuts' => 'Shortcuts', 'about' => 'About', 'stats' => 'Statistics', @@ -309,4 +311,6 @@ return array ( 'stats_entry_per_day' => 'Entries per day (last 30 days)', 'stats_feed_per_category' => 'Feeds per category', 'stats_entry_per_category' => 'Entries per category', + 'stats_top_feed' => 'Top ten feeds', + 'stats_entry_count' => 'Entry count', ); diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 53645477b..693dce5d1 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -11,6 +11,8 @@ return array ( 'users' => 'Utilisateurs', 'categories' => 'Catégories', 'category' => 'Catégorie', + 'feed' => 'Flux', + 'feeds' => 'Flux', 'shortcuts' => 'Raccourcis', 'about' => 'À propos', 'stats' => 'Statistiques', @@ -309,4 +311,6 @@ return array ( 'stats_entry_per_day' => 'Nombre d’articles par jour (30 derniers jours)', 'stats_feed_per_category' => 'Flux par catégorie', 'stats_entry_per_category' => 'Articles par catégorie', + 'stats_top_feed' => 'Les dix plus gros flux', + 'stats_entry_count' => 'Nombre d’articles', ); diff --git a/app/views/index/stats.phtml b/app/views/index/stats.phtml index 487c50a4b..ae9db172b 100644 --- a/app/views/index/stats.phtml +++ b/app/views/index/stats.phtml @@ -16,23 +16,23 @@ - - + + - - + + - - + + - - + +
      
    repartition['main_stream']['total']?>repartition['all_feeds']['total']?>repartition['main_stream']['total']?>repartition['all_feeds']['total']?>
    repartition['main_stream']['read']?>repartition['all_feeds']['read']?>repartition['main_stream']['read']?>repartition['all_feeds']['read']?>
    repartition['main_stream']['unread']?>repartition['all_feeds']['unread']?>repartition['main_stream']['unread']?>repartition['all_feeds']['unread']?>
    repartition['main_stream']['favorite']?>repartition['all_feeds']['favorite']?>repartition['main_stream']['favorite']?>repartition['all_feeds']['favorite']?>
    @@ -55,6 +55,27 @@
    +
    +

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

    + +
    +

    + +
      + idleFeeds['lastWeek'] as $feed): ?> +
    • + +
    +
    + +
    +

    + +
      + idleFeeds['lastMonth'] as $feed): ?> +
    • + +
    +
    + +
    +

    + +
      + idleFeeds['last3Month'] as $feed): ?> +
    • + +
    +
    + +
    +

    + +
      + idleFeeds['last6Month'] as $feed): ?> +
    • + +
    +
    + +
    +

    + +
      + idleFeeds['lastYear'] as $feed): ?> +
    • + +
    +
    +
    \ No newline at end of file diff --git a/app/views/stats/main.phtml b/app/views/stats/main.phtml new file mode 100644 index 000000000..fe372e221 --- /dev/null +++ b/app/views/stats/main.phtml @@ -0,0 +1,127 @@ +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): ?> + + + + + + + +
    +
    +
    + + -- cgit v1.2.3 From 300c508d9e4732a7c6e8025ee62f934f7ecc5328 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 1 Jul 2014 23:29:53 +0200 Subject: Automatically correct cache number of unread articles --- app/Controllers/indexController.php | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 4fcc3176f..0f244f9b6 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -126,6 +126,12 @@ class FreshRSS_index_Controller extends Minz_ActionController { // on essaye de récupérer tous les articles if ($state === FreshRSS_Entry::STATE_NOT_READ && empty($entries) && ($state_param === null)) { Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG); + $feedDAO = new FreshRSS_FeedDAO(); + try { + $feedDAO->updateCachedValues(); + } catch (Exception $ex) { + Minz_Log::record ('Failed to automatically correct nbNotRead! ' + $ex->getMessage(), Minz_Log::DEBUG); + } $this->view->state = FreshRSS_Entry::STATE_ALL; $entries = $entryDAO->listWhere($getType, $getId, $this->view->state, $order, $nb, $first, $filter, $date_min, true, $keepHistoryDefault); } -- cgit v1.2.3 From 5b06f6cd944ec56c8cc58b019f07ea65d415224f Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 2 Jul 2014 00:17:08 +0200 Subject: Minz_Log::NOTICE instead of DEBUG if failing to update cache of unread articles https://github.com/marienfressinaud/FreshRSS/commit/300c508d9e4732a7c6e8025ee62f934f7ecc5328 --- app/Controllers/indexController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 0f244f9b6..c3f39fbe5 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -130,7 +130,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { try { $feedDAO->updateCachedValues(); } catch (Exception $ex) { - Minz_Log::record ('Failed to automatically correct nbNotRead! ' + $ex->getMessage(), Minz_Log::DEBUG); + Minz_Log::record ('Failed to automatically correct nbNotRead! ' + $ex->getMessage(), Minz_Log::NOTICE); } $this->view->state = FreshRSS_Entry::STATE_ALL; $entries = $entryDAO->listWhere($getType, $getId, $this->view->state, $order, $nb, $first, $filter, $date_min, true, $keepHistoryDefault); -- cgit v1.2.3 From d6f414108667f32fe2b480adeb7ec9c218db2f4a Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 3 Jul 2014 21:26:30 +0200 Subject: Preparation for SQLite https://github.com/marienfressinaud/FreshRSS/issues/100 --- app/Controllers/configureController.php | 2 +- app/Controllers/entryController.php | 6 +- app/Controllers/feedController.php | 4 +- app/Controllers/importExportController.php | 2 +- app/Controllers/indexController.php | 2 +- app/Controllers/usersController.php | 6 +- app/Models/CategoryDAO.php | 12 ++-- app/Models/Entry.php | 2 +- app/Models/EntryDAO.php | 101 +++++++++++++++++------------ app/Models/EntryDAO_SQLite.php | 42 ++++++++++++ app/Models/Factory.php | 13 ++++ app/Models/FeedDAO.php | 46 ++++++------- app/Models/UserDAO.php | 31 ++++++--- app/SQL/install.sql.mysql.php | 60 +++++++++++++++++ app/SQL/install.sql.sqlite.php | 57 ++++++++++++++++ app/sql.php | 58 ----------------- lib/Minz/Configuration.php | 80 ++++++++++++++--------- lib/Minz/ModelPdo.php | 52 +++++++++------ 18 files changed, 375 insertions(+), 201 deletions(-) create mode 100644 app/Models/EntryDAO_SQLite.php create mode 100644 app/Models/Factory.php create mode 100644 app/SQL/install.sql.mysql.php create mode 100644 app/SQL/install.sql.sqlite.php delete mode 100644 app/sql.php (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 89130cae4..cb8e6528f 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -291,7 +291,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { Minz_View::prependTitle(Minz_Translate::t('archiving_configuration') . ' · '); - $entryDAO = new FreshRSS_EntryDAO(); + $entryDAO = FreshRSS_Factory::createEntryDao(); $this->view->nb_total = $entryDAO->count(); $this->view->size_user = $entryDAO->size(); diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index bbcb990f5..c2d897cf3 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -43,7 +43,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $nextGet = Minz_Request::param ('nextGet', $get); $idMax = Minz_Request::param ('idMax', 0); - $entryDAO = new FreshRSS_EntryDAO (); + $entryDAO = FreshRSS_Factory::createEntryDao(); if ($id == false) { if (!$get) { $entryDAO->markReadEntries ($idMax); @@ -85,7 +85,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $id = Minz_Request::param ('id'); if ($id) { - $entryDAO = new FreshRSS_EntryDAO (); + $entryDAO = FreshRSS_Factory::createEntryDao(); $entryDAO->markFavorite ($id, (bool)(Minz_Request::param ('is_favorite', true))); } } @@ -97,7 +97,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { // La table des entrées a tendance à grossir énormément // Cette action permet d'optimiser cette table permettant de grapiller un peu de place // Cette fonctionnalité n'est à appeler qu'occasionnellement - $entryDAO = new FreshRSS_EntryDAO(); + $entryDAO = FreshRSS_Factory::createEntryDao(); $entryDAO->optimizeTable(); $feedDAO = new FreshRSS_FeedDAO(); diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 5f5a40bc7..149557a48 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -102,7 +102,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $is_read = $this->view->conf->mark_when['reception'] ? 1 : 0; - $entryDAO = new FreshRSS_EntryDAO (); + $entryDAO = FreshRSS_Factory::createEntryDao(); $entries = array_reverse($feed->entries()); //We want chronological order and SimplePie uses reverse order // on calcule la date des articles les plus anciens qu'on accepte @@ -217,7 +217,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { @set_time_limit(300); $feedDAO = new FreshRSS_FeedDAO (); - $entryDAO = new FreshRSS_EntryDAO (); + $entryDAO = FreshRSS_Factory::createEntryDao(); Minz_Session::_param('actualize_feeds', false); $id = Minz_Request::param ('id'); diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 3cd791781..12154d9b6 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -12,7 +12,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { require_once(LIB_PATH . '/lib_opml.php'); $this->catDAO = new FreshRSS_CategoryDAO(); - $this->entryDAO = new FreshRSS_EntryDAO(); + $this->entryDAO = FreshRSS_Factory::createEntryDao(); $this->feedDAO = new FreshRSS_FeedDAO(); } diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index c3f39fbe5..46c9518c9 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -45,7 +45,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { } $catDAO = new FreshRSS_CategoryDAO(); - $entryDAO = new FreshRSS_EntryDAO(); + $entryDAO = FreshRSS_Factory::createEntryDao(); $this->view->cat_aside = $catDAO->listCategories (); $this->view->nb_favorites = $entryDAO->countUnreadReadFavorites (); diff --git a/app/Controllers/usersController.php b/app/Controllers/usersController.php index 38b8f829b..35fa3675f 100644 --- a/app/Controllers/usersController.php +++ b/app/Controllers/usersController.php @@ -99,7 +99,8 @@ class FreshRSS_users_Controller extends Minz_ActionController { public function createAction() { if (Minz_Request::isPost() && Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { - require_once(APP_PATH . '/sql.php'); + $db = Minz_Configuration::dataBase(); + require_once(APP_PATH . '/SQL/sql.' . $db['type'] . '.php'); $new_user_language = Minz_Request::param('new_user_language', $this->view->conf->language); if (!in_array($new_user_language, $this->view->conf->availableLanguages())) { @@ -170,7 +171,8 @@ class FreshRSS_users_Controller extends Minz_ActionController { public function deleteAction() { if (Minz_Request::isPost() && Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { - require_once(APP_PATH . '/sql.php'); + $db = Minz_Configuration::dataBase(); + require_once(APP_PATH . '/SQL/sql.' . $db['type'] . '.php'); $username = Minz_Request::param('username'); $ok = ctype_alnum($username); diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index 6a9b839b9..3003dea0d 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -12,8 +12,8 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $this->bd->lastInsertId(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error addCategory: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -43,8 +43,8 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error updateCategory: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -58,8 +58,8 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error deleteCategory: ' . $info[2], Minz_Log::ERROR); return false; } } diff --git a/app/Models/Entry.php b/app/Models/Entry.php index fa9066d5b..a24a902d5 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -154,7 +154,7 @@ class FreshRSS_Entry extends Minz_Model { // Gestion du contenu // On cherche à récupérer les articles en entier... même si le flux ne le propose pas if ($pathEntries) { - $entryDAO = new FreshRSS_EntryDAO(); + $entryDAO = FreshRSS_Factory::createEntryDao(); $entry = $entryDAO->searchByGuid($this->feed, $this->guid); if($entry) { diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 4e24541dc..9ba0d2128 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -1,9 +1,18 @@ prefix . 'entry`(id, guid, title, author, content_bin, link, date, is_read, is_favorite, id_feed, tags) ' - . 'VALUES(?, ?, ?, ?, COMPRESS(?), ?, ?, ?, ?, ?, ?)'; + $sql = 'INSERT INTO `' . $this->prefix . 'entry`(id, guid, title, author, ' + . ($this->isCompressed() ? 'content_bin' : 'content') + . ', link, date, is_read, is_favorite, id_feed, tags) ' + . 'VALUES(?, ?, ?, ?, ' + . ($this->isCompressed() ? 'COMPRESS(?)' : '?') + . ', ?, ?, ?, ?, ?, ?)'; $stm = $this->bd->prepare ($sql); $values = array ( @@ -23,9 +32,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $this->bd->lastInsertId(); } else { - $info = $stm->errorInfo(); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); if ((int)($info[0] / 1000) !== 23) { //Filter out "SQLSTATE Class code 23: Constraint Violation" because of expected duplicate entries - Minz_Log::record ('SQL error ' . $info[0] . ': ' . $info[1] . ' ' . $info[2] + Minz_Log::record('SQL error addEntry: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2] . ' while adding entry in feed ' . $valuesTmp['id_feed'] . ' with title: ' . $valuesTmp['title'], Minz_Log::ERROR); } /*else { Minz_Log::record ('SQL error ' . $info[0] . ': ' . $info[1] . ' ' . $info[2] @@ -78,14 +87,14 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markFavorite: ' . $info[2], Minz_Log::ERROR); return false; } } public function markRead($ids, $is_read = true) { - if (is_array($ids)) { + if (is_array($ids)) { //Many IDs at once if (count($ids) < 6) { //Speed heuristics $affected = 0; foreach ($ids as $id) { @@ -102,8 +111,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $values = array_merge($values, $ids); $stm = $this->bd->prepare($sql); if (!($stm && $stm->execute($values))) { - $info = $stm->errorInfo(); - Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack(); return false; } @@ -121,8 +130,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { . 'SET f.cache_nbEntries=x.nbEntries, f.cache_nbUnreads=x.nbUnreads'; $stm = $this->bd->prepare($sql); if (!($stm && $stm->execute())) { - $info = $stm->errorInfo(); - Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack(); return false; } @@ -134,14 +143,14 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' . 'SET e.is_read = ?,' . 'f.cache_nbUnreads=f.cache_nbUnreads' . ($is_read ? '-' : '+') . '1 ' - . 'WHERE e.id=?'; - $values = array($is_read ? 1 : 0, $ids); + . 'WHERE e.id=? AND e.is_read<>?'; + $values = array($is_read ? 1 : 0, $ids, $is_read ? 1 : 0); $stm = $this->bd->prepare($sql); if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -161,8 +170,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { if ($stm && $stm->execute ()) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadEntries: ' . $info[2], Minz_Log::ERROR); return false; } } else { @@ -179,8 +188,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $values = array ($idMax); $stm = $this->bd->prepare ($sql); if (!($stm && $stm->execute ($values))) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadEntries: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack (); return false; } @@ -198,8 +207,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { . 'SET f.cache_nbUnreads=COALESCE(x.nbUnreads, 0)'; $stm = $this->bd->prepare ($sql); if (!($stm && $stm->execute ())) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadEntries: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack (); return false; } @@ -220,8 +229,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadCat: ' . $info[2], Minz_Log::ERROR); return false; } } else { @@ -233,8 +242,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $values = array ($id, $idMax); $stm = $this->bd->prepare ($sql); if (!($stm && $stm->execute ($values))) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadCat: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack (); return false; } @@ -254,8 +263,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $values = array ($id); $stm = $this->bd->prepare ($sql); if (!($stm && $stm->execute ($values))) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadCat: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack (); return false; } @@ -278,8 +287,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadCatName: ' . $info[2], Minz_Log::ERROR); return false; } } else { @@ -293,8 +302,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $values = array($name, $idMax); $stm = $this->bd->prepare($sql); if (!($stm && $stm->execute($values))) { - $info = $stm->errorInfo(); - Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadCatName: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack(); return false; } @@ -315,8 +324,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $values = array($name); $stm = $this->bd->prepare($sql); if (!($stm && $stm->execute($values))) { - $info = $stm->errorInfo(); - Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadCatName: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack(); return false; } @@ -337,8 +346,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadFeed: ' . $info[2], Minz_Log::ERROR); return false; } } else { @@ -350,8 +359,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $values = array ($id, $idMax); $stm = $this->bd->prepare ($sql); if (!($stm && $stm->execute ($values))) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadFeed: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack (); return false; } @@ -364,8 +373,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $values = array ($id); $stm = $this->bd->prepare ($sql); if (!($stm && $stm->execute ($values))) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadFeed: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack (); return false; } @@ -378,7 +387,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { public function searchByGuid ($feed_id, $id) { // un guid est unique pour un flux donné - $sql = 'SELECT id, guid, title, author, UNCOMPRESS(content_bin) AS content, link, date, is_read, is_favorite, id_feed, tags ' + $sql = 'SELECT id, guid, title, author, ' + . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') + . ', link, date, is_read, is_favorite, id_feed, tags ' . 'FROM `' . $this->prefix . 'entry` WHERE id_feed=? AND guid=?'; $stm = $this->bd->prepare ($sql); @@ -394,7 +405,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { } public function searchById ($id) { - $sql = 'SELECT id, guid, title, author, UNCOMPRESS(content_bin) AS content, link, date, is_read, is_favorite, id_feed, tags ' + $sql = 'SELECT id, guid, title, author, ' + . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') + . ', link, date, is_read, is_favorite, id_feed, tags ' . 'FROM `' . $this->prefix . 'entry` WHERE id=?'; $stm = $this->bd->prepare ($sql); @@ -520,7 +533,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $search .= 'AND e1.tags LIKE ? '; $values[] = '%' . $word .'%'; } else { - $search .= 'AND CONCAT(e1.title, UNCOMPRESS(e1.content_bin)) LIKE ? '; + $search .= 'AND CONCAT(e1.title, ' . ($this->isCompressed() ? 'UNCOMPRESS(content_bin)' : 'content') . ') LIKE ? '; $values[] = '%' . $word .'%'; } } @@ -539,7 +552,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { public function listWhere($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) { list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filter, $date_min, $showOlderUnreadsorFavorites, $keepHistoryDefault); - $sql = 'SELECT e.id, e.guid, e.title, e.author, UNCOMPRESS(e.content_bin) AS content, e.link, e.date, e.is_read, e.is_favorite, e.id_feed, e.tags ' + $sql = 'SELECT e.id, e.guid, e.title, e.author, ' + . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') + . ', e.link, e.date, e.is_read, e.is_favorite, e.id_feed, e.tags ' . 'FROM `' . $this->prefix . 'entry` e ' . 'INNER JOIN (' . $sql diff --git a/app/Models/EntryDAO_SQLite.php b/app/Models/EntryDAO_SQLite.php new file mode 100644 index 000000000..f148f3c63 --- /dev/null +++ b/app/Models/EntryDAO_SQLite.php @@ -0,0 +1,42 @@ +markRead($id, $is_read); + } + return $affected; + } + } else { + $this->bd->beginTransaction(); + $sql = 'UPDATE `' . $this->prefix . 'entry` e SET e.is_read = ? WHERE e.id=? AND e.is_read<>?'; + $values = array($is_read ? 1 : 0, $ids, $is_read ? 1 : 0); + $stm = $this->bd->prepare($sql); + if (!($stm && $stm->execute ($values))) { + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack (); + return false; + } + $affected = $stm->rowCount(); + if ($affected > 0) { + $sql = 'UPDATE `' . $this->prefix . 'feed` f SET f.cache_nbUnreads=f.cache_nbUnreads' . ($is_read ? '-' : '+') . '1 ' + . 'WHERE f.id=(SELECT e.id_feed FROM `' . $this->prefix . 'entry` e WHERE e.id=?)'; + $values = array($ids); + $stm = $this->bd->prepare($sql); + if (!($stm && $stm->execute ($values))) { + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack (); + return false; + } + } + $this->bd->commit(); + return $affected; + } + } +} diff --git a/app/Models/Factory.php b/app/Models/Factory.php new file mode 100644 index 000000000..bea89c114 --- /dev/null +++ b/app/Models/Factory.php @@ -0,0 +1,13 @@ +execute ($values)) { return $this->bd->lastInsertId(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error addFeed: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -76,8 +76,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error updateFeed: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -106,8 +106,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error updateLastUpdate: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -130,8 +130,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error changeCategory: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -155,8 +155,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error deleteFeed: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -181,8 +181,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error deleteFeedByCategory: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -301,8 +301,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if ($stm && $stm->execute()) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error updateCachedValues: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -313,11 +313,11 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $values = array($id); $this->bd->beginTransaction (); if (!($stm && $stm->execute ($values))) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); - $this->bd->rollBack (); - return false; - } + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error truncate: ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack (); + return false; + } $affected = $stm->rowCount(); $sql = 'UPDATE `' . $this->prefix . 'feed` f ' @@ -325,8 +325,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $values = array ($id); $stm = $this->bd->prepare ($sql); if (!($stm && $stm->execute ($values))) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error truncate: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack (); return false; } @@ -350,8 +350,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if ($stm && $stm->execute ()) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error cleanOldEntries: ' . $info[2], Minz_Log::ERROR); return false; } } diff --git a/app/Models/UserDAO.php b/app/Models/UserDAO.php index a25b57f89..dcf847a62 100644 --- a/app/Models/UserDAO.php +++ b/app/Models/UserDAO.php @@ -2,33 +2,44 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { public function createUser($username) { - require_once(APP_PATH . '/sql.php'); $db = Minz_Configuration::dataBase(); + require_once(APP_PATH . '/SQL/sql.' . $db['type'] . '.php'); + + if (defined('SQL_CREATE_TABLES')) { + $sql = sprintf(SQL_CREATE_TABLES, $db['prefix'] . $username . '_', Minz_Translate::t('default_category')); + $stm = $c->prepare($sql); + $ok = $stm && $stm->execute(); + } else { + global $SQL_CREATE_TABLES; + if (is_array($SQL_CREATE_TABLES)) { + $ok = true; + foreach ($SQL_CREATE_TABLES as $instruction) { + $sql = sprintf($instruction, '', Minz_Translate::t('default_category')); + $stm = $c->prepare($sql); + $ok &= ($stm && $stm->execute()); + } + } + } - $sql = sprintf(SQL_CREATE_TABLES, $db['prefix'] . $username . '_'); - $stm = $this->bd->prepare($sql, array(PDO::ATTR_EMULATE_PREPARES => true)); - $values = array( - 'catName' => Minz_Translate::t('default_category'), - ); - if ($stm && $stm->execute($values)) { + if ($ok) { return true; } else { - $info = $stm->errorInfo(); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); return false; } } public function deleteUser($username) { - require_once(APP_PATH . '/sql.php'); $db = Minz_Configuration::dataBase(); + require_once(APP_PATH . '/SQL/sql.' . $db['type'] . '.php'); $sql = sprintf(SQL_DROP_TABLES, $db['prefix'] . $username . '_'); $stm = $this->bd->prepare($sql); if ($stm && $stm->execute()) { return true; } else { - $info = $stm->errorInfo(); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); return false; } diff --git a/app/SQL/install.sql.mysql.php b/app/SQL/install.sql.mysql.php new file mode 100644 index 000000000..d509cb4a8 --- /dev/null +++ b/app/SQL/install.sql.mysql.php @@ -0,0 +1,60 @@ +bd = self::$sharedBd; $this->prefix = self::$sharedPrefix; return; } - $db = Minz_Configuration::dataBase (); - $driver_options = null; + $db = Minz_Configuration::dataBase(); try { $type = $db['type']; - if($type == 'mysql') { - $string = $type - . ':host=' . $db['host'] + if ($type === 'mysql') { + $string = 'mysql:host=' . $db['host'] . ';dbname=' . $db['base'] . ';charset=utf8'; $driver_options = array( - PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8' + PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8', + ); + $this->prefix = $db['prefix'] . Minz_Session::param('currentUser', '_') . '_'; + } elseif ($type === 'sqlite') { + $string = 'sqlite:' . DATA_PATH . '/' . Minz_Session::param('currentUser', '_') . '.sqlite'; + $driver_options = array( + //PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + ); + $this->prefix = ''; + } else { + throw new Minz_PDOConnectionException( + 'Invalid database type!', + $db['user'], Minz_Exception::ERROR ); - } elseif($type == 'sqlite') { - $string = $type . ':/' . DATA_PATH . $db['base'] . '.sqlite'; //TODO: DEBUG UTF-8 http://www.siteduzero.com/forum/sujet/sqlite-connexion-utf-8-18797 } + self::$sharedDbType = $type; + self::$sharedPrefix = $this->prefix; - $this->bd = new FreshPDO ( + $this->bd = new FreshPDO( $string, $db['user'], $db['password'], $driver_options ); self::$sharedBd = $this->bd; - - $this->prefix = $db['prefix'] . Minz_Session::param('currentUser', '_') . '_'; - self::$sharedPrefix = $this->prefix; } catch (Exception $e) { - throw new Minz_PDOConnectionException ( + throw new Minz_PDOConnectionException( $string, $db['user'], Minz_Exception::ERROR ); @@ -81,15 +93,15 @@ class Minz_ModelPdo { } public function size($all = false) { - $db = Minz_Configuration::dataBase (); + $db = Minz_Configuration::dataBase(); $sql = 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema = ?'; - $values = array ($db['base']); + $values = array($db['base']); if (!$all) { $sql .= ' AND table_name LIKE ?'; $values[] = $this->prefix . '%'; } - $stm = $this->bd->prepare ($sql); - $stm->execute ($values); + $stm = $this->bd->prepare($sql); + $stm->execute($values); $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); return $res[0]; } @@ -107,12 +119,12 @@ class FreshPDO extends PDO { } } - public function prepare ($statement, $driver_options = array()) { + public function prepare($statement, $driver_options = array()) { FreshPDO::check($statement); return parent::prepare($statement, $driver_options); } - public function exec ($statement) { + public function exec($statement) { FreshPDO::check($statement); return parent::exec($statement); } -- cgit v1.2.3 From b34f59e85aa851b82210fdc50074918034f960db Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 3 Jul 2014 22:48:29 +0200 Subject: Preparation #3 for SQLite https://github.com/marienfressinaud/FreshRSS/issues/100 --- app/Controllers/configureController.php | 6 +++--- app/Controllers/entryController.php | 4 ++-- app/Controllers/feedController.php | 8 ++++---- app/Controllers/importExportController.php | 2 +- app/Controllers/indexController.php | 4 ++-- app/Controllers/javascriptController.php | 2 +- app/Models/Category.php | 2 +- app/Models/Entry.php | 2 +- app/Models/EntryDAOSQLite.php | 10 +++++----- app/Models/Factory.php | 9 +++++++++ app/Models/Feed.php | 4 ++-- app/Models/FeedDAO.php | 16 ++++++++-------- app/Models/FeedDAOSQLite.php | 5 +++++ p/api/greader.php | 2 +- 14 files changed, 45 insertions(+), 31 deletions(-) create mode 100644 app/Models/FeedDAOSQLite.php (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index cb8e6528f..a608df162 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -14,7 +14,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { } public function categorizeAction () { - $feedDAO = new FreshRSS_FeedDAO (); + $feedDAO = FreshRSS_Factory::createFeedDao(); $catDAO = new FreshRSS_CategoryDAO (); $defaultCategory = $catDAO->getDefault (); $defaultId = $defaultCategory->id (); @@ -70,7 +70,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $catDAO = new FreshRSS_CategoryDAO (); $this->view->categories = $catDAO->listCategories (false); - $feedDAO = new FreshRSS_FeedDAO (); + $feedDAO = FreshRSS_Factory::createFeedDao(); $this->view->feeds = $feedDAO->listFeeds (); $id = Minz_Request::param ('id'); @@ -336,7 +336,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { ); break; case 'f': - $dao = new FreshRSS_FeedDAO(); + $dao = FreshRSS_Factory::createFeedDao(); $feed = $dao->searchById(substr($query['get'], 2)); $this->view->query_get[$key] = array( 'type' => 'feed', diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index c2d897cf3..2d7fa718a 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -100,7 +100,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $entryDAO = FreshRSS_Factory::createEntryDao(); $entryDAO->optimizeTable(); - $feedDAO = new FreshRSS_FeedDAO(); + $feedDAO = FreshRSS_Factory::createFeedDao(); $feedDAO->updateCachedValues(); invalidateHttpCache(); @@ -124,7 +124,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $nb_month_old = max($this->view->conf->old_entries, 1); $date_min = time() - (3600 * 24 * 30 * $nb_month_old); - $feedDAO = new FreshRSS_FeedDAO(); + $feedDAO = FreshRSS_Factory::createFeedDao(); $feeds = $feedDAO->listFeedsOrderUpdate(); $nbTotal = 0; diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 149557a48..d30b60877 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -31,7 +31,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { ), true); } - $feedDAO = new FreshRSS_FeedDAO (); + $feedDAO = FreshRSS_Factory::createFeedDao(); $this->catDAO = new FreshRSS_CategoryDAO (); $this->catDAO->checkDefault (); @@ -201,7 +201,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { public function truncateAction () { if (Minz_Request::isPost ()) { $id = Minz_Request::param ('id'); - $feedDAO = new FreshRSS_FeedDAO (); + $feedDAO = FreshRSS_Factory::createFeedDao(); $n = $feedDAO->truncate($id); $notif = array( 'type' => $n === false ? 'bad' : 'good', @@ -216,7 +216,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { public function actualizeAction () { @set_time_limit(300); - $feedDAO = new FreshRSS_FeedDAO (); + $feedDAO = FreshRSS_Factory::createFeedDao(); $entryDAO = FreshRSS_Factory::createEntryDao(); Minz_Session::_param('actualize_feeds', false); @@ -375,7 +375,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $type = Minz_Request::param ('type', 'feed'); $id = Minz_Request::param ('id'); - $feedDAO = new FreshRSS_FeedDAO (); + $feedDAO = FreshRSS_Factory::createFeedDao(); if ($type == 'category') { if ($feedDAO->deleteFeedByCategory ($id)) { $notif = array ( diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 12154d9b6..6b4c3e81a 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -13,7 +13,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $this->catDAO = new FreshRSS_CategoryDAO(); $this->entryDAO = FreshRSS_Factory::createEntryDao(); - $this->feedDAO = new FreshRSS_FeedDAO(); + $this->feedDAO = FreshRSS_Factory::createFeedDao(); } public function indexAction() { diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 46c9518c9..4340865ec 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -126,7 +126,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { // on essaye de récupérer tous les articles if ($state === FreshRSS_Entry::STATE_NOT_READ && empty($entries) && ($state_param === null)) { Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG); - $feedDAO = new FreshRSS_FeedDAO(); + $feedDAO = FreshRSS_Factory::createFeedDao(); try { $feedDAO->updateCachedValues(); } catch (Exception $ex) { @@ -187,7 +187,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { case 'f': $feed = FreshRSS_CategoryDAO::findFeed($this->view->cat_aside, $getId); if (empty($feed)) { - $feedDAO = new FreshRSS_FeedDAO(); + $feedDAO = FreshRSS_Factory::createFeedDao(); $feed = $feedDAO->searchById($getId); } if ($feed) { diff --git a/app/Controllers/javascriptController.php b/app/Controllers/javascriptController.php index 3d741e298..737908c91 100755 --- a/app/Controllers/javascriptController.php +++ b/app/Controllers/javascriptController.php @@ -7,7 +7,7 @@ class FreshRSS_javascript_Controller extends Minz_ActionController { public function actualizeAction () { header('Content-Type: text/javascript; charset=UTF-8'); - $feedDAO = new FreshRSS_FeedDAO (); + $feedDAO = FreshRSS_Factory::createFeedDao(); $this->view->feeds = $feedDAO->listFeedsOrderUpdate(); } diff --git a/app/Models/Category.php b/app/Models/Category.php index 328bae799..0a0dbd3ca 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -44,7 +44,7 @@ class FreshRSS_Category extends Minz_Model { } public function feeds () { if ($this->feeds === null) { - $feedDAO = new FreshRSS_FeedDAO (); + $feedDAO = FreshRSS_Factory::createFeedDao(); $this->feeds = $feedDAO->listByCategory ($this->id ()); $this->nbFeed = 0; $this->nbNotRead = 0; diff --git a/app/Models/Entry.php b/app/Models/Entry.php index a24a902d5..0bf1f2616 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -74,7 +74,7 @@ class FreshRSS_Entry extends Minz_Model { } public function feed ($object = false) { if ($object) { - $feedDAO = new FreshRSS_FeedDAO (); + $feedDAO = FreshRSS_Factory::createFeedDao(); return $feedDAO->searchById ($this->feed); } else { return $this->feed; diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index 45d3a3ea9..0a837b3aa 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -13,24 +13,24 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { } } else { $this->bd->beginTransaction(); - $sql = 'UPDATE `' . $this->prefix . 'entry` e SET e.is_read = ? WHERE e.id=? AND e.is_read<>?'; + $sql = 'UPDATE `' . $this->prefix . 'entry` SET is_read=? WHERE id=? AND is_read<>?'; $values = array($is_read ? 1 : 0, $ids, $is_read ? 1 : 0); $stm = $this->bd->prepare($sql); if (!($stm && $stm->execute ($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR); + Minz_Log::record('SQL error markRead 1: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack (); return false; } $affected = $stm->rowCount(); if ($affected > 0) { - $sql = 'UPDATE `' . $this->prefix . 'feed` f SET f.cache_nbUnreads=f.cache_nbUnreads' . ($is_read ? '-' : '+') . '1 ' - . 'WHERE f.id=(SELECT e.id_feed FROM `' . $this->prefix . 'entry` e WHERE e.id=?)'; + $sql = 'UPDATE `' . $this->prefix . 'feed` SET cache_nbUnreads=cache_nbUnreads' . ($is_read ? '-' : '+') . '1 ' + . 'WHERE id=(SELECT e.id_feed FROM `' . $this->prefix . 'entry` e WHERE e.id=?)'; $values = array($ids); $stm = $this->bd->prepare($sql); if (!($stm && $stm->execute ($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR); + Minz_Log::record('SQL error markRead 2: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack (); return false; } diff --git a/app/Models/Factory.php b/app/Models/Factory.php index 3ef68c41d..95d21a277 100644 --- a/app/Models/Factory.php +++ b/app/Models/Factory.php @@ -2,6 +2,15 @@ class FreshRSS_Factory { + public static function createFeedDao() { + $db = Minz_Configuration::dataBase(); + if ($db['type'] === 'sqlite') { + return new FreshRSS_FeedDAOSQLite(); + } else { + return new FreshRSS_FeedDAO(); + } + } + public static function createEntryDao() { $db = Minz_Configuration::dataBase(); if ($db['type'] === 'sqlite') { diff --git a/app/Models/Feed.php b/app/Models/Feed.php index ba142c8c8..8093b2bfd 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -87,7 +87,7 @@ class FreshRSS_Feed extends Minz_Model { } public function nbEntries () { if ($this->nbEntries < 0) { - $feedDAO = new FreshRSS_FeedDAO (); + $feedDAO = FreshRSS_Factory::createFeedDao(); $this->nbEntries = $feedDAO->countEntries ($this->id ()); } @@ -95,7 +95,7 @@ class FreshRSS_Feed extends Minz_Model { } public function nbNotRead () { if ($this->nbNotRead < 0) { - $feedDAO = new FreshRSS_FeedDAO (); + $feedDAO = FreshRSS_Factory::createFeedDao(); $this->nbNotRead = $feedDAO->countNotRead ($this->id ()); } diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 3f3847aa4..9534d61bd 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -84,15 +84,15 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { public function updateLastUpdate ($id, $inError = 0, $updateCache = true) { if ($updateCache) { - $sql = 'UPDATE `' . $this->prefix . 'feed` f ' //2 sub-requests with FOREIGN KEY(e.id_feed), INDEX(e.is_read) faster than 1 request with GROUP BY or CASE - . 'SET f.cache_nbEntries=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=f.id),' - . 'f.cache_nbUnreads=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=f.id AND e2.is_read=0),' + $sql = 'UPDATE `' . $this->prefix . 'feed` ' //2 sub-requests with FOREIGN KEY(e.id_feed), INDEX(e.is_read) faster than 1 request with GROUP BY or CASE + . 'SET cache_nbEntries=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=feed.id),' + . 'cache_nbUnreads=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=feed.id AND e2.is_read=0),' . 'lastUpdate=?, error=? ' - . 'WHERE f.id=?'; + . 'WHERE id=?'; } else { - $sql = 'UPDATE `' . $this->prefix . 'feed` f ' + $sql = 'UPDATE `' . $this->prefix . 'feed` ' . 'SET lastUpdate=?, error=? ' - . 'WHERE f.id=?'; + . 'WHERE id=?'; } $values = array ( @@ -320,8 +320,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { } $affected = $stm->rowCount(); - $sql = 'UPDATE `' . $this->prefix . 'feed` f ' - . 'SET f.cache_nbEntries=0, f.cache_nbUnreads=0 WHERE f.id=?'; + $sql = 'UPDATE `' . $this->prefix . 'feed` ' + . 'SET cache_nbEntries=0, cache_nbUnreads=0 WHERE id=?'; $values = array ($id); $stm = $this->bd->prepare ($sql); if (!($stm && $stm->execute ($values))) { diff --git a/app/Models/FeedDAOSQLite.php b/app/Models/FeedDAOSQLite.php new file mode 100644 index 000000000..f3c92149e --- /dev/null +++ b/app/Models/FeedDAOSQLite.php @@ -0,0 +1,5 @@ +arrayFeedCategoryNames(); switch ($path) { -- cgit v1.2.3 From 76b64a6907e825588a795a567e3c05c3cebcab76 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 5 Jul 2014 12:08:17 +0200 Subject: Bug search Following introduction of user-queries https://github.com/marienfressinaud/FreshRSS/commit/2b8dc666345d334f50bf2f4f32f0b127edb40c3a#diff-be03dd1f6b53ebb2682ba9dd334bc273 --- app/Controllers/indexController.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 4340865ec..fdd29de4d 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -124,13 +124,13 @@ class FreshRSS_index_Controller extends Minz_ActionController { // Si on a récupéré aucun article "non lus" // on essaye de récupérer tous les articles - if ($state === FreshRSS_Entry::STATE_NOT_READ && empty($entries) && ($state_param === null)) { - Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG); + if ($state === FreshRSS_Entry::STATE_NOT_READ && empty($entries) && ($state_param === null) && ($filter == '')) { + Minz_Log::record('Conflicting information about nbNotRead!', Minz_Log::DEBUG); $feedDAO = FreshRSS_Factory::createFeedDao(); try { $feedDAO->updateCachedValues(); } catch (Exception $ex) { - Minz_Log::record ('Failed to automatically correct nbNotRead! ' + $ex->getMessage(), Minz_Log::NOTICE); + Minz_Log::record('Failed to automatically correct nbNotRead! ' + $ex->getMessage(), Minz_Log::NOTICE); } $this->view->state = FreshRSS_Entry::STATE_ALL; $entries = $entryDAO->listWhere($getType, $getId, $this->view->state, $order, $nb, $first, $filter, $date_min, true, $keepHistoryDefault); -- cgit v1.2.3 From 2da4c13263affdd435ed71edaa111f05a57972a4 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 13 Jul 2014 22:36:34 +0200 Subject: Number of unread articles as prefix in page title https://github.com/marienfressinaud/FreshRSS/issues/536 --- app/Controllers/indexController.php | 6 +++--- p/scripts/main.js | 12 +++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 0469b9611..9a46bde6c 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -70,11 +70,11 @@ class FreshRSS_index_Controller extends Minz_ActionController { // mise à jour des titres $this->view->rss_title = $this->view->currentName . ' | ' . Minz_View::title(); if ($this->view->nb_not_read > 0) { - Minz_View::appendTitle (' (' . formatNumber($this->view->nb_not_read) . ')'); + Minz_View::prependTitle('(' . formatNumber($this->view->nb_not_read) . ') '); } - Minz_View::prependTitle ( + Minz_View::prependTitle( + ($this->nb_not_read_cat > 0 ? '(' . formatNumber($this->nb_not_read_cat) . ') ' : '') . $this->view->currentName . - ($this->nb_not_read_cat > 0 ? ' (' . formatNumber($this->nb_not_read_cat) . ')' : '') . ' · ' ); diff --git a/p/scripts/main.js b/p/scripts/main.js index 243159ca5..e1e6d49ce 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -55,9 +55,11 @@ function numberFormat(nStr) { return x1 + x2; } -function incLabel(p, inc) { +function incLabel(p, inc, spaceAfter = false) { var i = str2int(p) + inc; - return i > 0 ? ' (' + numberFormat(i) + ')' : ''; + return i > 0 + ? ((spaceAfter ? '' : ' ') + '(' + numberFormat(i) + ')' + (spaceAfter ? ' ' : '')) + : ''; } function incUnreadsFeed(article, feed_id, nb) { @@ -96,13 +98,13 @@ function incUnreadsFeed(article, feed_id, nb) { var isCurrentView = false; //Update unread: title - document.title = document.title.replace(/((?: \([ 0-9]+\))?)( · .*?)((?: \([ 0-9]+\))?)$/, function (m, p1, p2, p3) { + document.title = document.title.replace(/^((?:\([ 0-9]+\) )?)(.*? · )((?:\([ 0-9]+\) )?)/, function (m, p1, p2, p3) { var $feed = $('#' + feed_id); if (article || ($feed.closest('.active').length > 0 && $feed.siblings('.active').length === 0)) { isCurrentView = true; - return incLabel(p1, nb) + p2 + incLabel(p3, feed_priority > 0 ? nb : 0); + return incLabel(p1, nb, true) + p2 + incLabel(p3, feed_priority > 0 ? nb : 0, true); } else { - return p1 + p2 + incLabel(p3, feed_priority > 0 ? nb : 0); + return p1 + p2 + incLabel(p3, feed_priority > 0 ? nb : 0, true); } }); return isCurrentView; -- cgit v1.2.3 From b68c5c6630c7b014ee29d0c782054faaa3aeb4fe Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 9 Aug 2014 22:00:20 +0200 Subject: Dynamic favicon: remove the same number in the title Now the number of unread articles in only shown in the favicon https://github.com/marienfressinaud/FreshRSS/issues/539 --- app/Controllers/indexController.php | 3 --- p/scripts/main.js | 8 ++++---- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 9a46bde6c..3119073b8 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -69,9 +69,6 @@ class FreshRSS_index_Controller extends Minz_ActionController { // mise à jour des titres $this->view->rss_title = $this->view->currentName . ' | ' . Minz_View::title(); - if ($this->view->nb_not_read > 0) { - Minz_View::prependTitle('(' . formatNumber($this->view->nb_not_read) . ') '); - } Minz_View::prependTitle( ($this->nb_not_read_cat > 0 ? '(' . formatNumber($this->nb_not_read_cat) . ') ' : '') . $this->view->currentName . diff --git a/p/scripts/main.js b/p/scripts/main.js index a91e23400..ae7b69364 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -106,16 +106,16 @@ function incUnreadsFeed(article, feed_id, nb) { var isCurrentView = false; //Update unread: title - document.title = document.title.replace(/^((?:\([ 0-9]+\) )?)(.*? · )((?:\([ 0-9]+\) )?)/, function (m, p1, p2, p3) { + document.title = document.title.replace(/^((?:\([ 0-9]+\) )?)/, function (m, p1) { var $feed = $('#' + feed_id); if (article || ($feed.closest('.active').length > 0 && $feed.siblings('.active').length === 0)) { isCurrentView = true; - return incLabel(p1, nb, true) + p2 + incLabel(p3, feed_priority > 0 ? nb : 0, true); + return incLabel(p1, nb, true); } else if ($('.all.active').length > 0) { isCurrentView = feed_priority > 0; - return incLabel(p1, feed_priority > 0 ? nb : 0, true) + p2 + incLabel(p3, feed_priority > 0 ? nb : 0, true); + return incLabel(p1, feed_priority > 0 ? nb : 0, true); } else { - return p1 + p2 + incLabel(p3, feed_priority > 0 ? nb : 0, true); + return p1; } }); return isCurrentView; -- cgit v1.2.3 From eceb7756cfcf117c2a18984291181a84697ed3cd Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 10 Aug 2014 20:29:43 +0200 Subject: Add possibility to keep logged in with form Add an option to keep logged in. Change lifetime of session cookie to 1 year. See https://github.com/marienfressinaud/FreshRSS/issues/465 --- app/Controllers/indexController.php | 9 +++++++++ app/i18n/en.php | 1 + app/i18n/fr.php | 1 + app/views/index/formLogin.phtml | 23 +++++++++++++++-------- lib/Minz/Session.php | 35 ++++++++++++++++++++++++++++++----- 5 files changed, 56 insertions(+), 13 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 3119073b8..18b99d0df 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -298,6 +298,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { public function formLoginAction () { if (Minz_Request::isPost()) { $ok = false; + $keep_logged_in = Minz_Request::param('keep_logged_in', false); $nonce = Minz_Session::param('nonce'); $username = Minz_Request::param('username', ''); $c = Minz_Request::param('challenge', ''); @@ -312,6 +313,11 @@ class FreshRSS_index_Controller extends Minz_ActionController { if ($ok) { Minz_Session::_param('currentUser', $username); Minz_Session::_param('passwordHash', $s); + if ($keep_logged_in) { + // New cookie with a lifetime of 1 year! + Minz_Session::keepCookie(31536000); + Minz_Session::regenerateID(); + } } else { Minz_Log::record('Password mismatch for user ' . $username . ', nonce=' . $nonce . ', c=' . $c, Minz_Log::WARNING); } @@ -371,6 +377,9 @@ class FreshRSS_index_Controller extends Minz_ActionController { Minz_Session::_param('currentUser'); Minz_Session::_param('mail'); Minz_Session::_param('passwordHash'); + Minz_Session::keepCookie(0); + Minz_Session::regenerateID(); + Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } } diff --git a/app/i18n/en.php b/app/i18n/en.php index d80299b10..3c55f62a2 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -3,6 +3,7 @@ return array ( // LAYOUT 'login' => 'Login', + 'keep_logged_in' => 'Keep me logged in', 'login_with_persona' => 'Login with Persona', 'logout' => 'Logout', 'search' => 'Search words or #tags', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 4be028ac3..63d779471 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -3,6 +3,7 @@ return array ( // LAYOUT 'login' => 'Connexion', + 'session_active' => 'Rester connecté', 'login_with_persona' => 'Connexion avec Persona', 'logout' => 'Déconnexion', 'search' => 'Rechercher des mots ou des #tags', diff --git a/app/views/index/formLogin.phtml b/app/views/index/formLogin.phtml index cc925ea59..f01a950b6 100644 --- a/app/views/index/formLogin.phtml +++ b/app/views/index/formLogin.phtml @@ -1,32 +1,39 @@
    -

    - +
    - +
    - +
    - + +
    +
    +
    +

    - - + +

    -

    +

    diff --git a/lib/Minz/Session.php b/lib/Minz/Session.php index ddabc4658..c859be2ed 100644 --- a/lib/Minz/Session.php +++ b/lib/Minz/Session.php @@ -15,13 +15,15 @@ class Minz_Session { * Le nom de session est utilisé comme nom pour les cookies et les URLs (i.e. PHPSESSID). * Il ne doit contenir que des caractères alphanumériques ; il doit être court et descriptif */ - public static function init ($name) { + public static function init($name) { + $cookie = session_get_cookie_params(); + self::keepCookie($cookie['lifetime']); + // démarre la session - session_name ($name); - session_set_cookie_params (0, dirname(empty($_SERVER['REQUEST_URI']) ? '/' : dirname($_SERVER['REQUEST_URI'])), null, false, true); - session_start (); + session_name($name); + session_start(); - if (isset ($_SESSION)) { + if (isset($_SESSION)) { self::$session = $_SESSION; } } @@ -68,4 +70,27 @@ class Minz_Session { Minz_Translate::reset (); } } + + + /** + * Spécifie la durée de vie des cookies + * @param $l la durée de vie + */ + public static function keepCookie($l) { + $cookie_dir = dirname( + empty($_SERVER['SCRIPT_NAME']) ? '' : $_SERVER['SCRIPT_NAME'] + ) . '/'; + session_set_cookie_params($l, $cookie_dir, $_SERVER['HTTP_HOST'], + false, true); + } + + + /** + * Régénère un id de session. + * Utile pour appeler session_set_cookie_params après session_start() + */ + public static function regenerateID() { + session_regenerate_id(true); + } + } -- cgit v1.2.3 From 93a77f84d45ee4eccda7c837dc08f17360f3de0f Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 10 Aug 2014 21:08:10 +0200 Subject: Change cookie lifetime to 1 month. See https://github.com/marienfressinaud/FreshRSS/issues/465 --- app/Controllers/indexController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 18b99d0df..b907c8eed 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -314,8 +314,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { Minz_Session::_param('currentUser', $username); Minz_Session::_param('passwordHash', $s); if ($keep_logged_in) { - // New cookie with a lifetime of 1 year! - Minz_Session::keepCookie(31536000); + // New cookie with a lifetime of 1 month. + Minz_Session::keepCookie(2592000); Minz_Session::regenerateID(); } } else { -- cgit v1.2.3 From ee1b8f6f72e8c2cbd3e0ad7b4322a4bb6863c028 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 13 Aug 2014 00:09:48 +0200 Subject: Long term cookie to keep session open Token system https://github.com/marienfressinaud/FreshRSS/issues/465 --- app/Controllers/indexController.php | 41 +++++++++++++++++++++++++++++-------- app/FreshRSS.php | 28 ++++++++++++++++++++++--- data/tokens/.gitignore | 1 + data/tokens/index.html | 13 ++++++++++++ lib/Minz/Session.php | 19 ++++++++++++----- 5 files changed, 86 insertions(+), 16 deletions(-) create mode 100644 data/tokens/.gitignore create mode 100644 data/tokens/index.html (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index b907c8eed..dd5b91e47 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -295,10 +295,39 @@ class FreshRSS_index_Controller extends Minz_ActionController { Minz_Session::_param('passwordHash'); } + private static function makeLongTermCookie($username, $passwordHash) { + do { + $token = sha1(Minz_Configuration::salt() . $username . uniqid(mt_rand(), true)); + $tokenFile = DATA_PATH . '/tokens/' . $token . '.txt'; + } while (file_exists($tokenFile)); + if (@file_put_contents($tokenFile, $username . "\t" . $passwordHash) === false) { + return false; + } + $expire = time() + 2629744; //1 month //TODO: Use a configuration instead + Minz_Session::setLongTermCookie('FreshRSS_login', $token, $expire); + Minz_Session::_param('token', $token); + return $token; + } + + private static function deleteLongTermCookie() { + Minz_Session::deleteLongTermCookie('FreshRSS_login'); + $token = Minz_Session::param('token', null); + if (ctype_alnum($token)) { + @unlink(DATA_PATH . '/tokens/' . $token . '.txt'); + } + Minz_Session::_param('token'); + if (rand(0, 10) === 1) { + self::purgeTokens(); + } + } + + private static function purgeTokens() { + //TODO: Delete old token files + } + public function formLoginAction () { if (Minz_Request::isPost()) { $ok = false; - $keep_logged_in = Minz_Request::param('keep_logged_in', false); $nonce = Minz_Session::param('nonce'); $username = Minz_Request::param('username', ''); $c = Minz_Request::param('challenge', ''); @@ -313,10 +342,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { if ($ok) { Minz_Session::_param('currentUser', $username); Minz_Session::_param('passwordHash', $s); - if ($keep_logged_in) { - // New cookie with a lifetime of 1 month. - Minz_Session::keepCookie(2592000); - Minz_Session::regenerateID(); + if (Minz_Request::param('keep_logged_in', false)) { + self::makeLongTermCookie($username, $s); } } else { Minz_Log::record('Password mismatch for user ' . $username . ', nonce=' . $nonce . ', c=' . $c, Minz_Log::WARNING); @@ -377,9 +404,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { Minz_Session::_param('currentUser'); Minz_Session::_param('mail'); Minz_Session::_param('passwordHash'); - Minz_Session::keepCookie(0); - Minz_Session::regenerateID(); - + self::deleteLongTermCookie(); Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } } diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 7c333b090..30f711e20 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -20,13 +20,35 @@ class FreshRSS extends Minz_FrontController { $this->loadNotifications(); } + private static function getCredentialsFromLongTermCookie() { + $token = Minz_Session::getLongTermCookie('FreshRSS_login'); + if (!ctype_alnum($token)) { + return array(); + } + $tokenFile = DATA_PATH . '/tokens/' . $token . '.txt'; + $mtime = @filemtime($tokenFile); + if ($mtime + 2629744 < time()) { //1 month //TODO: Use a configuration instead + @unlink($tokenFile); + return array(); //Expired or token does not exist + } + $credentials = @file_get_contents($tokenFile); + return $credentials === false ? array() : explode("\t", $credentials, 2); + } + private function accessControl($currentUser) { if ($currentUser == '') { switch (Minz_Configuration::authType()) { case 'form': - $currentUser = Minz_Configuration::defaultUser(); - Minz_Session::_param('passwordHash'); - $loginOk = false; + $credentials = self::getCredentialsFromLongTermCookie(); + if (isset($credentials[1])) { + $currentUser = trim($credentials[0]); + Minz_Session::_param('passwordHash', trim($credentials[1])); + } + $loginOk = $currentUser != ''; + if (!$loginOk) { + $currentUser = Minz_Configuration::defaultUser(); + Minz_Session::_param('passwordHash'); + } break; case 'http_auth': $currentUser = httpAuthUser(); diff --git a/data/tokens/.gitignore b/data/tokens/.gitignore new file mode 100644 index 000000000..2211df63d --- /dev/null +++ b/data/tokens/.gitignore @@ -0,0 +1 @@ +*.txt diff --git a/data/tokens/index.html b/data/tokens/index.html new file mode 100644 index 000000000..85faaa37e --- /dev/null +++ b/data/tokens/index.html @@ -0,0 +1,13 @@ + + + + + +Redirection + + + + +

    Redirection

    + + diff --git a/lib/Minz/Session.php b/lib/Minz/Session.php index 906acc03c..af4de75bb 100644 --- a/lib/Minz/Session.php +++ b/lib/Minz/Session.php @@ -65,11 +65,8 @@ class Minz_Session { * @param $l la durée de vie */ public static function keepCookie($l) { - $cookie_dir = dirname( - empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI'] - ) . '/'; - session_set_cookie_params($l, $cookie_dir, $_SERVER['HTTP_HOST'], - false, true); + $cookie_dir = empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI']; + session_set_cookie_params($l, $cookie_dir, '', false, true); } @@ -81,4 +78,16 @@ class Minz_Session { session_regenerate_id(true); } + public static function deleteLongTermCookie($name) { + setcookie($name, '', 1, '', '', false, true); + } + + public static function setLongTermCookie($name, $value, $expire) { + setcookie($name, $value, $expire, '', '', false, true); + } + + public static function getLongTermCookie($name) { + return isset($_COOKIE[$name]) ? $_COOKIE[$name] : null; + } + } -- cgit v1.2.3 From 359ccc8e4c20a60506c680e5054dbe8416fef4a9 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 13 Aug 2014 00:25:52 +0200 Subject: Long term cookie minor change https://github.com/marienfressinaud/FreshRSS/issues/465 --- app/Controllers/indexController.php | 2 ++ 1 file changed, 2 insertions(+) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index dd5b91e47..834db496c 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -344,6 +344,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { Minz_Session::_param('passwordHash', $s); if (Minz_Request::param('keep_logged_in', false)) { self::makeLongTermCookie($username, $s); + } else { + self::deleteLongTermCookie(); } } else { Minz_Log::record('Password mismatch for user ' . $username . ', nonce=' . $nonce . ', c=' . $c, Minz_Log::WARNING); -- cgit v1.2.3 From 5c2f3349fa4341533aad6294c896b32c4befd58f Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 13 Aug 2014 20:09:23 +0200 Subject: Long term cookie: purge old tokens https://github.com/marienfressinaud/FreshRSS/issues/465 --- app/Controllers/indexController.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 834db496c..9202f2b85 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -322,7 +322,12 @@ class FreshRSS_index_Controller extends Minz_ActionController { } private static function purgeTokens() { - //TODO: Delete old token files + $oldest = time() - 2629744; //1 month //TODO: Use a configuration instead + foreach (new DirectoryIterator(DATA_PATH . '/tokens/') as $fileInfo) { + if ($fileInfo->getExtension() === 'txt' && $fileInfo->getMTime() < $oldest) { + @unlink($fileInfo->getPathname()); + } + } } public function formLoginAction () { -- cgit v1.2.3 From fb664c2405d6efd3d2581c9613fef0e7063ef21b Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 23 Aug 2014 11:04:17 +0200 Subject: Correction pagination bug for read articles https://github.com/marienfressinaud/FreshRSS/issues/587 --- app/Controllers/indexController.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 9202f2b85..b0b051119 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -76,14 +76,14 @@ class FreshRSS_index_Controller extends Minz_ActionController { ); // On récupère les différents éléments de filtrage - $this->view->state = $state = Minz_Request::param ('state', $this->view->conf->default_view); + $this->view->state = Minz_Request::param('state', $this->view->conf->default_view); $state_param = Minz_Request::param ('state', null); $filter = Minz_Request::param ('search', ''); $this->view->order = $order = Minz_Request::param ('order', $this->view->conf->sort_order); $nb = Minz_Request::param ('nb', $this->view->conf->posts_per_page); $first = Minz_Request::param ('next', ''); - if ($state === FreshRSS_Entry::STATE_NOT_READ) { //Any unread article in this category at all? + if ($this->view->state === FreshRSS_Entry::STATE_NOT_READ) { //Any unread article in this category at all? switch ($getType) { case 'a': $hasUnread = $this->view->nb_not_read > 0; @@ -104,7 +104,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { break; } if (!$hasUnread && ($state_param === null)) { - $this->view->state = $state = FreshRSS_Entry::STATE_ALL; + $this->view->state = FreshRSS_Entry::STATE_ALL; } } @@ -117,11 +117,11 @@ class FreshRSS_index_Controller extends Minz_ActionController { $keepHistoryDefault = $this->view->conf->keep_history_default; try { - $entries = $entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min, true, $keepHistoryDefault); + $entries = $entryDAO->listWhere($getType, $getId, $this->view->state, $order, $nb + 1, $first, $filter, $date_min, true, $keepHistoryDefault); // Si on a récupéré aucun article "non lus" // on essaye de récupérer tous les articles - if ($state === FreshRSS_Entry::STATE_NOT_READ && empty($entries) && ($state_param === null) && ($filter == '')) { + if ($this->view->state === FreshRSS_Entry::STATE_NOT_READ && empty($entries) && ($state_param === null) && ($filter == '')) { Minz_Log::record('Conflicting information about nbNotRead!', Minz_Log::DEBUG); $feedDAO = FreshRSS_Factory::createFeedDao(); try { @@ -132,6 +132,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { $this->view->state = FreshRSS_Entry::STATE_ALL; $entries = $entryDAO->listWhere($getType, $getId, $this->view->state, $order, $nb, $first, $filter, $date_min, true, $keepHistoryDefault); } + Minz_Request::_param('state', $this->view->state); if (count($entries) <= $nb) { $this->view->nextId = ''; -- cgit v1.2.3 From 7d5e57b35b4eca07417c296c156edb3b88eef90b Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Wed, 17 Sep 2014 09:15:19 +0200 Subject: Load $nb/2 articles when articles are unfolded See https://github.com/marienfressinaud/FreshRSS/issues/559 --- app/Controllers/indexController.php | 5 +++++ app/layout/layout.phtml | 1 + app/views/helpers/pagination.phtml | 5 ++++- 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index b0b051119..1d74a570b 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -83,6 +83,11 @@ class FreshRSS_index_Controller extends Minz_ActionController { $nb = Minz_Request::param ('nb', $this->view->conf->posts_per_page); $first = Minz_Request::param ('next', ''); + $ajax_request = Minz_Request::param('ajax', false); + if ($ajax_request == 1 && $this->view->conf->display_posts) { + $nb = max(1, round($nb / 2)); + } + if ($this->view->state === FreshRSS_Entry::STATE_NOT_READ) { //Any unread article in this category at all? switch ($getType) { case 'a': diff --git a/app/layout/layout.phtml b/app/layout/layout.phtml index 96a88d245..f95f45b5e 100644 --- a/app/layout/layout.phtml +++ b/app/layout/layout.phtml @@ -13,6 +13,7 @@ if (!empty($this->nextId)) { $params = Minz_Request::params(); $params['next'] = $this->nextId; + $params['ajax'] = 1; ?> diff --git a/app/views/helpers/pagination.phtml b/app/views/helpers/pagination.phtml index f6fcbc701..1b15cc632 100755 --- a/app/views/helpers/pagination.phtml +++ b/app/views/helpers/pagination.phtml @@ -9,7 +9,10 @@
    • nextId)) { ?> - nextId; ?> + nextId; + $params['ajax'] = 1; + ?> -- cgit v1.2.3 From 23609ad858552c54fc3fe8d9f8e2f7d966fd28a1 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 18 Sep 2014 14:18:00 +0200 Subject: Add page for reset auth type [NOT WORKING] See https://github.com/marienfressinaud/FreshRSS/issues/521 --- app/Controllers/indexController.php | 77 +++++++++++++++++++++++++++++++++++++ app/views/index/resetAuth.phtml | 33 ++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 app/views/index/resetAuth.phtml (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 1d74a570b..fccf16ecf 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -420,4 +420,81 @@ class FreshRSS_index_Controller extends Minz_ActionController { self::deleteLongTermCookie(); Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } + + public function resetAuthAction() { + Minz_View::prependTitle(_t('reset_auth') . ' · '); + + $this->view->no_form = false; + // Enable changement of auth only if Persona! + if (Minz_Configuration::authType() != 'persona') { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('auth_not_persona') + ); + $this->view->no_form = true; + return; + } + + $conf = new FreshRSS_Configuration(Minz_Configuration::defaultUser()); + // Admin user must have set its master password. + if (!$conf->passwordHash) { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('auth_no_password_set') + ); + $this->view->no_form = true; + return; + } + + if (Minz_Request::isPost()) { + $nonce = Minz_Session::param('nonce'); + $username = Minz_Request::param('username', ''); + $c = Minz_Request::param('challenge', ''); + if (!(ctype_alnum($username) && ctype_graph($c) && ctype_alnum($nonce))) { + Minz_Log::debug('Invalid credential parameters:' . + ' user=' . $username . + ' challenge=' . $c . + ' nonce=' . $nonce); + Minz_Session::_param('notification', array( + 'type' => 'bad', + 'content' => Minz_Translate::t('invalid_login') + )); + return; + } + + if (!function_exists('password_verify')) { + include_once(LIB_PATH . '/password_compat.php'); + } + + try { + $s = $conf->passwordHash; + $ok = password_verify($nonce . $s, $c); + if (!$ok) { + Minz_Log::debug('Password mismatch for user ' . $username . + ', nonce=' . $nonce . ', c=' . $c); + Minz_Session::_param('notification', array( + 'type' => 'bad', + 'content' => Minz_Translate::t('invalid_login') + )); + return; + } + + Minz_Configuration::_authType('form'); + $ok = Minz_Configuration::writeFile(); + + if ($ok) { + Minz_Request::good(_t('auth_form_set')); + } else { + Minz_Session::_param('notification', array( + 'type' => 'bad', + 'content' => _t('auth_form_not_set') + )); + } + } catch (Minz_Exception $e) { + Minz_Log::warning('Login failure: ' . $e->getMessage()); + } + } + } } diff --git a/app/views/index/resetAuth.phtml b/app/views/index/resetAuth.phtml new file mode 100644 index 000000000..7f3b54bdb --- /dev/null +++ b/app/views/index/resetAuth.phtml @@ -0,0 +1,33 @@ +
      +

      + + message)) { ?> +

      + message['title']; ?> + message['body']; ?> +

      + + + no_form) { ?> +
      +

      + + +

      + +
      + + +
      +
      + + +
      + +
      +
      + +
      +
      + +
      -- cgit v1.2.3 From f727a1383639d5bdc762f73dfe93b9a5d577cb41 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 18 Sep 2014 14:50:54 +0200 Subject: Improve reset auth system - Add bcrypt.js in resetAuthAction() - Rename init_loginForm() in init_crypto_form() - Load init_crypto_form() everytime (if no #crypto-form, do nothing) --- app/Controllers/indexController.php | 3 +++ app/views/index/formLogin.phtml | 2 +- app/views/index/resetAuth.phtml | 2 +- p/scripts/main.js | 27 +++++++++++++++------------ 4 files changed, 20 insertions(+), 14 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index fccf16ecf..86863cc84 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -423,6 +423,9 @@ class FreshRSS_index_Controller extends Minz_ActionController { public function resetAuthAction() { Minz_View::prependTitle(_t('reset_auth') . ' · '); + Minz_View::appendScript(Minz_Url::display( + '/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js') + )); $this->view->no_form = false; // Enable changement of auth only if Persona! diff --git a/app/views/index/formLogin.phtml b/app/views/index/formLogin.phtml index b79c1b614..34f10de4a 100644 --- a/app/views/index/formLogin.phtml +++ b/app/views/index/formLogin.phtml @@ -3,7 +3,7 @@ switch (Minz_Configuration::authType()) { case 'form': - ?>
      + ?>
      diff --git a/app/views/index/resetAuth.phtml b/app/views/index/resetAuth.phtml index 7f3b54bdb..78cc527b3 100644 --- a/app/views/index/resetAuth.phtml +++ b/app/views/index/resetAuth.phtml @@ -9,7 +9,7 @@ no_form) { ?> - +

      diff --git a/p/scripts/main.js b/p/scripts/main.js index fd49d62ba..04151c30d 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -977,7 +977,7 @@ function init_load_more(box) { } // -// +// function poormanSalt() { //If crypto.getRandomValues is not available var text = '$2a$04$', base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789/abcdefghijklmnopqrstuvwxyz'; @@ -987,20 +987,24 @@ function poormanSalt() { //If crypto.getRandomValues is not available return text; } -function init_loginForm() { - var $loginForm = $('#loginForm'); - if ($loginForm.length === 0) { +function init_crypto_form() { + var $crypto_form = $('#crypto-form'); + if ($crypto_form.length === 0) { return; } + if (!(window.dcodeIO)) { if (window.console) { console.log('FreshRSS waiting for bcrypt.js…'); } - window.setTimeout(init_loginForm, 100); + window.setTimeout(init_crypto_form, 100); return; } - $loginForm.on('submit', function() { - $('#loginButton').attr('disabled', ''); + + $crypto_form.on('submit', function() { + var $submit_button = $(this).find('button[type="submit"]'); + $submit_button.attr('disabled', ''); + var success = false; $.ajax({ url: './?c=javascript&a=nonce&user=' + $('#username').val(), @@ -1027,11 +1031,12 @@ function init_loginForm() { }).fail(function() { alert('Communication error!'); }); - $('#loginButton').removeAttr('disabled'); + + $submit_button.removeAttr('disabled'); return success; }); } -// +// // function init_persona() { @@ -1233,14 +1238,12 @@ function init_all() { } init_notifications(); switch (authType) { - case 'form': - init_loginForm(); - break; case 'persona': init_persona(); break; } init_confirm_action(); + init_crypto_form(); $stream = $('#stream'); if ($stream.length > 0) { init_actualize(); -- cgit v1.2.3 From d2799d168e0d885cb6de24cf012e2a909215fcd8 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 18 Sep 2014 15:09:47 +0200 Subject: Improve resetAuth redirections See https://github.com/marienfressinaud/FreshRSS/issues/521 --- app/Controllers/indexController.php | 39 ++++++++++++++----------------------- 1 file changed, 15 insertions(+), 24 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 86863cc84..26e2618f0 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -451,6 +451,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { return; } + invalidateHttpCache(); + if (Minz_Request::isPost()) { $nonce = Minz_Session::param('nonce'); $username = Minz_Request::param('username', ''); @@ -460,43 +462,32 @@ class FreshRSS_index_Controller extends Minz_ActionController { ' user=' . $username . ' challenge=' . $c . ' nonce=' . $nonce); - Minz_Session::_param('notification', array( - 'type' => 'bad', - 'content' => Minz_Translate::t('invalid_login') - )); - return; + Minz_Request::bad(_t('invalid_login'), + array('c' => 'index', 'a' => 'resetAuth')); } if (!function_exists('password_verify')) { include_once(LIB_PATH . '/password_compat.php'); } - try { - $s = $conf->passwordHash; - $ok = password_verify($nonce . $s, $c); - if (!$ok) { - Minz_Log::debug('Password mismatch for user ' . $username . - ', nonce=' . $nonce . ', c=' . $c); - Minz_Session::_param('notification', array( - 'type' => 'bad', - 'content' => Minz_Translate::t('invalid_login') - )); - return; - } - + $s = $conf->passwordHash; + $ok = password_verify($nonce . $s, $c); + if ($ok) { Minz_Configuration::_authType('form'); $ok = Minz_Configuration::writeFile(); if ($ok) { Minz_Request::good(_t('auth_form_set')); } else { - Minz_Session::_param('notification', array( - 'type' => 'bad', - 'content' => _t('auth_form_not_set') - )); + Minz_Request::bad(_t('auth_form_not_set'), + array('c' => 'index', 'a' => 'resetAuth')); } - } catch (Minz_Exception $e) { - Minz_Log::warning('Login failure: ' . $e->getMessage()); + } else { + Minz_Log::debug('Password mismatch for user ' . $username . + ', nonce=' . $nonce . ', c=' . $c); + + Minz_Request::bad(_t('invalid_login'), + array('c' => 'index', 'a' => 'resetAuth')); } } } -- cgit v1.2.3 From 94915f1c0a477c240e6072b6ccc2f71152181510 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 18 Sep 2014 15:32:59 +0200 Subject: Reset auth system: i18n and style See https://github.com/marienfressinaud/FreshRSS/issues/521 --- app/Controllers/indexController.php | 2 +- app/i18n/en.php | 10 ++++++++++ app/i18n/fr.php | 10 ++++++++++ app/views/index/resetAuth.phtml | 12 ++++++------ 4 files changed, 27 insertions(+), 7 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 26e2618f0..b69c09127 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -422,7 +422,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { } public function resetAuthAction() { - Minz_View::prependTitle(_t('reset_auth') . ' · '); + Minz_View::prependTitle(_t('auth_reset') . ' · '); Minz_View::appendScript(Minz_Url::display( '/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js') )); diff --git a/app/i18n/en.php b/app/i18n/en.php index b14b36b32..11edc603f 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -159,6 +159,7 @@ return array ( 'save' => 'Save', 'delete' => 'Delete', 'cancel' => 'Cancel', + 'submit' => 'Submit', 'back_to_rss_feeds' => '← Go back to your RSS feeds', 'feeds_moved_category_deleted' => 'When you delete a category, their feeds are automatically classified under %s.', @@ -204,6 +205,7 @@ return array ( 'informations' => 'Information', 'damn' => 'Damn!', 'ok' => 'Ok!', + 'attention' => 'Be careful!', 'feed_in_error' => 'This feed has encountered a problem. Please verify that it is always reachable then actualize it.', 'feed_empty' => 'This feed is empty. Please verify that it is still maintained.', 'feed_description' => 'Description', @@ -255,6 +257,7 @@ return array ( 'users_list' => 'List of users', 'create_user' => 'Create new user', 'username' => 'Username', + 'username_admin' => 'Administrator username', 'password' => 'Password', 'create' => 'Create', 'user_created' => 'User %s has been created', @@ -434,4 +437,11 @@ return array ( 'no_update' => 'No update to apply', 'update_problem' => 'The update process has encountered an error: %s', 'update_finished' => 'Update completed!', + + 'auth_reset' => 'Authentication reset', + 'auth_will_reset' => 'Authentication system will be reseted: form will be used instead of Persona.', + 'auth_not_persona' => 'Only Persona system can be reseted.', + 'auth_no_password_set' => 'Administrator password hasn’t been set. This feature isn’t available.', + 'auth_form_set' => 'Form is now your default authentication system.', + 'auth_form_not_set' => 'A problem occured during authentication system configuration. Please retry later.', ); diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 3dc429bbb..279cb89fa 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -159,6 +159,7 @@ return array ( 'save' => 'Enregistrer', 'delete' => 'Supprimer', 'cancel' => 'Annuler', + 'submit' => 'Valider', 'back_to_rss_feeds' => '← Retour à vos flux RSS', 'feeds_moved_category_deleted' => 'Lors de la suppression d’une catégorie, ses flux seront automatiquement classés dans %s.', @@ -204,6 +205,7 @@ return array ( 'informations' => 'Informations', 'damn' => 'Arf !', 'ok' => 'Ok !', + 'attention' => 'Attention !', 'feed_in_error' => 'Ce flux a rencontré un problème. Veuillez vérifier qu’il est toujours accessible puis actualisez-le.', 'feed_empty' => 'Ce flux est vide. Veuillez vérifier qu’il est toujours maintenu.', 'feed_description' => 'Description', @@ -255,6 +257,7 @@ return array ( 'users_list' => 'Liste des utilisateurs', 'create_user' => 'Créer un nouvel utilisateur', 'username' => 'Nom d’utilisateur', + 'username_admin' => 'Nom d’utilisateur administrateur', 'password' => 'Mot de passe', 'create' => 'Créer', 'user_created' => 'L’utilisateur %s a été créé.', @@ -434,4 +437,11 @@ return array ( 'no_update' => 'Aucune mise à jour à appliquer', 'update_problem' => 'La mise à jour a rencontré un problème : %s', 'update_finished' => 'La mise à jour est terminée !', + + 'auth_reset' => 'Reset de l’authentification', + 'auth_will_reset' => 'Le système d’authentification va être remis à zéro : le formulaire sera utilisé à la place de Persona.', + 'auth_not_persona' => 'Seul le système d’authentification Persona peut être remis à zéro.', + 'auth_no_password_set' => 'Aucun mot de passe administrateur n’a été précisé. Cette fonctionnalité n’est pas disponible.', + 'auth_form_set' => 'Le formulaire est désormais votre système d’authentification.', + 'auth_form_not_set' => 'Un problème est survenu lors de la configuration de votre système d’authentification. Veuillez réessayer plus tard.', ); diff --git a/app/views/index/resetAuth.phtml b/app/views/index/resetAuth.phtml index 78cc527b3..6d4282c14 100644 --- a/app/views/index/resetAuth.phtml +++ b/app/views/index/resetAuth.phtml @@ -1,9 +1,9 @@ -

      -

      +
      +

      message)) { ?>

      - message['title']; ?> + message['title']; ?>
      message['body']; ?>

      @@ -11,12 +11,12 @@ no_form) { ?>

      - +

      - +
      @@ -26,7 +26,7 @@
      - +
      -- cgit v1.2.3 From 07d123f1aa09afc79c322b2364132981f5285ac1 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Fri, 19 Sep 2014 18:47:49 +0200 Subject: Load $nb/2 articles when reading view Fix https://github.com/marienfressinaud/FreshRSS/commit/7d5e57b35b4eca07417c296c156edb3b88eef90b See https://github.com/marienfressinaud/FreshRSS/issues/559 --- app/Controllers/indexController.php | 2 +- app/i18n/en.php | 2 +- app/i18n/fr.php | 2 +- app/views/configure/reading.phtml | 4 +--- 4 files changed, 4 insertions(+), 6 deletions(-) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index b69c09127..bc03f1916 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -84,7 +84,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { $first = Minz_Request::param ('next', ''); $ajax_request = Minz_Request::param('ajax', false); - if ($ajax_request == 1 && $this->view->conf->display_posts) { + if ($output === 'reader') { $nb = max(1, round($nb / 2)); } diff --git a/app/i18n/en.php b/app/i18n/en.php index 82c853255..4c6ce6eab 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -276,7 +276,7 @@ return array ( 'reading_configuration' => 'Reading', 'display_configuration' => 'Display', 'articles_per_page' => 'Number of articles per page', - 'number_divided_when_unfolded' => 'Divided by 2 during loading of unfolded articles.', + 'number_divided_when_reader' => 'Divided by 2 in the reading view.', 'default_view' => 'Default view', 'articles_to_display' => 'Articles to display', 'sort_order' => 'Sort order', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index b5ab32dae..889bdd173 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -276,7 +276,7 @@ return array ( 'reading_configuration' => 'Lecture', 'display_configuration' => 'Affichage', 'articles_per_page' => 'Nombre d’articles par page', - 'number_divided_when_unfolded' => 'Divisé par 2 lors du chargement d’articles dépliés.', + 'number_divided_when_reader' => 'Divisé par 2 dans la vue de lecture.', 'default_view' => 'Vue par défaut', 'articles_to_display' => 'Articles à afficher', 'sort_order' => 'Ordre de tri', diff --git a/app/views/configure/reading.phtml b/app/views/configure/reading.phtml index 86d816eb3..8b2da2a28 100644 --- a/app/views/configure/reading.phtml +++ b/app/views/configure/reading.phtml @@ -10,9 +10,7 @@
      - conf->display_posts) { ?> - - +
      -- cgit v1.2.3 From a14d325432b1d6515beaaccef73fa01f3ab3a85c Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Fri, 19 Sep 2014 20:30:54 +0200 Subject: Auto-redirect from formLogin if already logged in --- app/Controllers/indexController.php | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'app/Controllers/indexController.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index bc03f1916..e8e26b142 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -337,6 +337,10 @@ class FreshRSS_index_Controller extends Minz_ActionController { } public function formLoginAction () { + if ($this->view->loginOk) { + Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); + } + if (Minz_Request::isPost()) { $ok = false; $nonce = Minz_Session::param('nonce'); -- cgit v1.2.3