diff options
| author | 2014-09-08 19:26:35 +0200 | |
|---|---|---|
| committer | 2014-09-08 19:26:35 +0200 | |
| commit | ef1b35fc4385c99c4d38e3f87e8126d0dbe21519 (patch) | |
| tree | c2127f92281084c3cb28f635dea63a9a179eabbb /app/Controllers | |
| parent | 909d8747ba09f9c9a6ac895f1f4f0763bdb27a55 (diff) | |
| parent | c3fd8877c021b86180b3bea4d4260e6478f0558e (diff) | |
Merge branch 'dev' into 411-update-system
Conflicts:
constants.php
Diffstat (limited to 'app/Controllers')
| -rwxr-xr-x | app/Controllers/configureController.php | 1 | ||||
| -rw-r--r-- | app/Controllers/importExportController.php | 252 | ||||
| -rwxr-xr-x | app/Controllers/indexController.php | 52 | ||||
| -rw-r--r-- | app/Controllers/statsController.php | 5 |
4 files changed, 176 insertions, 134 deletions
diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 0bf58880f..bb96bfae3 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -184,6 +184,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->conf->_default_view((int)Minz_Request::param('default_view', FreshRSS_Entry::STATE_ALL)); $this->view->conf->_auto_load_more(Minz_Request::param('auto_load_more', false)); $this->view->conf->_display_posts(Minz_Request::param('display_posts', false)); + $this->view->conf->_display_categories(Minz_Request::param('display_categories', false)); $this->view->conf->_hide_read_feeds(Minz_Request::param('hide_read_feeds', false)); $this->view->conf->_onread_jump_next(Minz_Request::param('onread_jump_next', false)); $this->view->conf->_lazyload(Minz_Request::param('lazyload', false)); diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 2b3353d93..5adf3878a 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -5,7 +5,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { if (!$this->view->loginOk) { Minz_Error::error( 403, - array('error' => array(Minz_Translate::t('access_denied'))) + array('error' => array(_t('access_denied'))) ); } @@ -20,33 +20,51 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $this->view->categories = $this->catDAO->listCategories(); $this->view->feeds = $this->feedDAO->listFeeds(); - Minz_View::prependTitle(Minz_Translate::t('import_export') . ' · '); + Minz_View::prependTitle(_t('import_export') . ' · '); } public function importAction() { - if (Minz_Request::isPost() && $_FILES['file']['error'] == 0) { - @set_time_limit(300); + if (!Minz_Request::isPost()) { + Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true); + } - $file = $_FILES['file']; - $type_file = $this->guessFileType($file['name']); + $file = $_FILES['file']; + $status_file = $file['error']; - $list_files = array( - 'opml' => array(), - 'json_starred' => array(), - 'json_feed' => array() - ); + if ($status_file !== 0) { + Minz_Log::error('File cannot be uploaded. Error code: ' . $status_file); + Minz_Request::bad(_t('file_cannot_be_uploaded'), + array('c' => 'importExport', 'a' => 'index')); + } - // We try to list all files according to their type - // A zip file is first opened and then its files are listed - $list = array(); - if ($type_file === 'zip') { - $zip = zip_open($file['tmp_name']); + @set_time_limit(300); - while (($zipfile = zip_read($zip)) !== false) { - $type_zipfile = $this->guessFileType( - zip_entry_name($zipfile) - ); + $type_file = $this->guessFileType($file['name']); + $list_files = array( + 'opml' => array(), + 'json_starred' => array(), + 'json_feed' => array() + ); + + // We try to list all files according to their type + $list = array(); + if ($type_file === 'zip' && extension_loaded('zip')) { + $zip = zip_open($file['tmp_name']); + + if (!is_resource($zip)) { + // zip_open cannot open file: something is wrong + Minz_Log::error('Zip archive cannot be imported. Error code: ' . $zip); + Minz_Request::bad(_t('zip_error'), + array('c' => 'importExport', 'a' => 'index')); + } + + while (($zipfile = zip_read($zip)) !== false) { + if (!is_resource($zipfile)) { + // zip_entry() can also return an error code! + Minz_Log::error('Zip file cannot be imported. Error code: ' . $zipfile); + } else { + $type_zipfile = $this->guessFileType(zip_entry_name($zipfile)); if ($type_file !== 'unknown') { $list_files[$type_zipfile][] = zip_entry_read( $zipfile, @@ -54,59 +72,37 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { ); } } - - zip_close($zip); - } elseif ($type_file !== 'unknown') { - $list_files[$type_file][] = file_get_contents( - $file['tmp_name'] - ); - } - - // Import different files. - // OPML first(so categories and feeds are imported) - // Starred articles then so the "favourite" status is already set - // And finally all other files. - $error = false; - foreach ($list_files['opml'] as $opml_file) { - $error = $this->importOpml($opml_file); - } - foreach ($list_files['json_starred'] as $article_file) { - $error = $this->importArticles($article_file, true); - } - foreach ($list_files['json_feed'] as $article_file) { - $error = $this->importArticles($article_file); } - // And finally, we get import status and redirect to the home page - $notif = null; - if ($error === true) { - $content_notif = Minz_Translate::t( - 'feeds_imported_with_errors' - ); - } else { - $content_notif = Minz_Translate::t( - 'feeds_imported' - ); - } - - Minz_Session::_param('notification', array( - 'type' => 'good', - 'content' => $content_notif - )); - Minz_Session::_param('actualize_feeds', true); + zip_close($zip); + } elseif ($type_file === 'zip') { + // Zip extension is not loaded + Minz_Request::bad(_t('no_zip_extension'), + array('c' => 'importExport', 'a' => 'index')); + } elseif ($type_file !== 'unknown') { + $list_files[$type_file][] = file_get_contents($file['tmp_name']); + } - Minz_Request::forward(array( - 'c' => 'index', - 'a' => 'index' - ), true); + // Import file contents. + // OPML first(so categories and feeds are imported) + // Starred articles then so the "favourite" status is already set + // And finally all other files. + $error = false; + foreach ($list_files['opml'] as $opml_file) { + $error = $this->importOpml($opml_file); + } + foreach ($list_files['json_starred'] as $article_file) { + $error = $this->importArticles($article_file, true); + } + foreach ($list_files['json_feed'] as $article_file) { + $error = $this->importArticles($article_file); } - // What are you doing? you have to call this controller - // with a POST request! - Minz_Request::forward(array( - 'c' => 'importExport', - 'a' => 'index' - )); + // And finally, we get import status and redirect to the home page + Minz_Session::_param('actualize_feeds', true); + $content_notif = $error === true ? _t('feeds_imported_with_errors') : + _t('feeds_imported'); + Minz_Request::good($content_notif); } private function guessFileType($filename) { @@ -120,7 +116,8 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } elseif (substr_compare($filename, '.opml', -5) === 0 || substr_compare($filename, '.xml', -4) === 0) { return 'opml'; - } elseif (strcmp($filename, 'starred.json') === 0) { + } elseif (substr_compare($filename, '.json', -5) === 0 && + strpos($filename, 'starred') !== false) { return 'json_starred'; } elseif (substr_compare($filename, '.json', -5) === 0 && strpos($filename, 'feed_') === 0) { @@ -176,15 +173,15 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } // We get different useful information - $url = html_chars_utf8($feed_elt['xmlUrl']); - $name = html_chars_utf8($feed_elt['text']); + $url = Minz_Helper::htmlspecialchars_utf8($feed_elt['xmlUrl']); + $name = Minz_Helper::htmlspecialchars_utf8($feed_elt['text']); $website = ''; if (isset($feed_elt['htmlUrl'])) { - $website = html_chars_utf8($feed_elt['htmlUrl']); + $website = Minz_Helper::htmlspecialchars_utf8($feed_elt['htmlUrl']); } $description = ''; if (isset($feed_elt['description'])) { - $description = html_chars_utf8($feed_elt['description']); + $description = Minz_Helper::htmlspecialchars_utf8($feed_elt['description']); } $error = false; @@ -210,7 +207,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { private function addCategoryOpml($cat_elt, $parent_cat) { // Create a new Category object - $cat = new FreshRSS_Category(html_chars_utf8($cat_elt['text'])); + $cat = new FreshRSS_Category(Minz_Helper::htmlspecialchars_utf8($cat_elt['text'])); $id = $this->catDAO->addCategoryObject($cat); $error = ($id === false); @@ -287,7 +284,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $url = $origin[$key]; $name = $origin['title']; $website = $origin['htmlUrl']; - $error = false; + try { // Create a Feed object and add it in DB $feed = new FreshRSS_Feed($url); @@ -311,61 +308,53 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } public function exportAction() { - if (Minz_Request::isPost()) { - $this->view->_useLayout(false); + if (!Minz_Request::isPost()) { + Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true); + } - $export_opml = Minz_Request::param('export_opml', false); - $export_starred = Minz_Request::param('export_starred', false); - $export_feeds = Minz_Request::param('export_feeds', array ()); + $this->view->_useLayout(false); - $export_files = array (); - if ($export_opml) { - $export_files['feeds.opml'] = $this->generateOpml(); - } + $export_opml = Minz_Request::param('export_opml', false); + $export_starred = Minz_Request::param('export_starred', false); + $export_feeds = Minz_Request::param('export_feeds', array()); - if ($export_starred) { - $export_files['starred.json'] = $this->generateArticles('starred'); - } + $export_files = array(); + if ($export_opml) { + $export_files['feeds.opml'] = $this->generateOpml(); + } - foreach ($export_feeds as $feed_id) { - $feed = $this->feedDAO->searchById($feed_id); - if ($feed) { - $filename = 'feed_' . $feed->category() . '_' - . $feed->id() . '.json'; - $export_files[$filename] = $this->generateArticles( - 'feed', $feed - ); - } - } + if ($export_starred) { + $export_files['starred.json'] = $this->generateArticles('starred'); + } - $nb_files = count($export_files); - if ($nb_files > 1) { - // If there are more than 1 file to export, we need an .zip - try { - $this->exportZip($export_files); - } catch (Exception $e) { - # Oops, there is no Zip extension! - $notif = array( - 'type' => 'bad', - 'content' => _t('export_no_zip_extension') - ); - Minz_Session::_param('notification', $notif); - Minz_Request::forward(array('c' => 'importExport'), true); - } - } elseif ($nb_files === 1) { - // Only one file? Guess its type and export it. - $filename = key($export_files); - $type = null; - if (substr_compare($filename, '.opml', -5) === 0) { - $type = "text/xml"; - } elseif (substr_compare($filename, '.json', -5) === 0) { - $type = "text/json"; - } + foreach ($export_feeds as $feed_id) { + $feed = $this->feedDAO->searchById($feed_id); + if ($feed) { + $filename = 'feed_' . $feed->category() . '_' + . $feed->id() . '.json'; + $export_files[$filename] = $this->generateArticles( + 'feed', $feed + ); + } + } - $this->exportFile($filename, $export_files[$filename], $type); - } else { - Minz_Request::forward(array('c' => 'importExport'), true); + $nb_files = count($export_files); + if ($nb_files > 1) { + // If there are more than 1 file to export, we need a zip archive. + try { + $this->exportZip($export_files); + } catch (Exception $e) { + # Oops, there is no Zip extension! + Minz_Request::bad(_t('export_no_zip_extension'), + array('c' => 'importExport', 'a' => 'index')); } + } elseif ($nb_files === 1) { + // Only one file? Guess its type and export it. + $filename = key($export_files); + $type = $this->guessFileType($filename); + $this->exportFile('freshrss_' . $filename, $export_files[$filename], $type); + } else { + Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true); } } @@ -384,7 +373,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $this->view->categories = $this->catDAO->listCategories(); if ($type == 'starred') { - $this->view->list_title = Minz_Translate::t('starred_list'); + $this->view->list_title = _t('starred_list'); $this->view->type = 'starred'; $unread_fav = $this->entryDAO->countUnreadReadFavorites(); $this->view->entries = $this->entryDAO->listWhere( @@ -392,9 +381,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $unread_fav['all'] ); } elseif ($type == 'feed' && !is_null($feed)) { - $this->view->list_title = Minz_Translate::t( - 'feed_list', $feed->name() - ); + $this->view->list_title = _t('feed_list', $feed->name()); $this->view->type = 'feed/' . $feed->id(); $this->view->entries = $this->entryDAO->listWhere( 'f', $feed->id(), FreshRSS_Entry::STATE_ALL, 'ASC', @@ -430,11 +417,18 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } private function exportFile($filename, $content, $type) { - if (is_null($type)) { + if ($type === 'unknown') { return; } - header('Content-Type: ' . $type . '; charset=utf-8'); + $content_type = ''; + if ($type === 'opml') { + $content_type = "text/opml"; + } elseif ($type === 'json_feed' || $type === 'json_starred') { + $content_type = "text/json"; + } + + header('Content-Type: ' . $content_type . '; charset=utf-8'); header('Content-disposition: attachment; filename=' . $filename); print($content); } diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 3119073b8..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 = ''; @@ -295,6 +296,41 @@ 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() { + $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 () { if (Minz_Request::isPost()) { $ok = false; @@ -312,6 +348,11 @@ class FreshRSS_index_Controller extends Minz_ActionController { if ($ok) { Minz_Session::_param('currentUser', $username); 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); } @@ -371,6 +412,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { Minz_Session::_param('currentUser'); Minz_Session::_param('mail'); Minz_Session::_param('passwordHash'); + self::deleteLongTermCookie(); Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } } diff --git a/app/Controllers/statsController.php b/app/Controllers/statsController.php index 06a20c2a6..000b41dd2 100644 --- a/app/Controllers/statsController.php +++ b/app/Controllers/statsController.php @@ -58,15 +58,20 @@ class FreshRSS_stats_Controller extends Minz_ActionController { public function repartitionAction() { $statsDAO = FreshRSS_Factory::createStatsDAO(); + $categoryDAO = new FreshRSS_CategoryDAO(); $feedDAO = FreshRSS_Factory::createFeedDao(); Minz_View::appendScript(Minz_Url::display('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js'))); $id = Minz_Request::param ('id', null); + $this->view->categories = $categoryDAO->listCategories(); $this->view->feed = $feedDAO->searchById($id); $this->view->days = $statsDAO->getDays(); $this->view->months = $statsDAO->getMonths(); $this->view->repartitionHour = $statsDAO->calculateEntryRepartitionPerFeedPerHour($id); + $this->view->averageHour = $statsDAO->calculateEntryAveragePerFeedPerHour($id); $this->view->repartitionDayOfWeek = $statsDAO->calculateEntryRepartitionPerFeedPerDayOfWeek($id); + $this->view->averageDayOfWeek = $statsDAO->calculateEntryAveragePerFeedPerDayOfWeek($id); $this->view->repartitionMonth = $statsDAO->calculateEntryRepartitionPerFeedPerMonth($id); + $this->view->averageMonth = $statsDAO->calculateEntryAveragePerFeedPerMonth($id); } public function firstAction() { |
