aboutsummaryrefslogtreecommitdiff
path: root/app/Controllers
diff options
context:
space:
mode:
authorGravatar Marien Fressinaud <dev@marienfressinaud.fr> 2014-09-08 19:26:35 +0200
committerGravatar Marien Fressinaud <dev@marienfressinaud.fr> 2014-09-08 19:26:35 +0200
commitef1b35fc4385c99c4d38e3f87e8126d0dbe21519 (patch)
treec2127f92281084c3cb28f635dea63a9a179eabbb /app/Controllers
parent909d8747ba09f9c9a6ac895f1f4f0763bdb27a55 (diff)
parentc3fd8877c021b86180b3bea4d4260e6478f0558e (diff)
Merge branch 'dev' into 411-update-system
Conflicts: constants.php
Diffstat (limited to 'app/Controllers')
-rwxr-xr-xapp/Controllers/configureController.php1
-rw-r--r--app/Controllers/importExportController.php252
-rwxr-xr-xapp/Controllers/indexController.php52
-rw-r--r--app/Controllers/statsController.php5
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() {