From e7dba0ce7cfaf5e84687593a8b0d58d89fbff302 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sat, 9 Aug 2014 23:29:13 +0200 Subject: Add basic system of update - Check on update.freshrss.org for new updates - Download script - Apply script - Need translations and verifications NOTE: current script on server indicates version 0.7.3 is an update of 0.8-dev ==> IT'S ONLY FOR MY TESTS! Script just does a backup of ./data actually... See https://github.com/marienfressinaud/FreshRSS/issues/411 --- app/Controllers/updateController.php | 106 +++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 app/Controllers/updateController.php (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php new file mode 100644 index 000000000..a15cb9fd5 --- /dev/null +++ b/app/Controllers/updateController.php @@ -0,0 +1,106 @@ +view->loginOk && Minz_Configuration::isAdmin($current_user)) { + Minz_Error::error( + 403, + array('error' => array(_t('access_denied'))) + ); + } + + Minz_View::prependTitle(_t('update_system') . ' · '); + } + + public function indexAction() { + if (file_exists(UPDATE_FILENAME)) { + // There is an update file to apply! + $this->view->message = array( + 'status' => 'good', + 'title' => _t('ok'), + 'body' => _t('update_can_apply', _url('update', 'apply')) + ); + + return; + } + } + + public function checkAction() { + $this->view->change_view('update', 'index'); + + if (file_exists(UPDATE_FILENAME)) { + // There is already an update file to apply: we don't need to check + // the webserver! + $this->view->message = array( + 'status' => 'good', + 'title' => _t('ok'), + 'body' => _t('update_can_apply', _url('update', 'apply')) + ); + + return; + } + + $c = curl_init(FRESHRSS_UPDATE_WEBSITE); + curl_setopt($c, CURLOPT_RETURNTRANSFER, true); + $result = curl_exec($c); + + if (curl_getinfo($c, CURLINFO_HTTP_CODE) == 200) { + $res_array = explode("\n", $result, 2); + $status = $res_array[0]; + + if (strpos($status, 'UPDATE') === 0) { + $script = $res_array[1]; + if (file_put_contents(UPDATE_FILENAME, $script) !== false) { + $this->view->message = array( + 'status' => 'good', + 'title' => _t('ok'), + 'body' => _t('update_can_apply', _url('update', 'apply')) + ); + } else { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('update_problem') + ); + } + } else { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('no_update') + ); + } + } else { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('update_server_not_found', FRESHRSS_UPDATE_WEBSITE) + ); + } + curl_close($c); + } + + public function applyAction() { + require(UPDATE_FILENAME); + $res = apply_update(); + + if ($res === true) { + @unlink(UPDATE_FILENAME); + + Minz_Session::_param('notification', array( + 'type' => 'good', + 'content' => Minz_Translate::t('update_finished') + )); + + Minz_Request::forward(array(), true); + } else { + Minz_Session::_param('notification', array( + 'type' => 'bad', + 'content' => Minz_Translate::t('update_failed', $res) + )); + + Minz_Request::forward(array('c' => 'update'), true); + } + } +} \ No newline at end of file -- cgit v1.2.3 From 7ed111b1bf152613d17254808a4fcf89f5774297 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 10 Aug 2014 10:35:17 +0200 Subject: Add translations for update system --- app/Controllers/updateController.php | 7 +++++-- app/i18n/en.php | 9 +++++++++ app/i18n/fr.php | 11 ++++++++++- app/views/update/index.phtml | 4 +++- 4 files changed, 27 insertions(+), 4 deletions(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index a15cb9fd5..a94af4417 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -11,6 +11,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { } Minz_View::prependTitle(_t('update_system') . ' · '); + $this->view->last_update_time = 'unknown'; // TODO } public function indexAction() { @@ -61,7 +62,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { $this->view->message = array( 'status' => 'bad', 'title' => _t('damn'), - 'body' => _t('update_problem') + 'body' => _t('update_problem', 'Cannot save the update script') ); } } else { @@ -88,6 +89,8 @@ class FreshRSS_update_Controller extends Minz_ActionController { if ($res === true) { @unlink(UPDATE_FILENAME); + // TODO: record last update + Minz_Session::_param('notification', array( 'type' => 'good', 'content' => Minz_Translate::t('update_finished') @@ -97,7 +100,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { } else { Minz_Session::_param('notification', array( 'type' => 'bad', - 'content' => Minz_Translate::t('update_failed', $res) + 'content' => Minz_Translate::t('update_problem', $res) )); Minz_Request::forward(array('c' => 'update'), true); diff --git a/app/i18n/en.php b/app/i18n/en.php index 6110ccb11..6a0b4a139 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -192,6 +192,7 @@ return array ( 'informations' => 'Information', 'damn' => 'Damn!', + 'ok' => 'Ok!', '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', @@ -409,5 +410,13 @@ return array ( 'stats_top_feed' => 'Top ten feeds', 'stats_entry_count' => 'Entry count', + 'update' => 'Update', + 'update_system' => 'Update system', + 'update_check' => 'Check for new updates', + 'update_last' => 'Last update: %s', 'update_can_apply' => 'There is an available update. Apply', + 'update_server_not_found' => 'Update server cannot be found. [%s]', + 'no_update' => 'No update to apply', + 'update_problem' => 'Update has encountered an error: %s', + 'update_finished' => 'Update is now finished!', ); diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 5f88aa069..d0637b9f7 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -192,6 +192,7 @@ return array ( 'informations' => 'Informations', 'damn' => 'Arf !', + 'ok' => 'Ok !', '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', @@ -409,5 +410,13 @@ return array ( 'stats_top_feed' => 'Les dix plus gros flux', 'stats_entry_count' => 'Nombre d’articles', - 'update_can_apply' => 'Il y’a une mise à jour à appliquer. Appliquer', + 'update' => 'Mise à jour', + 'update_system' => 'Système de mise à jour', + 'update_check' => 'Vérifier les mises à jour', + 'update_last' => 'Dernière mise à jour : %s', + 'update_can_apply' => 'Il y’a une mise à jour à appliquer. Appliquer la mise à jour', + 'update_server_not_found' => 'Le serveur de mise à jour n’a pas été trouvé. [%s]', + '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 !', ); diff --git a/app/views/update/index.phtml b/app/views/update/index.phtml index a1a872845..8f6ee6269 100644 --- a/app/views/update/index.phtml +++ b/app/views/update/index.phtml @@ -12,8 +12,10 @@ message) || $this->message['status'] !== 'good') { ?>

- last_update_time); ?>

+

+ +

-- cgit v1.2.3 From 9a5d6245fbeb413766362fd6b2c4f5f5b6a22a22 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 10 Aug 2014 10:55:51 +0200 Subject: Improve update API Update script must implement 4 functions: - apply_update() to perform the update (most important). Return true if all is ok, else false. - need_info_update() returns true if we need more info for update, else false. If this function always returns false, you don't need to implement following functions (but it's better to not forget) - ask_info_update() should be a HTML form to ask infos. Method must be post and action must point to _url('update', 'apply') (or leave it blank) - save_info_update() is called for POST requests (to save form from ask_info_update()) --- app/Controllers/updateController.php | 37 +++++++++++++++++++++--------------- app/views/update/apply.phtml | 9 +++++++++ app/views/update/index.phtml | 2 ++ 3 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 app/views/update/apply.phtml (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index a94af4417..1095f9da7 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -84,26 +84,33 @@ class FreshRSS_update_Controller extends Minz_ActionController { public function applyAction() { require(UPDATE_FILENAME); - $res = apply_update(); - if ($res === true) { - @unlink(UPDATE_FILENAME); + if (Minz_Request::isPost()) { + save_info_update(); + } - // TODO: record last update + if (!need_info_update()) { + $res = apply_update(); - Minz_Session::_param('notification', array( - 'type' => 'good', - 'content' => Minz_Translate::t('update_finished') - )); + if ($res === true) { + @unlink(UPDATE_FILENAME); - Minz_Request::forward(array(), true); - } else { - Minz_Session::_param('notification', array( - 'type' => 'bad', - 'content' => Minz_Translate::t('update_problem', $res) - )); + // TODO: record last update + + Minz_Session::_param('notification', array( + 'type' => 'good', + 'content' => Minz_Translate::t('update_finished') + )); - Minz_Request::forward(array('c' => 'update'), true); + Minz_Request::forward(array(), true); + } else { + Minz_Session::_param('notification', array( + 'type' => 'bad', + 'content' => Minz_Translate::t('update_problem', $res) + )); + + Minz_Request::forward(array('c' => 'update'), true); + } } } } \ No newline at end of file diff --git a/app/views/update/apply.phtml b/app/views/update/apply.phtml new file mode 100644 index 000000000..d7ea466c5 --- /dev/null +++ b/app/views/update/apply.phtml @@ -0,0 +1,9 @@ +partial('aside_configure'); ?> + +
+ + +

+ + +
\ No newline at end of file diff --git a/app/views/update/index.phtml b/app/views/update/index.phtml index 8f6ee6269..1824c02b8 100644 --- a/app/views/update/index.phtml +++ b/app/views/update/index.phtml @@ -1,6 +1,8 @@ partial('aside_configure'); ?>
+ +

message)) { ?> -- cgit v1.2.3 From 3ca8c7ec4c55b4fa751fbcdc8e28f28351c4a967 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 10 Aug 2014 11:52:18 +0200 Subject: Litlle improvements (update system) - Check UPDATE_FILENAME exists before applying update - Add empty line at the end of files --- app/Controllers/updateController.php | 7 ++++++- app/views/update/apply.phtml | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 1095f9da7..fa62f4a70 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -79,10 +79,15 @@ class FreshRSS_update_Controller extends Minz_ActionController { 'body' => _t('update_server_not_found', FRESHRSS_UPDATE_WEBSITE) ); } + curl_close($c); } public function applyAction() { + if (!file_exists(UPDATE_FILENAME)) { + Minz_Request::forward(array('c' => 'update'), true); + } + require(UPDATE_FILENAME); if (Minz_Request::isPost()) { @@ -113,4 +118,4 @@ class FreshRSS_update_Controller extends Minz_ActionController { } } } -} \ No newline at end of file +} diff --git a/app/views/update/apply.phtml b/app/views/update/apply.phtml index d7ea466c5..30566c7ab 100644 --- a/app/views/update/apply.phtml +++ b/app/views/update/apply.phtml @@ -6,4 +6,4 @@

-
\ No newline at end of file + -- cgit v1.2.3 From 909d8747ba09f9c9a6ac895f1f4f0763bdb27a55 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 12 Aug 2014 20:15:46 +0200 Subject: Update system now uses HTTPS connection - Add some curl checks - Refactor code --- app/Controllers/updateController.php | 60 +++++++++++++++++++----------------- constants.php | 2 +- 2 files changed, 33 insertions(+), 29 deletions(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index fa62f4a70..857d975b2 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -44,43 +44,47 @@ class FreshRSS_update_Controller extends Minz_ActionController { $c = curl_init(FRESHRSS_UPDATE_WEBSITE); curl_setopt($c, CURLOPT_RETURNTRANSFER, true); + curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2); $result = curl_exec($c); + $c_status = curl_getinfo($c, CURLINFO_HTTP_CODE); + curl_close($c); - if (curl_getinfo($c, CURLINFO_HTTP_CODE) == 200) { - $res_array = explode("\n", $result, 2); - $status = $res_array[0]; - - if (strpos($status, 'UPDATE') === 0) { - $script = $res_array[1]; - if (file_put_contents(UPDATE_FILENAME, $script) !== false) { - $this->view->message = array( - 'status' => 'good', - 'title' => _t('ok'), - 'body' => _t('update_can_apply', _url('update', 'apply')) - ); - } else { - $this->view->message = array( - 'status' => 'bad', - 'title' => _t('damn'), - 'body' => _t('update_problem', 'Cannot save the update script') - ); - } - } else { - $this->view->message = array( - 'status' => 'bad', - 'title' => _t('damn'), - 'body' => _t('no_update') - ); - } - } else { + if ($c_status !== 200) { $this->view->message = array( 'status' => 'bad', 'title' => _t('damn'), 'body' => _t('update_server_not_found', FRESHRSS_UPDATE_WEBSITE) ); + return; } - curl_close($c); + $res_array = explode("\n", $result, 2); + $status = $res_array[0]; + if (strpos($status, 'UPDATE') !== 0) { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('no_update') + ); + + return; + } + + $script = $res_array[1]; + if (file_put_contents(UPDATE_FILENAME, $script) !== false) { + $this->view->message = array( + 'status' => 'good', + 'title' => _t('ok'), + 'body' => _t('update_can_apply', _url('update', 'apply')) + ); + } else { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('update_problem', 'Cannot save the update script') + ); + } } public function applyAction() { diff --git a/constants.php b/constants.php index a968b82f4..ba9c508dc 100644 --- a/constants.php +++ b/constants.php @@ -1,7 +1,7 @@ Date: Mon, 8 Sep 2014 19:52:21 +0200 Subject: Check FRESHRSS_PATH is writable. FRESHRSS_PATH needs to be writable before performing update. --- app/Controllers/updateController.php | 41 ++++++++++++------------------------ app/i18n/en.php | 2 ++ app/i18n/fr.php | 2 ++ 3 files changed, 18 insertions(+), 27 deletions(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 857d975b2..5d5ec3586 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -15,15 +15,19 @@ class FreshRSS_update_Controller extends Minz_ActionController { } public function indexAction() { - if (file_exists(UPDATE_FILENAME)) { + if (file_exists(UPDATE_FILENAME) && !is_writable(FRESHRSS_PATH)) { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('file_is_nok', FRESHRSS_PATH) + ); + } elseif (file_exists(UPDATE_FILENAME)) { // There is an update file to apply! $this->view->message = array( 'status' => 'good', 'title' => _t('ok'), 'body' => _t('update_can_apply', _url('update', 'apply')) ); - - return; } } @@ -33,11 +37,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { if (file_exists(UPDATE_FILENAME)) { // There is already an update file to apply: we don't need to check // the webserver! - $this->view->message = array( - 'status' => 'good', - 'title' => _t('ok'), - 'body' => _t('update_can_apply', _url('update', 'apply')) - ); + Minz_Request::forward(array('c' => 'update')); return; } @@ -73,11 +73,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { $script = $res_array[1]; if (file_put_contents(UPDATE_FILENAME, $script) !== false) { - $this->view->message = array( - 'status' => 'good', - 'title' => _t('ok'), - 'body' => _t('update_can_apply', _url('update', 'apply')) - ); + Minz_Request::forward(array('c' => 'update')); } else { $this->view->message = array( 'status' => 'bad', @@ -88,7 +84,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { } public function applyAction() { - if (!file_exists(UPDATE_FILENAME)) { + if (!file_exists(UPDATE_FILENAME) || !is_writable(FRESHRSS_PATH)) { Minz_Request::forward(array('c' => 'update'), true); } @@ -104,21 +100,12 @@ class FreshRSS_update_Controller extends Minz_ActionController { if ($res === true) { @unlink(UPDATE_FILENAME); - // TODO: record last update + // TODO: record last update_finished - Minz_Session::_param('notification', array( - 'type' => 'good', - 'content' => Minz_Translate::t('update_finished') - )); - - Minz_Request::forward(array(), true); + Minz_Request::good(_t('update_finished')); } else { - Minz_Session::_param('notification', array( - 'type' => 'bad', - 'content' => Minz_Translate::t('update_problem', $res) - )); - - Minz_Request::forward(array('c' => 'update'), true); + Minz_Request::bad(_t('update_problem', $res), + array('c' => 'update', 'a' => 'index')); } } } diff --git a/app/i18n/en.php b/app/i18n/en.php index 95356af2c..c5911cde7 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -152,6 +152,8 @@ return array ( 'public' => 'Public', 'invalid_login' => 'Login is invalid', + 'file_is_nok' => 'Check permissions on %s directory. HTTP server must have rights to write into.', + // VIEWS 'save' => 'Save', 'delete' => 'Delete', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 8437e872e..789a0bb98 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -152,6 +152,8 @@ return array ( 'public' => 'Public', 'invalid_login' => 'L’identifiant est invalide !', + 'file_is_nok' => 'Veuillez vérifier les droits sur le répertoire %s. Le serveur HTTP doit être capable d’écrire dedans.', + // VIEWS 'save' => 'Enregistrer', 'delete' => 'Supprimer', -- cgit v1.2.3 From d59eebf5423afb94ff68550aa9218674889ab4ad Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Mon, 8 Sep 2014 20:07:09 +0200 Subject: Add data/last_update.txt Remember last update timestamp. --- app/Controllers/updateController.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 5d5ec3586..5424792f4 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -11,7 +11,11 @@ class FreshRSS_update_Controller extends Minz_ActionController { } Minz_View::prependTitle(_t('update_system') . ' · '); - $this->view->last_update_time = 'unknown'; // TODO + $this->view->last_update_time = 'unknown'; + $timestamp = (int)@file_get_contents(DATA_PATH . '/last_update.txt'); + if (is_numeric($timestamp) && $timestamp > 0) { + $this->view->last_update_time = timestamptodate($timestamp); + } } public function indexAction() { @@ -99,8 +103,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { if ($res === true) { @unlink(UPDATE_FILENAME); - - // TODO: record last update_finished + @file_put_contents(DATA_PATH . '/last_update.txt', time()); Minz_Request::good(_t('update_finished')); } else { -- cgit v1.2.3 From 213bc2b9ddef88fd9e3cb50ac0893742f5fdd101 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Fri, 12 Sep 2014 21:07:53 +0200 Subject: Check if update has been done during last minute Cancel check action if update has been done during last hour. --- app/Controllers/updateController.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 5424792f4..ec6778d51 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -38,7 +38,11 @@ class FreshRSS_update_Controller extends Minz_ActionController { public function checkAction() { $this->view->change_view('update', 'index'); - if (file_exists(UPDATE_FILENAME)) { + // Get the last update. If already check during the last hour, do nothing. + $last_update = (int)@file_get_contents(DATA_PATH . '/last_update.txt'); + $check_last_hour = (time() - 3600) <= $last_update; + + if (file_exists(UPDATE_FILENAME) || $check_last_hour) { // There is already an update file to apply: we don't need to check // the webserver! Minz_Request::forward(array('c' => 'update')); -- cgit v1.2.3 From 098f5e6d747cf442b72f75a22a9ce43d36605d65 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Fri, 12 Sep 2014 21:10:45 +0200 Subject: Log error if update.freshrss.org is unreachable --- app/Controllers/updateController.php | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index ec6778d51..4c1dd002c 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -56,9 +56,14 @@ class FreshRSS_update_Controller extends Minz_ActionController { curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2); $result = curl_exec($c); $c_status = curl_getinfo($c, CURLINFO_HTTP_CODE); + $c_error = curl_error($c); curl_close($c); if ($c_status !== 200) { + Minz_Log::error( + 'Error during update (HTTP code ' . $c_status . '): ' . $c_error + ); + $this->view->message = array( 'status' => 'bad', 'title' => _t('damn'), -- cgit v1.2.3 From a4e43e9c53ac404d16af5d913a56eeb444b1ce10 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 16 Sep 2014 18:30:24 +0200 Subject: Message if last update checking is close last_update.txt is updated even if there is no update If last_update.txt has been modified during last hour, show a message See https://github.com/marienfressinaud/FreshRSS/issues/480#issuecomment-55765373 --- app/Controllers/updateController.php | 11 ++++++----- app/i18n/en.php | 2 +- app/i18n/fr.php | 2 +- app/views/update/index.phtml | 17 +++++++++++++---- 4 files changed, 21 insertions(+), 11 deletions(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 4c1dd002c..72244e9c7 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -12,9 +12,11 @@ class FreshRSS_update_Controller extends Minz_ActionController { Minz_View::prependTitle(_t('update_system') . ' · '); $this->view->last_update_time = 'unknown'; + $this->view->check_last_hour = false; $timestamp = (int)@file_get_contents(DATA_PATH . '/last_update.txt'); if (is_numeric($timestamp) && $timestamp > 0) { $this->view->last_update_time = timestamptodate($timestamp); + $this->view->check_last_hour = (time() - 3600) <= $timestamp; } } @@ -38,13 +40,10 @@ class FreshRSS_update_Controller extends Minz_ActionController { public function checkAction() { $this->view->change_view('update', 'index'); - // Get the last update. If already check during the last hour, do nothing. - $last_update = (int)@file_get_contents(DATA_PATH . '/last_update.txt'); - $check_last_hour = (time() - 3600) <= $last_update; - - if (file_exists(UPDATE_FILENAME) || $check_last_hour) { + if (file_exists(UPDATE_FILENAME) || $this->view->check_last_hour) { // There is already an update file to apply: we don't need to check // the webserver! + // Or if already check during the last hour, do nothing. Minz_Request::forward(array('c' => 'update')); return; @@ -81,6 +80,8 @@ class FreshRSS_update_Controller extends Minz_ActionController { 'body' => _t('no_update') ); + @file_put_contents(DATA_PATH . '/last_update.txt', time()); + return; } diff --git a/app/i18n/en.php b/app/i18n/en.php index c5911cde7..8f39115ad 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -424,7 +424,7 @@ return array ( 'update' => 'Update', 'update_system' => 'Update system', 'update_check' => 'Check for new updates', - 'update_last' => 'Last update: %s', + 'update_last' => 'Last verification: %s', 'update_can_apply' => 'There is an available update. Apply', 'update_server_not_found' => 'Update server cannot be found. [%s]', 'no_update' => 'No update to apply', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 789a0bb98..48b4c1732 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -424,7 +424,7 @@ return array ( 'update' => 'Mise à jour', 'update_system' => 'Système de mise à jour', 'update_check' => 'Vérifier les mises à jour', - 'update_last' => 'Dernière mise à jour : %s', + 'update_last' => 'Dernière vérification : %s', 'update_can_apply' => 'Il y’a une mise à jour à appliquer. Appliquer la mise à jour', 'update_server_not_found' => 'Le serveur de mise à jour n’a pas été trouvé. [%s]', 'no_update' => 'Aucune mise à jour à appliquer', diff --git a/app/views/update/index.phtml b/app/views/update/index.phtml index 1824c02b8..5be8b1e8b 100644 --- a/app/views/update/index.phtml +++ b/app/views/update/index.phtml @@ -5,17 +5,26 @@

+

+ last_update_time); ?> +

+ message)) { ?>

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

+ check_last_hour) { ?> +

+ + +

- message) || $this->message['status'] !== 'good') { ?> -

- last_update_time); ?> -

+ check_last_hour && + (empty($this->message) || $this->message['status'] !== 'good')) { + ?>

-- cgit v1.2.3 From a3b5e72729be08f79585c782d497f49edd11c064 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Wed, 17 Sep 2014 13:26:32 +0200 Subject: Move button to apply update outside the message --- app/Controllers/updateController.php | 4 +++- app/i18n/en.php | 3 ++- app/i18n/fr.php | 3 ++- app/views/update/index.phtml | 4 ++++ 4 files changed, 11 insertions(+), 3 deletions(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 72244e9c7..78d636163 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -11,6 +11,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { } Minz_View::prependTitle(_t('update_system') . ' · '); + $this->view->update_to_apply = false; $this->view->last_update_time = 'unknown'; $this->view->check_last_hour = false; $timestamp = (int)@file_get_contents(DATA_PATH . '/last_update.txt'); @@ -29,10 +30,11 @@ class FreshRSS_update_Controller extends Minz_ActionController { ); } elseif (file_exists(UPDATE_FILENAME)) { // There is an update file to apply! + $this->view->update_to_apply = true; $this->view->message = array( 'status' => 'good', 'title' => _t('ok'), - 'body' => _t('update_can_apply', _url('update', 'apply')) + 'body' => _t('update_can_apply') ); } } diff --git a/app/i18n/en.php b/app/i18n/en.php index 562697585..f84593cb5 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -428,7 +428,8 @@ return array ( 'update_system' => 'Update system', 'update_check' => 'Check for new updates', 'update_last' => 'Last verification: %s', - 'update_can_apply' => 'There is an available update. Apply', + 'update_can_apply' => 'There is an available update.', + 'update_apply' => 'Apply', 'update_server_not_found' => 'Update server cannot be found. [%s]', 'no_update' => 'No update to apply', 'update_problem' => 'Update has encountered an error: %s', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 9516f66cb..da5819529 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -428,7 +428,8 @@ return array ( 'update_system' => 'Système de mise à jour', 'update_check' => 'Vérifier les mises à jour', 'update_last' => 'Dernière vérification : %s', - 'update_can_apply' => 'Il y’a une mise à jour à appliquer. Appliquer la mise à jour', + 'update_can_apply' => 'Il y’a une mise à jour à appliquer.', + 'update_apply' => 'Appliquer la mise à jour', 'update_server_not_found' => 'Le serveur de mise à jour n’a pas été trouvé. [%s]', 'no_update' => 'Aucune mise à jour à appliquer', 'update_problem' => 'La mise à jour a rencontré un problème : %s', diff --git a/app/views/update/index.phtml b/app/views/update/index.phtml index 5be8b1e8b..401f6acd6 100644 --- a/app/views/update/index.phtml +++ b/app/views/update/index.phtml @@ -29,4 +29,8 @@

+ + update_to_apply) { ?> + + -- cgit v1.2.3 From 3b8c381689334a15e7c034425f8615860dc3fa13 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Wed, 17 Sep 2014 13:37:39 +0200 Subject: No cache for update system See https://github.com/marienfressinaud/FreshRSS/issues/616 --- app/Controllers/updateController.php | 2 ++ 1 file changed, 2 insertions(+) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 78d636163..da5bddc65 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -10,6 +10,8 @@ class FreshRSS_update_Controller extends Minz_ActionController { ); } + invalidateHttpCache(); + Minz_View::prependTitle(_t('update_system') . ' · '); $this->view->update_to_apply = false; $this->view->last_update_time = 'unknown'; -- cgit v1.2.3 From 79aa5beaf44af13a1828bfa5fc824a08c62054dc Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Mon, 6 Oct 2014 23:29:20 +0200 Subject: Refactor authentication system. Big work, not finished. A lot of features have been removed. See https://github.com/marienfressinaud/FreshRSS/issues/655 --- app/Controllers/categoryController.php | 2 +- app/Controllers/configureController.php | 2 +- app/Controllers/entryController.php | 2 +- app/Controllers/feedController.php | 2 +- app/Controllers/importExportController.php | 2 +- app/Controllers/indexController.php | 296 ++++++----------------------- app/Controllers/statsController.php | 2 +- app/Controllers/subscriptionController.php | 2 +- app/Controllers/updateController.php | 2 +- app/Controllers/usersController.php | 2 +- app/FreshRSS.php | 135 ++----------- app/Models/Auth.php | 209 ++++++++++++++++++++ app/layout/aside_flux.phtml | 6 +- app/layout/header.phtml | 32 +--- app/layout/nav_menu.phtml | 4 +- app/views/helpers/view/normal_view.phtml | 6 +- app/views/index/index.phtml | 2 +- app/views/index/login.phtml | 1 - app/views/index/logout.phtml | 1 - app/views/index/resetAuth.phtml | 33 ---- 20 files changed, 309 insertions(+), 434 deletions(-) create mode 100644 app/Models/Auth.php delete mode 100644 app/views/index/login.phtml delete mode 100644 app/views/index/logout.phtml delete mode 100644 app/views/index/resetAuth.phtml (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/categoryController.php b/app/Controllers/categoryController.php index c79f37fa4..537a2b210 100644 --- a/app/Controllers/categoryController.php +++ b/app/Controllers/categoryController.php @@ -12,7 +12,7 @@ class FreshRSS_category_Controller extends Minz_ActionController { * */ public function firstAction() { - if (!$this->view->loginOk) { + if (!FreshRSS_Auth::hasAccess()) { Minz_Error::error( 403, array('error' => array(_t('access_denied'))) diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 789e9dfb0..7e77a757a 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -10,7 +10,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { * underlying framework. */ public function firstAction() { - if (!$this->view->loginOk) { + if (!FreshRSS_Auth::hasAccess()) { Minz_Error::error( 403, array('error' => array(_t('access_denied'))) diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index c46fbf346..a1dfacb4d 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -10,7 +10,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { * underlying framework. */ public function firstAction() { - if (!$this->view->loginOk) { + if (!FreshRSS_Auth::hasAccess()) { Minz_Error::error( 403, array('error' => array(_t('access_denied'))) diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 18829d252..2a7238eaf 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -10,7 +10,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { * underlying framework. */ public function firstAction() { - if (!$this->view->loginOk) { + if (!FreshRSS_Auth::hasAccess()) { // Token is useful in the case that anonymous refresh is forbidden // and CRON task cannot be used with php command so the user can // set a CRON task to refresh his feeds by using token inside url diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 57759f277..aaac1b68b 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -10,7 +10,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * underlying framework. */ public function firstAction() { - if (!$this->view->loginOk) { + if (!FreshRSS_Auth::hasAccess()) { Minz_Error::error( 403, array('error' => array(_t('access_denied'))) diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 0d2eff700..3006480f9 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -8,7 +8,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { $token = $this->view->conf->token; // check if user is logged in - if (!$this->view->loginOk && !Minz_Configuration::allowAnonymous()) { + if (!FreshRSS_Auth::hasAccess() && !Minz_Configuration::allowAnonymous()) { $token_param = Minz_Request::param('token', ''); $token_is_ok = ($token != '' && $token === $token_param); if ($output === 'rss' && !$token_is_ok) { @@ -20,7 +20,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { } elseif ($output !== 'rss') { // "hard" redirection is not required, just ask dispatcher to // forward to the login form without 302 redirection - Minz_Request::forward(array('c' => 'index', 'a' => 'formLogin')); + Minz_Request::forward(array('c' => 'index', 'a' => 'login')); return; } } @@ -207,7 +207,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { } public function logsAction() { - if (!$this->view->loginOk) { + if (!FreshRSS_Auth::hasAccess()) { Minz_Error::error( 403, array('error' => array(_t('access_denied'))) @@ -229,265 +229,91 @@ class FreshRSS_index_Controller extends Minz_ActionController { $this->view->logsPaginator->_currentPage($page); } + /** + * This action handles the login 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); - - $loginOk = false; - $reason = ''; - if ($res['status'] === 'okay') { - $email = filter_var($res['email'], FILTER_VALIDATE_EMAIL); - if ($email != '') { - $personaFile = DATA_PATH . '/persona/' . $email . '.txt'; - if (($currentUser = @file_get_contents($personaFile)) !== false) { - $currentUser = trim($currentUser); - if (ctype_alnum($currentUser)) { - try { - $this->conf = new FreshRSS_Configuration($currentUser); - $loginOk = strcasecmp($email, $this->conf->mail_login) === 0; - } catch (Minz_Exception $e) { - $reason = 'Invalid configuration for user [' . $currentUser . ']! ' . $e->getMessage(); //Permission denied or conf file does not exist - } - } else { - $reason = 'Invalid username format [' . $currentUser . ']!'; - } - } - } else { - $reason = 'Invalid email format [' . $res['email'] . ']!'; - } - } - if ($loginOk) { - Minz_Session::_param('currentUser', $currentUser); - Minz_Session::_param('mail', $email); - $this->view->loginOk = true; - invalidateHttpCache(); - } else { - $res = array(); - $res['status'] = 'failure'; - $res['reason'] = $reason == '' ? _t('invalid_login') : $reason; - Minz_Log::warning('Persona: ' . $res['reason']); + if (FreshRSS_Auth::hasAccess()) { + Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } - header('Content-Type: application/json; charset=UTF-8'); - $this->view->res = json_encode($res); - } - - public function logoutAction() { - $this->view->_useLayout(false); invalidateHttpCache(); - Minz_Session::_param('currentUser'); - Minz_Session::_param('mail'); - 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()); - } + $auth_type = Minz_Configuration::authType(); + switch ($auth_type) { + case 'form': + Minz_Request::forward(array('c' => 'index', 'a' => 'formLogin')); + break; + case 'http_auth': + case 'none': + // It should not happened! + Minz_Error::error(404); + default: + // TODO load plugin instead + Minz_Error::error(404); } } + /** + * + */ public function formLoginAction() { - if ($this->view->loginOk) { + if (FreshRSS_Auth::hasAccess()) { Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } - if (Minz_Request::isPost()) { - $ok = false; - $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)) { - if (!function_exists('password_verify')) { - include_once(LIB_PATH . '/password_compat.php'); - } - try { - $conf = new FreshRSS_Configuration($username); - $s = $conf->passwordHash; - $ok = password_verify($nonce . $s, $c); - 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::warning('Password mismatch for user ' . $username . ', nonce=' . $nonce . ', c=' . $c); - } - } catch (Minz_Exception $me) { - Minz_Log::warning('Login failure: ' . $me->getMessage()); - } - } else { - Minz_Log::debug('Invalid credential parameters: user=' . $username . ' challenge=' . $c . ' nonce=' . $nonce); - } - if (!$ok) { - $notif = array( - 'type' => 'bad', - 'content' => _t('invalid_login') - ); - Minz_Session::_param('notification', $notif); - } - $this->view->_useLayout(false); - Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); - } elseif (Minz_Configuration::unsafeAutologinEnabled() && isset($_GET['u']) && isset($_GET['p'])) { - Minz_Session::_param('currentUser'); - Minz_Session::_param('mail'); - Minz_Session::_param('passwordHash'); - $username = ctype_alnum($_GET['u']) ? $_GET['u'] : ''; - $passwordPlain = $_GET['p']; - Minz_Request::_param('p'); //Discard plain-text password ASAP - $_GET['p'] = ''; - if (!function_exists('password_verify')) { - include_once(LIB_PATH . '/password_compat.php'); - } - try { - $conf = new FreshRSS_Configuration($username); - $s = $conf->passwordHash; - $ok = password_verify($passwordPlain, $s); - unset($passwordPlain); - if ($ok) { - Minz_Session::_param('currentUser', $username); - Minz_Session::_param('passwordHash', $s); - } else { - Minz_Log::warning('Unsafe password mismatch for user ' . $username); - } - } catch (Minz_Exception $me) { - Minz_Log::warning('Unsafe login failure: ' . $me->getMessage()); - } - Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); - } elseif (!Minz_Configuration::canLogIn()) { - Minz_Error::error( - 403, - array('error' => array(_t('access_denied'))) - ); - } invalidateHttpCache(); - } - public function formLogoutAction() { - $this->view->_useLayout(false); - invalidateHttpCache(); - Minz_Session::_param('currentUser'); - Minz_Session::_param('mail'); - Minz_Session::_param('passwordHash'); - self::deleteLongTermCookie(); - Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); - } - - public function resetAuthAction() { - Minz_View::prependTitle(_t('auth_reset') . ' · '); - 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! - 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; - } - - invalidateHttpCache(); + $file_mtime = @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js'); + Minz_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . $file_mtime)); 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); + $challenge = Minz_Request::param('challenge', ''); + try { + $conf = new FreshRSS_Configuration($username); + } catch(Minz_Exception $e) { + // $username is not a valid user, nor the configuration file! + Minz_Log::warning('Login failure: ' . $e->getMessage()); Minz_Request::bad(_t('invalid_login'), - array('c' => 'index', 'a' => 'resetAuth')); - } - - if (!function_exists('password_verify')) { - include_once(LIB_PATH . '/password_compat.php'); + array('c' => 'index', 'a' => 'login')); } - $s = $conf->passwordHash; - $ok = password_verify($nonce . $s, $c); + $ok = FreshRSS_FormAuth::checkCredentials( + $username, $conf->passwordHash, $nonce, $challenge + ); if ($ok) { - Minz_Configuration::_authType('form'); - $ok = Minz_Configuration::writeFile(); - - if ($ok) { - Minz_Request::good(_t('auth_form_set')); + // Set session parameter to give access to the user. + Minz_Session::_param('currentUser', $username); + Minz_Session::_param('passwordHash', $conf->passwordHash); + FreshRSS_Auth::giveAccess(); + + // Set cookie parameter if nedded. + if (Minz_Request::param('keep_logged_in', false)) { + FreshRSS_FormAuth::makeCookie($username, $conf->passwordHash); } else { - Minz_Request::bad(_t('auth_form_not_set'), - array('c' => 'index', 'a' => 'resetAuth')); + FreshRSS_FormAuth::deleteCookie(); } - } else { - Minz_Log::debug('Password mismatch for user ' . $username . - ', nonce=' . $nonce . ', c=' . $c); + // All is good, go back to the index. + Minz_Request::good(_t('login'), + array('c' => 'index', 'a' => 'index')); + } else { + Minz_Log::warning('Password mismatch for' . + ' user=' . $username . + ', nonce=' . $nonce . + ', c=' . $challenge); Minz_Request::bad(_t('invalid_login'), - array('c' => 'index', 'a' => 'resetAuth')); + array('c' => 'index', 'a' => 'login')); } } } + + public function logoutAction() { + invalidateHttpCache(); + FreshRSS_Auth::removeAccess(); + Minz_Request::good(_t('disconnected'), + array('c' => 'index', 'a' => 'index')); + } } diff --git a/app/Controllers/statsController.php b/app/Controllers/statsController.php index 99c57c809..0e3430fcc 100644 --- a/app/Controllers/statsController.php +++ b/app/Controllers/statsController.php @@ -118,7 +118,7 @@ class FreshRSS_stats_Controller extends Minz_ActionController { * underlying framework. */ public function firstAction() { - if (!$this->view->loginOk) { + if (!FreshRSS_Auth::hasAccess()) { Minz_Error::error( 403, array('error' => array(_t('access_denied'))) ); diff --git a/app/Controllers/subscriptionController.php b/app/Controllers/subscriptionController.php index 7cc8179a0..a89168eb3 100644 --- a/app/Controllers/subscriptionController.php +++ b/app/Controllers/subscriptionController.php @@ -10,7 +10,7 @@ class FreshRSS_subscription_Controller extends Minz_ActionController { * underlying framework. */ public function firstAction() { - if (!$this->view->loginOk) { + if (!FreshRSS_Auth::hasAccess()) { Minz_Error::error( 403, array('error' => array(_t('access_denied'))) diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index da5bddc65..9da1e8657 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -3,7 +3,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { public function firstAction() { $current_user = Minz_Session::param('currentUser', ''); - if (!$this->view->loginOk && Minz_Configuration::isAdmin($current_user)) { + if (!FreshRSS_Auth::hasAccess() && Minz_Configuration::isAdmin($current_user)) { Minz_Error::error( 403, array('error' => array(_t('access_denied'))) diff --git a/app/Controllers/usersController.php b/app/Controllers/usersController.php index 7d0171bc7..c2b1d163f 100644 --- a/app/Controllers/usersController.php +++ b/app/Controllers/usersController.php @@ -5,7 +5,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { const BCRYPT_COST = 9; //Will also have to be computed client side on mobile devices, so do not use a too high cost public function firstAction() { - if (!$this->view->loginOk) { + if (!FreshRSS_Auth::hasAccess()) { Minz_Error::error( 403, array('error' => array(_t('access_denied'))) diff --git a/app/FreshRSS.php b/app/FreshRSS.php index efd302ecc..35a37b887 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -4,130 +4,33 @@ class FreshRSS extends Minz_FrontController { if (!isset($_SESSION)) { Minz_Session::init('FreshRSS'); } - $loginOk = $this->accessControl(Minz_Session::param('currentUser', '')); + + FreshRSS_Auth::init(); + $this->loadConfiguration(); $this->loadParamsView(); if (Minz_Request::isPost() && !is_referer_from_same_domain()) { - $loginOk = false; //Basic protection against XSRF attacks + //Basic protection against XSRF attacks + FreshRSS_Auth::removeAccess(); Minz_Error::error( 403, array('error' => array(_t('access_denied') . ' [HTTP_REFERER=' . - htmlspecialchars(empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']) . ']')) + htmlspecialchars(empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']) . ']')) ); } - Minz_View::_param('loginOk', $loginOk); - $this->loadStylesAndScripts($loginOk); //TODO: Do not load that when not needed, e.g. some Ajax requests + $this->loadStylesAndScripts(); $this->loadNotifications(); $this->loadExtensions(); } - 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': - $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(); - $loginOk = $currentUser != ''; - break; - case 'persona': - $loginOk = false; - $email = filter_var(Minz_Session::param('mail'), FILTER_VALIDATE_EMAIL); - if ($email != '') { //TODO: Remove redundancy with indexController - $personaFile = DATA_PATH . '/persona/' . $email . '.txt'; - if (($currentUser = @file_get_contents($personaFile)) !== false) { - $currentUser = trim($currentUser); - $loginOk = true; - } - } - if (!$loginOk) { - $currentUser = Minz_Configuration::defaultUser(); - } - break; - case 'none': - $currentUser = Minz_Configuration::defaultUser(); - $loginOk = true; - break; - default: - $currentUser = Minz_Configuration::defaultUser(); - $loginOk = false; - break; - } - } else { - $loginOk = true; - } - - if (!ctype_alnum($currentUser)) { - Minz_Session::_param('currentUser', ''); - die('Invalid username [' . $currentUser . ']!'); - } - + private function loadConfiguration() { + $current_user = Minz_Session::param('currentUser'); try { - $this->conf = new FreshRSS_Configuration($currentUser); + $this->conf = new FreshRSS_Configuration($current_user); Minz_View::_param('conf', $this->conf); - Minz_Session::_param('currentUser', $currentUser); - } catch (Minz_Exception $me) { - $loginOk = false; - try { - $this->conf = new FreshRSS_Configuration(Minz_Configuration::defaultUser()); - Minz_Session::_param('currentUser', Minz_Configuration::defaultUser()); - Minz_View::_param('conf', $this->conf); - $notif = array( - 'type' => 'bad', - 'content' => 'Invalid configuration for user [' . $currentUser . ']!', - ); - Minz_Session::_param('notification', $notif); - Minz_Log::warning($notif['content'] . ' ' . $me->getMessage()); - Minz_Session::_param('currentUser', ''); - } catch (Exception $e) { - die($e->getMessage()); - } - } - - if ($loginOk) { - switch (Minz_Configuration::authType()) { - case 'form': - $loginOk = Minz_Session::param('passwordHash') === $this->conf->passwordHash; - break; - case 'http_auth': - $loginOk = strcasecmp($currentUser, httpAuthUser()) === 0; - break; - case 'persona': - $loginOk = strcasecmp(Minz_Session::param('mail'), $this->conf->mail_login) === 0; - break; - case 'none': - $loginOk = true; - break; - default: - $loginOk = false; - break; - } + } catch(Minz_Exception $e) { + Minz_Log::error('Cannot load configuration file of user `' . $current_user . '`'); + die($e->getMessage()); } - return $loginOk; } private function loadParamsView() { @@ -140,7 +43,7 @@ class FreshRSS extends Minz_FrontController { } } - private function loadStylesAndScripts($loginOk) { + private function loadStylesAndScripts() { $theme = FreshRSS_Themes::load($this->conf->theme); if ($theme) { foreach($theme['files'] as $file) { @@ -158,16 +61,6 @@ class FreshRSS extends Minz_FrontController { } } - switch (Minz_Configuration::authType()) { - case 'form': - if (!$loginOk) { - Minz_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js'))); - } - break; - case 'persona': - Minz_View::appendScript('https://login.persona.org/include.js'); - break; - } Minz_View::appendScript(Minz_Url::display('/scripts/jquery.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/jquery.min.js'))); Minz_View::appendScript(Minz_Url::display('/scripts/shortcut.js?' . @filemtime(PUBLIC_PATH . '/scripts/shortcut.js'))); Minz_View::appendScript(Minz_Url::display('/scripts/main.js?' . @filemtime(PUBLIC_PATH . '/scripts/main.js'))); diff --git a/app/Models/Auth.php b/app/Models/Auth.php new file mode 100644 index 000000000..c4a3abd98 --- /dev/null +++ b/app/Models/Auth.php @@ -0,0 +1,209 @@ +getMessage()); + } + + switch (Minz_Configuration::authType()) { + case 'form': + self::$login_ok = Minz_Session::param('passwordHash') === $conf->passwordHash; + break; + case 'http_auth': + self::$login_ok = strcasecmp($current_user, httpAuthUser()) === 0; + break; + case 'none': + self::$login_ok = true; + break; + default: + // TODO: extensions + self::$login_ok = false; + } + + Minz_Session::_param('loginOk', self::$login_ok); + } + + /** + * Returns if current user is connected. + * + * @return boolean true if user is connected, false else. + */ + public static function hasAccess() { + return self::$login_ok; + } + + /** + * Removes all accesses for the current user. + */ + public static function removeAccess() { + Minz_Session::_param('loginOk'); + self::$login_ok = false; + Minz_Session::_param('currentUser', Minz_Configuration::defaultUser()); + + switch (Minz_Configuration::authType()) { + case 'form': + Minz_Session::_param('passwordHash'); + FreshRSS_FormAuth::deleteCookie(); + break; + case 'http_auth': + case 'none': + // Nothing to do... + break; + default: + // TODO: extensions + } + } +} + + +class FreshRSS_FormAuth { + public static function checkCredentials($username, $hash, $nonce, $challenge) { + if (!ctype_alnum($username) || + !ctype_graph($challenge) || + !ctype_alnum($nonce)) { + Minz_Log::debug('Invalid credential parameters:' . + ' user=' . $username . + ' challenge=' . $challenge . + ' nonce=' . $nonce); + return false; + } + + if (!function_exists('password_verify')) { + include_once(LIB_PATH . '/password_compat.php'); + } + + return password_verify($nonce . $hash, $challenge); + } + + public static function getCredentialsFromCookie() { + $token = Minz_Session::getLongTermCookie('FreshRSS_login'); + if (!ctype_alnum($token)) { + return array(); + } + + $token_file = DATA_PATH . '/tokens/' . $token . '.txt'; + $mtime = @filemtime($token_file); + if ($mtime + 2629744 < time()) { + // Token has expired (> 1 month) or does not exist. + // TODO: 1 month -> use a configuration instead + @unlink($token_file); + return array(); + } + + $credentials = @file_get_contents($token_file); + return $credentials === false ? array() : explode("\t", $credentials, 2); + } + + public static function makeCookie($username, $password_hash) { + do { + $token = sha1(Minz_Configuration::salt() . $username . uniqid(mt_rand(), true)); + $token_file = DATA_PATH . '/tokens/' . $token . '.txt'; + } while (file_exists($token_file)); + + if (@file_put_contents($token_file, $username . "\t" . $password_hash) === false) { + return false; + } + + $expire = time() + 2629744; //1 month //TODO: Use a configuration instead + Minz_Session::setLongTermCookie('FreshRSS_login', $token, $expire); + return $token; + } + + public static function deleteCookie() { + $token = Minz_Session::getLongTermCookie('FreshRSS_login'); + Minz_Session::deleteLongTermCookie('FreshRSS_login'); + if (ctype_alnum($token)) { + @unlink(DATA_PATH . '/tokens/' . $token . '.txt'); + } + + if (rand(0, 10) === 1) { + self::purgeTokens(); + } + } + + public static function purgeTokens() { + $oldest = time() - 2629744; // 1 month // TODO: Use a configuration instead + foreach (new DirectoryIterator(DATA_PATH . '/tokens/') as $file_info) { + // $extension = $file_info->getExtension(); doesn't work in PHP < 5.3.7 + $extension = pathinfo($file_info->getFilename(), PATHINFO_EXTENSION); + if ($extension === 'txt' && $file_info->getMTime() < $oldest) { + @unlink($file_info->getPathname()); + } + } + } +} diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml index a8ae2f424..a66be2ed9 100644 --- a/app/layout/aside_flux.phtml +++ b/app/layout/aside_flux.phtml @@ -2,7 +2,7 @@
    - loginOk) { ?> +
  • @@ -83,11 +83,11 @@ diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index a9e6614e7..090b55785 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -6,7 +6,7 @@ - loginOk) { ?> + diff --git a/app/views/helpers/view/normal_view.phtml b/app/views/helpers/view/normal_view.phtml index 109fad0eb..db25714bb 100644 --- a/app/views/helpers/view/normal_view.phtml +++ b/app/views/helpers/view/normal_view.phtml @@ -7,7 +7,7 @@ if (!empty($this->entries)) { $display_today = true; $display_yesterday = true; $display_others = true; - if ($this->loginOk) { + if (FreshRSS_Auth::hasAccess()) { $sharing = $this->conf->sharing; } else { $sharing = array(); @@ -58,7 +58,7 @@ if (!empty($this->entries)) { } ?>
      loginOk) { + if (FreshRSS_Auth::hasAccess()) { if ($topline_read) { ?>
    • 'entry', 'a' => 'read', 'params' => array('id' => $item->id())); @@ -103,7 +103,7 @@ if (!empty($this->entries)) { ?>
      loginOk) { + if (FreshRSS_Auth::hasAccess()) { if ($bottomline_read) { ?>
    • 'entry', 'a' => 'read', 'params' => array('id' => $item->id())); diff --git a/app/views/index/index.phtml b/app/views/index/index.phtml index 584792e29..a59063557 100644 --- a/app/views/index/index.phtml +++ b/app/views/index/index.phtml @@ -2,7 +2,7 @@ $output = Minz_Request::param('output', 'normal'); -if ($this->loginOk || Minz_Configuration::allowAnonymous()) { +if (FreshRSS_Auth::hasAccess() || Minz_Configuration::allowAnonymous()) { if ($output === 'normal') { $this->renderHelper('view/normal_view'); } elseif ($output === 'reader') { diff --git a/app/views/index/login.phtml b/app/views/index/login.phtml deleted file mode 100644 index 79fbe9d21..000000000 --- a/app/views/index/login.phtml +++ /dev/null @@ -1 +0,0 @@ -res); ?> diff --git a/app/views/index/logout.phtml b/app/views/index/logout.phtml deleted file mode 100644 index a0aba9318..000000000 --- a/app/views/index/logout.phtml +++ /dev/null @@ -1 +0,0 @@ -OK \ No newline at end of file diff --git a/app/views/index/resetAuth.phtml b/app/views/index/resetAuth.phtml deleted file mode 100644 index 6d4282c14..000000000 --- a/app/views/index/resetAuth.phtml +++ /dev/null @@ -1,33 +0,0 @@ -
      -

      - - message)) { ?> -

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

      - - - no_form) { ?> - -

      -
      - -

      - -
      - - -
      -
      - - -
      - -
      -
      - -
      - - -
      -- cgit v1.2.3 From 6009990935a2d06c252073f6b51ea5378536ef52 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 7 Oct 2014 10:16:38 +0200 Subject: Introduce FreshRSS_Auth::hasAccess('admin') Replace Minz_Configuration::isAdmin($user). FreshRSS_Auth::hasAccess() could be extended to others scopes later. See https://github.com/marienfressinaud/FreshRSS/issues/655 --- app/Controllers/configureController.php | 2 +- app/Controllers/updateController.php | 2 +- app/Controllers/usersController.php | 8 ++++---- app/Models/Auth.php | 19 +++++++++++++++---- app/layout/aside_configure.phtml | 5 +---- app/layout/header.phtml | 5 +---- app/views/configure/archiving.phtml | 2 +- app/views/users/index.phtml | 6 +++--- lib/Minz/Configuration.php | 3 --- 9 files changed, 27 insertions(+), 25 deletions(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 7e77a757a..fb8c1466e 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -229,7 +229,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->nb_total = $entryDAO->count(); $this->view->size_user = $entryDAO->size(); - if (Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { + if (FreshRSS_Auth::hasAccess('admin')) { $this->view->size_total = $entryDAO->size(true); } } diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 9da1e8657..9d1e1ddf5 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -3,7 +3,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { public function firstAction() { $current_user = Minz_Session::param('currentUser', ''); - if (!FreshRSS_Auth::hasAccess() && Minz_Configuration::isAdmin($current_user)) { + if (!FreshRSS_Auth::hasAccess('admin')) { Minz_Error::error( 403, array('error' => array(_t('access_denied'))) diff --git a/app/Controllers/usersController.php b/app/Controllers/usersController.php index c2b1d163f..11862ce27 100644 --- a/app/Controllers/usersController.php +++ b/app/Controllers/usersController.php @@ -51,7 +51,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { $this->view->conf->_apiPasswordHash($passwordHash); } - if (Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { + if (FreshRSS_Auth::hasAccess('admin')) { $this->view->conf->_mail_login(Minz_Request::param('mail_login', '', true)); } $email = $this->view->conf->mail_login; @@ -65,7 +65,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { $ok &= (file_put_contents($personaFile, Minz_Session::param('currentUser', '_')) !== false); } - if (Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { + if (FreshRSS_Auth::hasAccess('admin')) { $current_token = $this->view->conf->token; $token = Minz_Request::param('token', $current_token); $this->view->conf->_token($token); @@ -105,7 +105,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { } public function createAction() { - if (Minz_Request::isPost() && Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { + if (Minz_Request::isPost() && FreshRSS_Auth::hasAccess('admin')) { $db = Minz_Configuration::dataBase(); require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); @@ -177,7 +177,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { } public function deleteAction() { - if (Minz_Request::isPost() && Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { + if (Minz_Request::isPost() && FreshRSS_Auth::hasAccess('admin')) { $db = Minz_Configuration::dataBase(); require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); diff --git a/app/Models/Auth.php b/app/Models/Auth.php index c4a3abd98..992b444a5 100644 --- a/app/Models/Auth.php +++ b/app/Models/Auth.php @@ -99,12 +99,23 @@ class FreshRSS_Auth { } /** - * Returns if current user is connected. + * Returns if current user has access to the given scope. * - * @return boolean true if user is connected, false else. + * @param string $scope general (default) or admin + * @return boolean true if user has corresponding access, false else. */ - public static function hasAccess() { - return self::$login_ok; + public static function hasAccess($scope = 'general') { + $ok = self::$login_ok; + switch ($scope) { + case 'general': + break; + case 'admin': + $ok &= Minz_Session::param('currentUser') === Minz_Configuration::defaultUser(); + break; + default: + $ok = false; + } + return $ok; } /** diff --git a/app/layout/aside_configure.phtml b/app/layout/aside_configure.phtml index e17bcb254..59846a7c8 100644 --- a/app/layout/aside_configure.phtml +++ b/app/layout/aside_configure.phtml @@ -22,10 +22,7 @@
    • - +
    • diff --git a/app/layout/header.phtml b/app/layout/header.phtml index fadfd13d7..12c86d61d 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -64,10 +64,7 @@ if (Minz_Configuration::canLogIn()) {
    • - +
    • diff --git a/app/views/configure/archiving.phtml b/app/views/configure/archiving.phtml index a883571aa..adbfdb77e 100644 --- a/app/views/configure/archiving.phtml +++ b/app/views/configure/archiving.phtml @@ -67,7 +67,7 @@ - +

      diff --git a/app/views/users/index.phtml b/app/views/users/index.phtml index 95659f727..f1cdf01a3 100644 --- a/app/views/users/index.phtml +++ b/app/views/users/index.phtml @@ -11,7 +11,7 @@
      @@ -44,7 +44,7 @@ conf->mail_login; ?>
      - placeholder="alice@example.net" /> + placeholder="alice@example.net" />
      @@ -56,7 +56,7 @@
      - + diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index 4e9da58b4..554bc8c96 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -100,9 +100,6 @@ class Minz_Configuration { public static function defaultUser () { return self::$default_user; } - public static function isAdmin($currentUser) { - return $currentUser === self::$default_user; - } public static function allowAnonymous() { return self::$allow_anonymous; } -- cgit v1.2.3 From 7080a32650ab8b19e917d8add944a75cc98381bc Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Mon, 20 Oct 2014 11:54:31 +0200 Subject: Add checking installation feature --- app/Controllers/updateController.php | 14 +++++- app/Models/DatabaseDAO.php | 83 ++++++++++++++++++++++++++++++++++++ app/Models/DatabaseDAOSQLite.php | 48 +++++++++++++++++++++ app/Models/Factory.php | 9 ++++ app/SQL/install.sql.mysql.php | 2 - app/SQL/install.sql.sqlite.php | 2 - app/layout/aside_configure.phtml | 7 ++- app/layout/header.phtml | 1 + app/views/update/checkInstall.phtml | 30 +++++++++++++ lib/lib_rss.php | 62 +++++++++++++++++++++++++++ 10 files changed, 252 insertions(+), 6 deletions(-) create mode 100644 app/Models/DatabaseDAO.php create mode 100644 app/Models/DatabaseDAOSQLite.php create mode 100644 app/views/update/checkInstall.phtml (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 9d1e1ddf5..4ebb11f51 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -12,7 +12,6 @@ class FreshRSS_update_Controller extends Minz_ActionController { invalidateHttpCache(); - Minz_View::prependTitle(_t('update_system') . ' · '); $this->view->update_to_apply = false; $this->view->last_update_time = 'unknown'; $this->view->check_last_hour = false; @@ -24,6 +23,8 @@ class FreshRSS_update_Controller extends Minz_ActionController { } public function indexAction() { + Minz_View::prependTitle(_t('update_system') . ' · '); + if (file_exists(UPDATE_FILENAME) && !is_writable(FRESHRSS_PATH)) { $this->view->message = array( 'status' => 'bad', @@ -126,4 +127,15 @@ class FreshRSS_update_Controller extends Minz_ActionController { } } } + + /** + * This action displays information about installation. + */ + public function checkInstallAction() { + Minz_View::prependTitle(_t('gen.title.check_install') . ' · '); + + $this->view->status_php = check_install_php(); + $this->view->status_files = check_install_files(); + $this->view->status_database = check_install_database(); + } } diff --git a/app/Models/DatabaseDAO.php b/app/Models/DatabaseDAO.php new file mode 100644 index 000000000..0d85718e3 --- /dev/null +++ b/app/Models/DatabaseDAO.php @@ -0,0 +1,83 @@ +bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + + $tables = array( + $this->prefix . 'category' => false, + $this->prefix . 'feed' => false, + $this->prefix . 'entry' => false, + ); + foreach ($res as $value) { + $tables[array_pop($value)] = true; + } + + return count(array_keys($tables, true, true)) == count($tables); + } + + public function getSchema($table) { + $sql = 'DESC ' . $this->prefix . $table; + $stm = $this->bd->prepare($sql); + $stm->execute(); + + return $this->listDaoToSchema($stm->fetchAll(PDO::FETCH_ASSOC)); + } + + public function checkTable($table, $schema) { + $columns = $this->getSchema($table); + + $ok = (count($columns) == count($schema)); + foreach ($columns as $c) { + $ok &= in_array($c['name'], $schema); + } + + return $ok; + } + + public function categoryIsCorrect() { + return $this->checkTable('category', array( + 'id', 'name' + )); + } + + public function feedIsCorrect() { + return $this->checkTable('feed', array( + 'id', 'url', 'category', 'name', 'website', 'description', 'lastUpdate', + 'priority', 'pathEntries', 'httpAuth', 'error', 'keep_history', 'ttl', + 'cache_nbEntries', 'cache_nbUnreads' + )); + } + + public function entryIsCorrect() { + return $this->checkTable('entry', array( + 'id', 'guid', 'title', 'author', 'content_bin', 'link', 'date', 'is_read', + 'is_favorite', 'id_feed', 'tags' + )); + } + + public function daoToSchema($dao) { + return array( + 'name' => $dao['Field'], + 'type' => strtolower($dao['Type']), + 'notnull' => (bool)$dao['Null'], + 'default' => $dao['Default'], + ); + } + + public function listDaoToSchema($listDAO) { + $list = array(); + + foreach ($listDAO as $dao) { + $list[] = $this->daoToSchema($dao); + } + + return $list; + } +} diff --git a/app/Models/DatabaseDAOSQLite.php b/app/Models/DatabaseDAOSQLite.php new file mode 100644 index 000000000..7f53f967d --- /dev/null +++ b/app/Models/DatabaseDAOSQLite.php @@ -0,0 +1,48 @@ +bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + + $tables = array( + 'category' => false, + 'feed' => false, + 'entry' => false, + ); + foreach ($res as $value) { + $tables[$value['name']] = true; + } + + return count(array_keys($tables, true, true)) == count($tables); + } + + public function getSchema($table) { + $sql = 'PRAGMA table_info(' . $table . ')'; + $stm = $this->bd->prepare($sql); + $stm->execute(); + + return $this->listDaoToSchema($stm->fetchAll(PDO::FETCH_ASSOC)); + } + + public function entryIsCorrect() { + return $this->checkTable('entry', array( + 'id', 'guid', 'title', 'author', 'content', 'link', 'date', 'is_read', + 'is_favorite', 'id_feed', 'tags' + )); + } + + public function daoToSchema($dao) { + return array( + 'name' => $dao['name'], + 'type' => strtolower($dao['type']), + 'notnull' => $dao['notnull'] === '1' ? true : false, + 'default' => $dao['dflt_value'], + ); + } +} diff --git a/app/Models/Factory.php b/app/Models/Factory.php index 93f4552f7..91cb84998 100644 --- a/app/Models/Factory.php +++ b/app/Models/Factory.php @@ -29,4 +29,13 @@ class FreshRSS_Factory { } } + public static function createDatabaseDAO($username = null) { + $db = Minz_Configuration::dataBase(); + if ($db['type'] === 'sqlite') { + return new FreshRSS_DatabaseDAOSQLite($username); + } else { + return new FreshRSS_DatabaseDAO($username); + } + } + } diff --git a/app/SQL/install.sql.mysql.php b/app/SQL/install.sql.mysql.php index 16cb3a3b8..cf0159199 100644 --- a/app/SQL/install.sql.mysql.php +++ b/app/SQL/install.sql.mysql.php @@ -57,5 +57,3 @@ INSERT IGNORE INTO `%1$scategory` (id, name) VALUES(1, "%2$s"); '); define('SQL_DROP_TABLES', 'DROP TABLES %1$sentry, %1$sfeed, %1$scategory'); - -define('SQL_SHOW_TABLES', 'SHOW tables;'); diff --git a/app/SQL/install.sql.sqlite.php b/app/SQL/install.sql.sqlite.php index 7988ada04..30bca2810 100644 --- a/app/SQL/install.sql.sqlite.php +++ b/app/SQL/install.sql.sqlite.php @@ -55,5 +55,3 @@ $SQL_CREATE_TABLES = array( ); define('SQL_DROP_TABLES', 'DROP TABLES %1$sentry, %1$sfeed, %1$scategory'); - -define('SQL_SHOW_TABLES', 'SELECT name FROM sqlite_master WHERE type="table"'); diff --git a/app/layout/aside_configure.phtml b/app/layout/aside_configure.phtml index 20446c877..32dc19a4e 100644 --- a/app/layout/aside_configure.phtml +++ b/app/layout/aside_configure.phtml @@ -31,7 +31,12 @@
    • -
    • +
    • + +
    • +
    • diff --git a/app/layout/header.phtml b/app/layout/header.phtml index e848ac4eb..506cec175 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -68,6 +68,7 @@ if (Minz_Configuration::canLogIn()) {
    • +
    • diff --git a/app/views/update/checkInstall.phtml b/app/views/update/checkInstall.phtml new file mode 100644 index 000000000..32058714e --- /dev/null +++ b/app/views/update/checkInstall.phtml @@ -0,0 +1,30 @@ +partial('aside_configure'); ?> + +
      + + +

      + + status_php as $key => $status) { ?> +

      + +

      + + +

      + + status_files as $key => $status) { ?> +

      + +

      + + +

      + + status_database as $key => $status) { ?> +

      + +

      + + +
      diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 2f9a2ea45..dbed207d0 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -245,3 +245,65 @@ function is_referer_from_same_domain() { } return (isset($host['port']) ? $host['port'] : 0) === (isset($referer['port']) ? $referer['port'] : 0); } + + +/** + * + */ +function check_install_php() { + return array( + 'php' => version_compare(PHP_VERSION, '5.2.1') >= 0, + 'minz' => file_exists(LIB_PATH . '/Minz'), + 'curl' => extension_loaded('curl'), + 'pdo_mysql' => extension_loaded('pdo_mysql'), + 'pdo_sqlite' => extension_loaded('pdo_sqlite'), + 'pdo' => extension_loaded('pdo_mysql') || extension_loaded('pdo_sqlite'), + 'pcre' => extension_loaded('pcre'), + 'ctype' => extension_loaded('ctype'), + 'dom' => class_exists('DOMDocument'), + 'json' => extension_loaded('json'), + 'zip' => extension_loaded('zip'), + ); +} + + +/** + * + */ +function check_install_files() { + return array( + 'data' => DATA_PATH && is_writable(DATA_PATH), + 'cache' => CACHE_PATH && is_writable(CACHE_PATH), + 'logs' => LOG_PATH && is_writable(LOG_PATH), + 'favicons' => is_writable(DATA_PATH . '/favicons'), + 'persona' => is_writable(DATA_PATH . '/persona'), + 'tokens' => is_writable(DATA_PATH . '/tokens'), + ); +} + + +/** + * + */ +function check_install_database() { + $status = array( + 'connection' => true, + 'tables' => false, + 'categories' => false, + 'feeds' => false, + 'entries' => false, + ); + + try { + $dbDAO = FreshRSS_Factory::createDatabaseDAO(); + + $status['tables'] = $dbDAO->tablesAreCorrect(); + $status['categories'] = $dbDAO->categoryIsCorrect(); + $status['feeds'] = $dbDAO->feedIsCorrect(); + $status['entries'] = $dbDAO->entryIsCorrect(); + } catch(Minz_PDOConnectionException $e) { + $status['connection'] = false; + } + + return $status; +} -- cgit v1.2.3 From 9478d2f0116be69e08071dd02c0f945c5f78d7e0 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 30 Oct 2014 12:43:52 +0100 Subject: Add do_post_update support --- app/Controllers/updateController.php | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 4ebb11f51..4ef5357ea 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -109,6 +109,19 @@ class FreshRSS_update_Controller extends Minz_ActionController { require(UPDATE_FILENAME); + if (Minz_Request::param('post_conf', false)) { + $res = do_post_update(); + + if ($res === true) { + @unlink(UPDATE_FILENAME); + @file_put_contents(DATA_PATH . '/last_update.txt', time()); + Minz_Request::good(_t('update_finished')); + } else { + Minz_Request::bad(_t('update_problem', $res), + array('c' => 'update', 'a' => 'index')); + } + } + if (Minz_Request::isPost()) { save_info_update(); } @@ -117,10 +130,11 @@ class FreshRSS_update_Controller extends Minz_ActionController { $res = apply_update(); if ($res === true) { - @unlink(UPDATE_FILENAME); - @file_put_contents(DATA_PATH . '/last_update.txt', time()); - - Minz_Request::good(_t('update_finished')); + Minz_Request::forward(array( + 'c' => 'update', + 'a' => 'apply', + 'params' => array('post_conf' => true) + ), true); } else { Minz_Request::bad(_t('update_problem', $res), array('c' => 'update', 'a' => 'index')); -- cgit v1.2.3 From 58deab37cdd97e93ac25aba574a32befe1db2243 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 30 Oct 2014 19:57:08 +0100 Subject: Fix Minz_Error::error() -> use default values --- app/Controllers/authController.php | 3 +-- app/Controllers/categoryController.php | 5 +---- app/Controllers/configureController.php | 5 +---- app/Controllers/entryController.php | 5 +---- app/Controllers/feedController.php | 10 ++-------- app/Controllers/importExportController.php | 5 +---- app/Controllers/statsController.php | 29 +++++++++++++---------------- app/Controllers/subscriptionController.php | 10 ++-------- app/Controllers/updateController.php | 5 +---- app/Controllers/userController.php | 8 ++------ 10 files changed, 25 insertions(+), 60 deletions(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/authController.php b/app/Controllers/authController.php index 491be8d8a..44496cd3e 100644 --- a/app/Controllers/authController.php +++ b/app/Controllers/authController.php @@ -19,8 +19,7 @@ class FreshRSS_auth_Controller extends Minz_ActionController { */ public function indexAction() { if (!FreshRSS_Auth::hasAccess('admin')) { - Minz_Error::error(403, - array('error' => array(_t('access_denied')))); + Minz_Error::error(403); } Minz_View::prependTitle(_t('gen.title.authentication') . ' · '); diff --git a/app/Controllers/categoryController.php b/app/Controllers/categoryController.php index 609284559..50b1d841a 100644 --- a/app/Controllers/categoryController.php +++ b/app/Controllers/categoryController.php @@ -13,10 +13,7 @@ class FreshRSS_category_Controller extends Minz_ActionController { */ public function firstAction() { if (!FreshRSS_Auth::hasAccess()) { - Minz_Error::error( - 403, - array('error' => array(_t('access_denied'))) - ); + Minz_Error::error(403); } $catDAO = new FreshRSS_CategoryDAO(); diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index deb8cc849..1c8ac9111 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -11,10 +11,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { */ public function firstAction() { if (!FreshRSS_Auth::hasAccess()) { - Minz_Error::error( - 403, - array('error' => array(_t('access_denied'))) - ); + Minz_Error::error(403); } } diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index d11f3a520..b4beed619 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -11,10 +11,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { */ public function firstAction() { if (!FreshRSS_Auth::hasAccess()) { - Minz_Error::error( - 403, - array('error' => array(_t('access_denied'))) - ); + Minz_Error::error(403); } // If ajax request, we do not print layout diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 8563b1c0f..9990a852c 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -20,10 +20,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $action = Minz_Request::actionName(); if ($action !== 'actualize' || !(Minz_Configuration::allowAnonymousRefresh() || $token_is_ok)) { - Minz_Error::error( - 403, - array('error' => array(_t('access_denied'))) - ); + Minz_Error::error(403); } } } @@ -442,10 +439,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } else { Minz_Log::warning('Cannot move feed `' . $feed_id . '` ' . 'in the category `' . $cat_id . '`'); - Minz_Error::error( - 404, - array('error' => array(_t('error_occurred'))) - ); + Minz_Error::error(404); } } diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 8028af8ed..4e2dbd157 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -11,10 +11,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { */ public function firstAction() { if (!FreshRSS_Auth::hasAccess()) { - Minz_Error::error( - 403, - array('error' => array(_t('access_denied'))) - ); + Minz_Error::error(403); } require_once(LIB_PATH . '/lib_opml.php'); diff --git a/app/Controllers/statsController.php b/app/Controllers/statsController.php index 0e3430fcc..18fbca6df 100644 --- a/app/Controllers/statsController.php +++ b/app/Controllers/statsController.php @@ -5,6 +5,19 @@ */ class FreshRSS_stats_Controller extends Minz_ActionController { + /** + * This action is called before every other action in that class. It is + * the common boiler plate for every action. It is triggered by the + * underlying framework. + */ + public function firstAction() { + if (!FreshRSS_Auth::hasAccess()) { + Minz_Error::error(403); + } + + Minz_View::prependTitle(_t('stats') . ' · '); + } + /** * This action handles the statistic main page. * @@ -111,20 +124,4 @@ class FreshRSS_stats_Controller extends Minz_ActionController { $this->view->repartitionMonth = $statsDAO->calculateEntryRepartitionPerFeedPerMonth($id); $this->view->averageMonth = $statsDAO->calculateEntryAveragePerFeedPerMonth($id); } - - /** - * This action is called before every other action in that class. It is - * the common boiler plate for every action. It is triggered by the - * underlying framework. - */ - public function firstAction() { - if (!FreshRSS_Auth::hasAccess()) { - Minz_Error::error( - 403, array('error' => array(_t('access_denied'))) - ); - } - - Minz_View::prependTitle(_t('stats') . ' · '); - } - } diff --git a/app/Controllers/subscriptionController.php b/app/Controllers/subscriptionController.php index a89168eb3..67b95eba6 100644 --- a/app/Controllers/subscriptionController.php +++ b/app/Controllers/subscriptionController.php @@ -11,10 +11,7 @@ class FreshRSS_subscription_Controller extends Minz_ActionController { */ public function firstAction() { if (!FreshRSS_Auth::hasAccess()) { - Minz_Error::error( - 403, - array('error' => array(_t('access_denied'))) - ); + Minz_Error::error(403); } $catDAO = new FreshRSS_CategoryDAO(); @@ -71,10 +68,7 @@ class FreshRSS_subscription_Controller extends Minz_ActionController { $id = Minz_Request::param('id'); if ($id === false || !isset($this->view->feeds[$id])) { - Minz_Error::error( - 404, - array('error' => array(_t('page_not_found'))) - ); + Minz_Error::error(404); return; } diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 4ef5357ea..0896b13ac 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -4,10 +4,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { public function firstAction() { $current_user = Minz_Session::param('currentUser', ''); if (!FreshRSS_Auth::hasAccess('admin')) { - Minz_Error::error( - 403, - array('error' => array(_t('access_denied'))) - ); + Minz_Error::error(403); } invalidateHttpCache(); diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index 39db1d879..5050571a9 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -15,10 +15,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { */ public function firstAction() { if (!FreshRSS_Auth::hasAccess()) { - Minz_Error::error( - 403, - array('error' => array(_t('access_denied'))) - ); + Minz_Error::error(403); } } @@ -88,8 +85,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { */ public function manageAction() { if (!FreshRSS_Auth::hasAccess('admin')) { - Minz_Error::error(403, - array('error' => array(_t('access_denied')))); + Minz_Error::error(403); } Minz_View::prependTitle(_t('gen.title.user_management') . ' · '); -- cgit v1.2.3 From 59fc73baa9cbd87fdc2d63e83533a36533c740bf Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 11 Dec 2014 19:32:59 +0100 Subject: Fix i18n for update and user controllers --- app/Controllers/updateController.php | 30 +++++++++++++++--------------- app/Controllers/userController.php | 12 ++++++------ app/i18n/en/admin.php | 5 +++++ app/i18n/en/conf.php | 3 +++ app/i18n/en/feedback.php | 21 ++++++++++++++++++++- app/i18n/en/gen.php | 15 --------------- app/i18n/fr/admin.php | 7 ++++++- app/i18n/fr/conf.php | 3 +++ app/i18n/fr/feedback.php | 21 ++++++++++++++++++++- app/i18n/fr/gen.php | 14 -------------- 10 files changed, 78 insertions(+), 53 deletions(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 0896b13ac..1b44a739c 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -20,21 +20,21 @@ class FreshRSS_update_Controller extends Minz_ActionController { } public function indexAction() { - Minz_View::prependTitle(_t('update_system') . ' · '); + Minz_View::prependTitle(_t('admin.update.title') . ' · '); if (file_exists(UPDATE_FILENAME) && !is_writable(FRESHRSS_PATH)) { $this->view->message = array( 'status' => 'bad', - 'title' => _t('damn'), - 'body' => _t('file_is_nok', FRESHRSS_PATH) + 'title' => _t('gen.short.damn'), + 'body' => _t('feedback.update.file_is_nok', FRESHRSS_PATH) ); } elseif (file_exists(UPDATE_FILENAME)) { // There is an update file to apply! $this->view->update_to_apply = true; $this->view->message = array( 'status' => 'good', - 'title' => _t('ok'), - 'body' => _t('update_can_apply') + 'title' => _t('gen.short.ok'), + 'body' => _t('feedback.update.can_apply') ); } } @@ -67,8 +67,8 @@ class FreshRSS_update_Controller extends Minz_ActionController { $this->view->message = array( 'status' => 'bad', - 'title' => _t('damn'), - 'body' => _t('update_server_not_found', FRESHRSS_UPDATE_WEBSITE) + 'title' => _t('gen.short.damn'), + 'body' => _t('feedback.update.server_not_found', FRESHRSS_UPDATE_WEBSITE) ); return; } @@ -78,8 +78,8 @@ class FreshRSS_update_Controller extends Minz_ActionController { if (strpos($status, 'UPDATE') !== 0) { $this->view->message = array( 'status' => 'bad', - 'title' => _t('damn'), - 'body' => _t('no_update') + 'title' => _t('gen.short.damn'), + 'body' => _t('feedback.update.none') ); @file_put_contents(DATA_PATH . '/last_update.txt', time()); @@ -93,8 +93,8 @@ class FreshRSS_update_Controller extends Minz_ActionController { } else { $this->view->message = array( 'status' => 'bad', - 'title' => _t('damn'), - 'body' => _t('update_problem', 'Cannot save the update script') + 'title' => _t('gen.short.damn'), + 'body' => _t('feedback.update.error', 'Cannot save the update script') ); } } @@ -112,9 +112,9 @@ class FreshRSS_update_Controller extends Minz_ActionController { if ($res === true) { @unlink(UPDATE_FILENAME); @file_put_contents(DATA_PATH . '/last_update.txt', time()); - Minz_Request::good(_t('update_finished')); + Minz_Request::good(_t('feedback.update.finished')); } else { - Minz_Request::bad(_t('update_problem', $res), + Minz_Request::bad(_t('feedback.update.error', $res), array('c' => 'update', 'a' => 'index')); } } @@ -133,7 +133,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { 'params' => array('post_conf' => true) ), true); } else { - Minz_Request::bad(_t('update_problem', $res), + Minz_Request::bad(_t('feedback.update.error', $res), array('c' => 'update', 'a' => 'index')); } } @@ -143,7 +143,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { * This action displays information about installation. */ public function checkInstallAction() { - Minz_View::prependTitle(_t('gen.title.check_install') . ' · '); + Minz_View::prependTitle(_t('admin.check_install.title') . ' · '); $this->view->status_php = check_install_php(); $this->view->status_files = check_install_files(); diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index 5050571a9..3b40e42dc 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -23,7 +23,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { * This action displays the user profile page. */ public function profileAction() { - Minz_View::prependTitle(_t('gen.title.user_profile') . ' · '); + Minz_View::prependTitle(_t('conf.profile.title') . ' · '); if (Minz_Request::isPost()) { $ok = true; @@ -71,10 +71,10 @@ class FreshRSS_user_Controller extends Minz_ActionController { } if ($ok) { - Minz_Request::good(_t('feedback.user_profile.updated'), + Minz_Request::good(_t('feedback.profile.updated'), array('c' => 'user', 'a' => 'profile')); } else { - Minz_Request::bad(_t('error_occurred'), + Minz_Request::bad(_t('feedback.profile.error'), array('c' => 'user', 'a' => 'profile')); } } @@ -88,7 +88,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { Minz_Error::error(403); } - Minz_View::prependTitle(_t('gen.title.user_management') . ' · '); + Minz_View::prependTitle(_t('admin.user.title') . ' · '); // Get the correct current user. $username = Minz_Request::param('u', Minz_Session::param('currentUser')); @@ -168,7 +168,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { $notif = array( 'type' => $ok ? 'good' : 'bad', - 'content' => _t($ok ? 'user_created' : 'error_occurred', $new_user_name) + 'content' => _t('feedback.user.created' . (!$ok ? '.error' : ''), $new_user_name) ); Minz_Session::_param('notification', $notif); } @@ -201,7 +201,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { $notif = array( 'type' => $ok ? 'good' : 'bad', - 'content' => _t($ok ? 'user_deleted' : 'error_occurred', $username) + 'content' => _t('feedback.user.deleted' . (!$ok ? '.error' : ''), $username) ); Minz_Session::_param('notification', $notif); } diff --git a/app/i18n/en/admin.php b/app/i18n/en/admin.php index 05077cf66..7778c56a7 100644 --- a/app/i18n/en/admin.php +++ b/app/i18n/en/admin.php @@ -81,6 +81,7 @@ return array( 'nok' => 'There is one or more lacking tables in the database.', 'ok' => 'Tables are existing in the database.', ), + 'title' => 'Installation checking', 'tokens' => array( 'nok' => 'Check permissions on ./data/tokens directory. HTTP server must have rights to write into', 'ok' => 'Permissions on tokens directory are good.', @@ -122,7 +123,11 @@ return array( 'title' => 'Statistics', 'top_feed' => 'Top ten feeds', ), + 'update' => array( + 'title' => 'Update system', + ), 'users' => array( 'articles_and_size' => '%s articles (%s)', + 'title' => 'Manage users', ), ); diff --git a/app/i18n/en/conf.php b/app/i18n/en/conf.php index 0a1aa9f8e..09f9fa9ce 100644 --- a/app/i18n/en/conf.php +++ b/app/i18n/en/conf.php @@ -11,6 +11,9 @@ return array( 'number' => 'Query n°%d', 'title' => 'User queries', ), + 'profile' => array( + 'title' => 'Profile', + ), 'reading' => array( 'title' => 'Reading', ), diff --git a/app/i18n/en/feedback.php b/app/i18n/en/feedback.php index 0dcbbe44d..192a33fcf 100644 --- a/app/i18n/en/feedback.php +++ b/app/i18n/en/feedback.php @@ -70,7 +70,26 @@ return array( ), 'purge_completed' => 'Purge completed (%d articles deleted)', ), - 'user_profile' => array( + 'update' => array( + 'can_apply' => 'An update is available.', + 'error' => 'The update process has encountered an error: %s', + 'file_is_nok' => 'Check permissions on %s directory. HTTP server must have rights to write into', + 'finished' => 'Update completed!', + 'none' => 'No update to apply', + 'server_not_found' => 'Update server cannot be found. [%s]', + ), + 'user' => array( + 'created' => array( + '_' => 'User %s has been created', + 'error' => 'User %s cannot be created', + ), + 'deleted' => array( + '_' => 'User %s has been deleted', + 'error' => 'User %s cannot be deleted', + ), + ), + 'profile' => array( + 'error' => 'Your profile cannot be modified', 'updated' => 'Your profile has been modified', ), ); diff --git a/app/i18n/en/gen.php b/app/i18n/en/gen.php index 2a858d6a6..761d566bf 100644 --- a/app/i18n/en/gen.php +++ b/app/i18n/en/gen.php @@ -123,12 +123,6 @@ return array( 'or' => 'or', 'yes' => 'Yes', ), - 'title' => array( - 'authentication' => 'Authentication', - 'check_install' => 'Installation checking', - 'user_management' => 'Manage users', - 'user_profile' => 'Profile', - ), 'freshrss' => 'FreshRSS', 'activate_sharing' => 'Activate sharing', 'after_onread' => 'After “mark all as read”,', @@ -203,7 +197,6 @@ return array( 'favicons_is_ok' => 'Permissions on favicons directory are good', 'feed' => 'Feed', 'feeds' => 'Feeds', - 'file_is_nok' => 'Check permissions on %s directory. HTTP server must have rights to write into', 'finish_installation' => 'Complete installation', 'first_article' => 'Skip to the first article', 'fix_errors_before' => 'Fix errors before skip to the next step.', @@ -250,7 +243,6 @@ return array( 'no_query_filter' => 'No filter', 'no_rss_feed' => 'No RSS feed', 'no_selected_feed' => 'No feed selected.', - 'no_update' => 'No update to apply', 'not_read' => '%d unread', 'not_reads' => '%d unread', 'not_yet_implemented' => 'Not yet implemented', @@ -323,20 +315,13 @@ return array( 'top_line' => 'Top line', 'unsafe_autologin' => 'Allow unsafe automatic login using the format: ', 'update_apply' => 'Apply', - 'update_can_apply' => 'An update is available.', 'update_check' => 'Check for new updates', 'update_end' => 'Update process is completed, now you can go to the final step.', - 'update_finished' => 'Update completed!', 'update_last' => 'Last verification: %s', 'update_long' => 'This can take a long time, depending on the size of your database. You may have to wait for this page to time out (~5 minutes) and then refresh this page.', - 'update_problem' => 'The update process has encountered an error: %s', - 'update_server_not_found' => 'Update server cannot be found. [%s]', 'update_start' => 'Start update process', - 'update_system' => 'Update system', 'updated' => 'Modifications have been updated', 'upon_reception' => 'upon reception of the article', - 'user_created' => 'User %s has been created', - 'user_deleted' => 'User %s has been deleted', 'user_filter' => 'Access user filters', 'user_filter_help' => 'If there is only one user filter, it is used. Else filters are accessible by their number.', 'username' => 'Username', diff --git a/app/i18n/fr/admin.php b/app/i18n/fr/admin.php index 25c62c6ea..ffb390876 100644 --- a/app/i18n/fr/admin.php +++ b/app/i18n/fr/admin.php @@ -81,6 +81,7 @@ return array( 'nok' => 'Il manque une ou plusieurs tables en base de données.', 'ok' => 'Les tables sont bien présentes en base de données.', ), + 'title' => 'Vérification de l’installation', 'tokens' => array( 'nok' => 'Veuillez vérifier les droits sur le répertoire ./data/tokens. Le serveur HTTP doit être capable d’écrire dedans', 'ok' => 'Les droits sur le répertoire des tokens sont bons.', @@ -122,7 +123,11 @@ return array( 'title' => 'Statistiques', 'top_feed' => 'Les dix plus gros flux', ), - 'users' => array( + 'update' => array( + 'title' => 'Système de mise à jour', + ), + 'user' => array( 'articles_and_size' => '%s articles (%s)', + 'title' => 'Gestion des utilisateurs', ), ); diff --git a/app/i18n/fr/conf.php b/app/i18n/fr/conf.php index deba4509e..9f78fde87 100644 --- a/app/i18n/fr/conf.php +++ b/app/i18n/fr/conf.php @@ -11,6 +11,9 @@ return array( 'number' => 'Filtre n°%d', 'title' => 'Filtres utilisateurs', ), + 'profile' => array( + 'title' => 'Profil', + ), 'reading' => array( 'title' => 'Lecture', ), diff --git a/app/i18n/fr/feedback.php b/app/i18n/fr/feedback.php index 539fce55d..992300c7d 100644 --- a/app/i18n/fr/feedback.php +++ b/app/i18n/fr/feedback.php @@ -70,7 +70,26 @@ return array( ), 'purge_completed' => 'Purge effectuée (%d articles supprimés).', ), - 'user_profile' => array( + 'update' => array( + 'can_apply' => 'Une mise à jour est disponible.', + 'error' => 'La mise à jour a rencontré un problème : %s', + 'file_is_nok' => 'Veuillez vérifier les droits sur le répertoire %s. Le serveur HTTP doit être capable d’écrire dedans', + 'finished' => 'La mise à jour est terminée !', + 'none' => 'Aucune mise à jour à appliquer', + 'server_not_found' => 'Le serveur de mise à jour n’a pas été trouvé. [%s]', + ), + 'user' => array( + 'created' => array( + '_' => 'L’utilisateur %s a été créé.', + 'error' => 'L’utilisateur %s ne peut pas être créé.', + ), + 'deleted' => array( + '_' => 'L’utilisateur %s a été supprimé.', + 'error' => 'L’utilisateur %s ne peut pas être supprimé.', + ), + ), + 'profile' => array( + 'error' => 'Votre profil n’a pas pu être mis à jour', 'updated' => 'Votre profil a été mis à jour', ), ); diff --git a/app/i18n/fr/gen.php b/app/i18n/fr/gen.php index f67ee3d52..1866f8e22 100644 --- a/app/i18n/fr/gen.php +++ b/app/i18n/fr/gen.php @@ -123,11 +123,6 @@ return array( 'or' => 'ou', 'yes' => 'Oui', ), - 'title' => array( - 'check_install' => 'Vérification de l’installation', - 'user_management' => 'Gestion des utilisateurs', - 'user_profile' => 'Profil', - ), 'freshrss' => 'FreshRSS', 'activate_sharing' => 'Activer le partage', 'after_onread' => 'Après “marquer tout comme lu”,', @@ -202,7 +197,6 @@ return array( 'favicons_is_ok' => 'Les droits sur le répertoire des favicons sont bons', 'feed' => 'Flux', 'feeds' => 'Flux', - 'file_is_nok' => 'Veuillez vérifier les droits sur le répertoire %s. Le serveur HTTP doit être capable d’écrire dedans', 'finish_installation' => 'Terminer l’installation', 'first_article' => 'Passer au premier article', 'fix_errors_before' => 'Veuillez corriger les erreurs avant de passer à l’étape suivante.', @@ -249,7 +243,6 @@ return array( 'no_query_filter' => 'Aucun filtre appliqué', 'no_rss_feed' => 'Aucun flux RSS', 'no_selected_feed' => 'Aucun flux sélectionné.', - 'no_update' => 'Aucune mise à jour à appliquer', 'not_read' => '%d non lu', 'not_reads' => '%d non lus', 'not_yet_implemented' => 'Pas encore implémenté', @@ -322,20 +315,13 @@ return array( 'top_line' => 'Ligne du haut', 'unsafe_autologin' => 'Autoriser les connexions automatiques non-sûres au format : ', 'update_apply' => 'Appliquer la mise à jour', - 'update_can_apply' => 'Une mise à jour est disponible.', 'update_check' => 'Vérifier les mises à jour', 'update_end' => 'La mise à jour est terminée, vous pouvez maintenant passer à l’étape finale.', - 'update_finished' => 'La mise à jour est terminée !', 'update_last' => 'Dernière vérification : %s', 'update_long' => 'Ce processus peut prendre longtemps, selon la taille de votre base de données. Vous aurez peut-être à attendre que cette page dépasse son temps maximum d’exécution (~5 minutes) puis à la recharger.', - 'update_problem' => 'La mise à jour a rencontré un problème : %s', - 'update_server_not_found' => 'Le serveur de mise à jour n’a pas été trouvé. [%s]', 'update_start' => 'Lancer la mise à jour', - 'update_system' => 'Système de mise à jour', 'updated' => 'Modifications enregistrées.', 'upon_reception' => 'dès la réception du nouvel article', - 'user_created' => 'L’utilisateur %s a été créé.', - 'user_deleted' => 'L’utilisateur %s a été supprimé.', 'user_filter' => 'Accéder aux filtres utilisateur', 'user_filter_help' => 'S’il n’y a qu’un filtre utilisateur, celui-ci est utilisé automatiquement. Sinon ils sont accessibles par leur numéro.', 'username' => 'Nom d’utilisateur', -- cgit v1.2.3 From e7c24b5d41c293fa13e4d92efb2aee7778ddcab4 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 13 Jan 2015 12:02:35 +0100 Subject: Show the version number during update process. Number is stored inside the data/last_update.txt file and shown if there is an update script. See https://github.com/FreshRSS/FreshRSS/issues/699 --- app/Controllers/updateController.php | 17 +++++++++++------ app/i18n/en/feedback.php | 2 +- app/i18n/fr/feedback.php | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 1b44a739c..4bdd96f6d 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -1,8 +1,8 @@ view->update_to_apply = false; $this->view->last_update_time = 'unknown'; $this->view->check_last_hour = false; - $timestamp = (int)@file_get_contents(DATA_PATH . '/last_update.txt'); - if (is_numeric($timestamp) && $timestamp > 0) { + $timestamp = @filemtime(join_path(DATA_PATH, 'last_update.txt')); + if ($timestamp !== false) { $this->view->last_update_time = timestamptodate($timestamp); $this->view->check_last_hour = (time() - 3600) <= $timestamp; } @@ -30,11 +30,12 @@ class FreshRSS_update_Controller extends Minz_ActionController { ); } elseif (file_exists(UPDATE_FILENAME)) { // There is an update file to apply! + $version = file_get_contents(join_path(DATA_PATH, 'last_update.txt')); $this->view->update_to_apply = true; $this->view->message = array( 'status' => 'good', 'title' => _t('gen.short.ok'), - 'body' => _t('feedback.update.can_apply') + 'body' => _t('feedback.update.can_apply', $version) ); } } @@ -82,13 +83,17 @@ class FreshRSS_update_Controller extends Minz_ActionController { 'body' => _t('feedback.update.none') ); - @file_put_contents(DATA_PATH . '/last_update.txt', time()); + @touch(join_path(DATA_PATH, 'last_update.txt')); return; } $script = $res_array[1]; if (file_put_contents(UPDATE_FILENAME, $script) !== false) { + $version = explode(' ', $status, 2); + $version = $version[1]; + @file_put_contents(join_path(DATA_PATH, 'last_update.txt'), $version); + Minz_Request::forward(array('c' => 'update')); } else { $this->view->message = array( @@ -111,7 +116,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { if ($res === true) { @unlink(UPDATE_FILENAME); - @file_put_contents(DATA_PATH . '/last_update.txt', time()); + @file_put_contents(join_path(DATA_PATH, 'last_update.txt'), ''); Minz_Request::good(_t('feedback.update.finished')); } else { Minz_Request::bad(_t('feedback.update.error', $res), diff --git a/app/i18n/en/feedback.php b/app/i18n/en/feedback.php index 5f7183da3..e9499ed46 100644 --- a/app/i18n/en/feedback.php +++ b/app/i18n/en/feedback.php @@ -80,7 +80,7 @@ return array( 'purge_completed' => 'Purge completed (%d articles deleted)', ), 'update' => array( - 'can_apply' => 'An update is available.', + 'can_apply' => 'FreshRSS will be now updated to the version %s.', 'error' => 'The update process has encountered an error: %s', 'file_is_nok' => 'Check permissions on %s directory. HTTP server must have rights to write into', 'finished' => 'Update completed!', diff --git a/app/i18n/fr/feedback.php b/app/i18n/fr/feedback.php index 5c71bbae1..c99314411 100644 --- a/app/i18n/fr/feedback.php +++ b/app/i18n/fr/feedback.php @@ -80,7 +80,7 @@ return array( 'purge_completed' => 'Purge effectuée (%d articles supprimés).', ), 'update' => array( - 'can_apply' => 'Une mise à jour est disponible.', + 'can_apply' => 'FreshRSS va maintenant être mis à jour vers la version %s.', 'error' => 'La mise à jour a rencontré un problème : %s', 'file_is_nok' => 'Veuillez vérifier les droits sur le répertoire %s. Le serveur HTTP doit être capable d’écrire dedans', 'finished' => 'La mise à jour est terminée !', -- cgit v1.2.3 From 556f4ad4bfa722e3350f20ab91a08f0af1d11f9e Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 15 Jan 2015 11:20:16 +0100 Subject: Remove restriction of 1h for update checking --- app/Controllers/updateController.php | 8 +++----- app/views/update/index.phtml | 8 +------- 2 files changed, 4 insertions(+), 12 deletions(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 4bdd96f6d..1ea5816da 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -11,11 +11,9 @@ class FreshRSS_update_Controller extends Minz_ActionController { $this->view->update_to_apply = false; $this->view->last_update_time = 'unknown'; - $this->view->check_last_hour = false; $timestamp = @filemtime(join_path(DATA_PATH, 'last_update.txt')); if ($timestamp !== false) { $this->view->last_update_time = timestamptodate($timestamp); - $this->view->check_last_hour = (time() - 3600) <= $timestamp; } } @@ -43,11 +41,11 @@ class FreshRSS_update_Controller extends Minz_ActionController { public function checkAction() { $this->view->change_view('update', 'index'); - if (file_exists(UPDATE_FILENAME) || $this->view->check_last_hour) { + if (file_exists(UPDATE_FILENAME)) { // There is already an update file to apply: we don't need to check // the webserver! // Or if already check during the last hour, do nothing. - Minz_Request::forward(array('c' => 'update')); + Minz_Request::forward(array('c' => 'update'), true); return; } @@ -94,7 +92,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { $version = $version[1]; @file_put_contents(join_path(DATA_PATH, 'last_update.txt'), $version); - Minz_Request::forward(array('c' => 'update')); + Minz_Request::forward(array('c' => 'update'), true); } else { $this->view->message = array( 'status' => 'bad', diff --git a/app/views/update/index.phtml b/app/views/update/index.phtml index 4c88a0d18..da1bc7ef5 100644 --- a/app/views/update/index.phtml +++ b/app/views/update/index.phtml @@ -18,16 +18,10 @@ message['title']; ?> message['body']; ?>

      - check_last_hour) { ?> -

      - - -

      check_last_hour && - (empty($this->message) || $this->message['status'] !== 'good')) { + if (empty($this->message) || $this->message['status'] !== 'good') { ?>

      -- cgit v1.2.3 From 13cf8b5f9f22a50bba5d1223174407abb1c1d94c Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 15 Jan 2015 12:02:42 +0100 Subject: Improve hook calls and add post_update hook - To the hook is associated a method signature (OneToOne or NoneToNone for now) so it is easier to call hooks correctly - post_update hook is called during the post update moment. --- app/Controllers/updateController.php | 2 + lib/Minz/ExtensionManager.php | 74 +++++++++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 13 deletions(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 1ea5816da..61b62773b 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -112,6 +112,8 @@ class FreshRSS_update_Controller extends Minz_ActionController { if (Minz_Request::param('post_conf', false)) { $res = do_post_update(); + Minz_ExtensionManager::callHook('post_update'); + if ($res === true) { @unlink(UPDATE_FILENAME); @file_put_contents(join_path(DATA_PATH, 'last_update.txt'), ''); diff --git a/lib/Minz/ExtensionManager.php b/lib/Minz/ExtensionManager.php index 8369e242a..7edc7afaa 100644 --- a/lib/Minz/ExtensionManager.php +++ b/lib/Minz/ExtensionManager.php @@ -15,9 +15,22 @@ class Minz_ExtensionManager { // List of available hooks. Please keep this list sorted. private static $hook_list = array( - 'entry_before_display' => array(), // function($entry) -> Entry | null - 'entry_before_insert' => array(), // function($entry) -> Entry | null - 'feed_before_insert' => array(), // function($feed) -> Feed | null + 'entry_before_display' => array( // function($entry) -> Entry | null + 'list' => array(), + 'signature' => 'OneToOne', + ), + 'entry_before_insert' => array( // function($entry) -> Entry | null + 'list' => array(), + 'signature' => 'OneToOne', + ), + 'feed_before_insert' => array( // function($feed) -> Feed | null + 'list' => array(), + 'signature' => 'OneToOne', + ), + 'post_update' => array( // function(none) -> none + 'list' => array(), + 'signature' => 'NoneToNone', + ), ); private static $ext_to_hooks = array(); @@ -214,7 +227,7 @@ class Minz_ExtensionManager { */ public static function addHook($hook_name, $hook_function, $ext) { if (isset(self::$hook_list[$hook_name]) && is_callable($hook_function)) { - self::$hook_list[$hook_name][] = $hook_function; + self::$hook_list[$hook_name]['list'][] = $hook_function; self::$ext_to_hooks[$ext->getName()][] = $hook_name; } } @@ -226,25 +239,60 @@ class Minz_ExtensionManager { * array keys. * * @param $hook_name the hook to call. - * @param additionnal parameters (for signature, please see self::$hook_list comments) - * @todo hook functions will have different signatures. So the $res = func($args); - * $args = $res; will not work for all of them in the future. We must - * find a better way to call hooks. + * @param additionnal parameters (for signature, please see self::$hook_list). + * @return the final result of the called hook. */ public static function callHook($hook_name) { + if (!isset(self::$hook_list[$hook_name])) { + return; + } + + $signature = self::$hook_list[$hook_name]['signature']; + $signature = 'self::call' . $signature; $args = func_get_args(); - unset($args[0]); - $result = $args[1]; - foreach (self::$hook_list[$hook_name] as $function) { - $result = call_user_func_array($function, $args); + return call_user_func_array($signature, $args); + } + + /** + * Call a hook which takes one argument and return a result. + * + * The result is chained between the extension, for instance, first extension + * hook will receive the initial argument and return a result which will be + * passed as an argument to the next extension hook and so on. + * + * If a hook return a null value, the method is stopped and return null. + * + * @param $hook_name is the hook to call. + * @param $arg is the argument to pass to the first extension hook. + * @return the final chained result of the hooks. If nothing is changed, + * the initial argument is returned. + */ + private static function callOneToOne($hook_name, $arg) { + $result = $arg; + foreach (self::$hook_list[$hook_name]['list'] as $function) { + $result = call_user_func($function, $arg); if (is_null($result)) { break; } - $args = $result; + $arg = $result; } return $result; } + + /** + * Call a hook which takes no argument and returns nothing. + * + * This case is simpler than callOneToOne because hooks are called one by + * one, without any consideration of argument nor result. + * + * @param $hook_name is the hook to call. + */ + private static function callNoneToNone($hook_name) { + foreach (self::$hook_list[$hook_name]['list'] as $function) { + call_user_func($function); + } + } } -- cgit v1.2.3 From d30b3becfa50b96982a3b4880c07cc2b770d7eed Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 19 Jan 2015 13:54:57 +0100 Subject: Addressed warnings when reading from new files There were warnings when reading extensions (trying to use e.g. README and .gitignore as directories), and when reading update file. https://github.com/FreshRSS/FreshRSS/issues/733 --- app/Controllers/updateController.php | 5 ++++- lib/Minz/ExtensionManager.php | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 61b62773b..4797a3486 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -28,7 +28,10 @@ class FreshRSS_update_Controller extends Minz_ActionController { ); } elseif (file_exists(UPDATE_FILENAME)) { // There is an update file to apply! - $version = file_get_contents(join_path(DATA_PATH, 'last_update.txt')); + $version = @file_get_contents(join_path(DATA_PATH, 'last_update.txt')); + if (empty($version)) { + $version = 'unknown'; + } $this->view->update_to_apply = true; $this->view->message = array( 'status' => 'good', diff --git a/lib/Minz/ExtensionManager.php b/lib/Minz/ExtensionManager.php index 7edc7afaa..f00453f6c 100644 --- a/lib/Minz/ExtensionManager.php +++ b/lib/Minz/ExtensionManager.php @@ -56,6 +56,9 @@ class Minz_ExtensionManager { foreach ($list_potential_extensions as $ext_dir) { $ext_pathname = EXTENSIONS_PATH . '/' . $ext_dir; + if (!is_dir($ext_pathname)) { + continue; + } $metadata_filename = $ext_pathname . '/' . self::$ext_metaname; // Try to load metadata file. -- cgit v1.2.3 From 079150eee4eebce3549c3d7db84dd0180bdd11e7 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 3 Jul 2015 23:47:18 +0200 Subject: Updated log visibility In particular, ensure that ERROR is only used for errors that may affect FreshRSS integrity, and ensure that feed errors are visible also in production, i.e. visibility of WARNING https://github.com/FreshRSS/FreshRSS/issues/885 https://github.com/FreshRSS/FreshRSS/issues/884 --- app/Controllers/authController.php | 2 +- app/Controllers/feedController.php | 2 +- app/Controllers/importExportController.php | 6 +++--- app/Controllers/updateController.php | 2 +- app/Models/CategoryDAO.php | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/authController.php b/app/Controllers/authController.php index 937c0759d..b55892475 100644 --- a/app/Controllers/authController.php +++ b/app/Controllers/authController.php @@ -253,7 +253,7 @@ class FreshRSS_auth_Controller extends Minz_ActionController { FreshRSS_Auth::giveAccess(); invalidateHttpCache(); } else { - Minz_Log::error($reason); + Minz_Log::warning($reason); $res = array(); $res['status'] = 'failure'; diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index b91f63b5b..488d066a9 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -322,7 +322,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feed->load(false); } } catch (FreshRSS_Feed_Exception $e) { - Minz_Log::notice($e->getMessage()); + Minz_Log::warning($e->getMessage()); $feedDAO->updateLastUpdate($feed->id(), true); $feed->unlock(); continue; diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 26b163e43..60e467255 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -47,7 +47,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $status_file = $file['error']; if ($status_file !== 0) { - Minz_Log::error('File cannot be uploaded. Error code: ' . $status_file); + Minz_Log::warning('File cannot be uploaded. Error code: ' . $status_file); Minz_Request::bad(_t('feedback.import_export.file_cannot_be_uploaded'), array('c' => 'importExport', 'a' => 'index')); } @@ -69,7 +69,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { if (!is_resource($zip)) { // zip_open cannot open file: something is wrong - Minz_Log::error('Zip archive cannot be imported. Error code: ' . $zip); + Minz_Log::warning('Zip archive cannot be imported. Error code: ' . $zip); Minz_Request::bad(_t('feedback.import_export.zip_error'), array('c' => 'importExport', 'a' => 'index')); } @@ -77,7 +77,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { 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); + Minz_Log::warning('Zip file cannot be imported. Error code: ' . $zipfile); } else { $type_zipfile = $this->guessFileType(zip_entry_name($zipfile)); if ($type_file !== 'unknown') { diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 4797a3486..84a33fe85 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -63,7 +63,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { curl_close($c); if ($c_status !== 200) { - Minz_Log::error( + Minz_Log::warning( 'Error during update (HTTP code ' . $c_status . '): ' . $c_error ); diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index 189a5f0e4..b5abac519 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -13,7 +13,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable return $this->bd->lastInsertId(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::error('SQL error addCategory: ' . $info[2] ); + Minz_Log::error('SQL error addCategory: ' . $info[2]); return false; } } -- cgit v1.2.3 From bb0556543d3d5d54832ea6d81372587b88062a5b Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Mon, 2 Nov 2015 20:20:40 +0100 Subject: Move auto-update server URL in configuration Fix https://github.com/FreshRSS/FreshRSS/issues/1019 --- app/Controllers/updateController.php | 5 +++-- constants.php | 1 - data/config.default.php | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'app/Controllers/updateController.php') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 84a33fe85..64c984b04 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -53,7 +53,8 @@ class FreshRSS_update_Controller extends Minz_ActionController { return; } - $c = curl_init(FRESHRSS_UPDATE_WEBSITE); + $auto_update_url = FreshRSS_Context::$system_conf->auto_update_url . '?v=' . FRESHRSS_VERSION; + $c = curl_init($auto_update_url); curl_setopt($c, CURLOPT_RETURNTRANSFER, true); curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2); @@ -70,7 +71,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { $this->view->message = array( 'status' => 'bad', 'title' => _t('gen.short.damn'), - 'body' => _t('feedback.update.server_not_found', FRESHRSS_UPDATE_WEBSITE) + 'body' => _t('feedback.update.server_not_found', $auto_update_url) ); return; } diff --git a/constants.php b/constants.php index 0035c259b..1c50d4a83 100644 --- a/constants.php +++ b/constants.php @@ -1,7 +1,6 @@ '', + # Specify address of the FreshRSS auto-update server. + 'auto_update_url' => 'https://update.freshrss.org', + # Natural language of the user interface, e.g. `en`, `fr`. 'language' => 'en', -- cgit v1.2.3 From 21e63595083d556bd4a6ff56c4ccaab5cd318dd4 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 5 Nov 2016 15:08:10 +0100 Subject: Add compatibility with git in Web update https://github.com/FreshRSS/FreshRSS/issues/907 https://github.com/FreshRSS/FreshRSS/issues/1355 --- CHANGELOG.md | 3 + README.fr.md | 4 +- README.md | 2 +- app/Controllers/updateController.php | 159 ++++++++++++++++++++++++----------- 4 files changed, 118 insertions(+), 50 deletions(-) (limited to 'app/Controllers/updateController.php') diff --git a/CHANGELOG.md b/CHANGELOG.md index 77ec29aed..140c8cfaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## 2016-xx-xx FreshRSS 1.7.0-dev +* Features + * Add git compatibility in Web update system [#1357](https://github.com/FreshRSS/FreshRSS/issues/1357) + * Requires that the initial installation is done with git ## 2016-11-02 FreshRSS 1.6.1 diff --git a/README.fr.md b/README.fr.md index 2587c2d8f..afa8fa1ce 100644 --- a/README.fr.md +++ b/README.fr.md @@ -23,7 +23,7 @@ Voir la [liste des versions](../../releases). * Pour ceux qui veulent bien aider à tester ou déveloper les dernières fonctionnalités, [la branche dev](https://github.com/FreshRSS/FreshRSS/tree/dev) vous ouvre les bras ! # Avertissements -Cette application a été développée pour s’adapter principalement à des besoins personnels, et aucune garantie n'est fournie. +Cette application a été développée pour s’adapter principalement à des besoins personnels, et aucune garantie n’est fournie. Les demandes de fonctionnalités, rapports de bugs, et autres contributions sont les bienvenues. Privilégiez pour cela des [demandes sur GitHub](https://github.com/FreshRSS/FreshRSS/issues). Nous sommes une communauté amicale. @@ -85,7 +85,7 @@ sudo git checkout -b dev origin/dev # Mettre les droits d’accès pour le serveur Web sudo chown -R :www-data . && sudo chmod -R g+r . && sudo chmod -R g+w ./data/ -# Si vous souhaitez permettre les mises à jour par l'interface Web (inutile pour les mises à jour par git) +# Si vous souhaitez permettre les mises à jour par l’interface Web sudo chmod -R g+w . # Publier FreshRSS dans votre répertoire HTML public diff --git a/README.md b/README.md index cfb7b6769..ebf1e3c5f 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ sudo git checkout -b dev origin/dev # Set the rights so that your Web server can access the files sudo chown -R :www-data . && sudo chmod -R g+r . && sudo chmod -R g+w ./data/ -# If you would like to allow Web updates (not needed for updates with git) +# If you would like to allow updates from the Web interface sudo chmod -R g+w . # Publish FreshRSS in your public HTML directory diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 64c984b04..8f939dbdb 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -2,6 +2,45 @@ class FreshRSS_update_Controller extends Minz_ActionController { + public static function isGit() { + return is_dir(FRESHRSS_PATH . '/.git/'); + } + + public static function hasGitUpdate() { + $cwd = getcwd(); + chdir(FRESHRSS_PATH); + $output = array(); + try { + exec('git fetch', $output, $return); + if ($return == 0) { + exec('git status -sb --porcelain remote', $output, $return); + } else { + $line = is_array($output) ? implode('; ', $output) : '' . $output; + Minz_Log::warning('git fetch warning:' . $line); + } + } catch (Exception $e) { + Minz_Log::warning('git fetch error:' . $e->getMessage()); + } + chdir($cwd); + $line = is_array($output) ? implode('; ', $output) : '' . $output; + return strpos($line, '[behind') !== false; + } + + public static function gitPull() { + $cwd = getcwd(); + chdir(FRESHRSS_PATH); + $output = array(); + $return = 1; + try { + exec('git pull --ff-only', $output, $return); + } catch (Exception $e) { + Minz_Log::warning('git pull error:' . $e->getMessage()); + } + chdir($cwd); + $line = is_array($output) ? implode('; ', $output) : '' . $output; + return $return == 0 ? true : 'Git error: ' . $line; + } + public function firstAction() { if (!FreshRSS_Auth::hasAccess('admin')) { Minz_Error::error(403); @@ -20,7 +59,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { public function indexAction() { Minz_View::prependTitle(_t('admin.update.title') . ' · '); - if (file_exists(UPDATE_FILENAME) && !is_writable(FRESHRSS_PATH)) { + if (!is_writable(FRESHRSS_PATH)) { $this->view->message = array( 'status' => 'bad', 'title' => _t('gen.short.damn'), @@ -53,49 +92,65 @@ class FreshRSS_update_Controller extends Minz_ActionController { return; } - $auto_update_url = FreshRSS_Context::$system_conf->auto_update_url . '?v=' . FRESHRSS_VERSION; - $c = curl_init($auto_update_url); - curl_setopt($c, CURLOPT_RETURNTRANSFER, true); - curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true); - curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2); - $result = curl_exec($c); - $c_status = curl_getinfo($c, CURLINFO_HTTP_CODE); - $c_error = curl_error($c); - curl_close($c); - - if ($c_status !== 200) { - Minz_Log::warning( - 'Error during update (HTTP code ' . $c_status . '): ' . $c_error - ); + $script = ''; + $version = ''; - $this->view->message = array( - 'status' => 'bad', - 'title' => _t('gen.short.damn'), - 'body' => _t('feedback.update.server_not_found', $auto_update_url) - ); - return; - } - - $res_array = explode("\n", $result, 2); - $status = $res_array[0]; - if (strpos($status, 'UPDATE') !== 0) { - $this->view->message = array( - 'status' => 'bad', - 'title' => _t('gen.short.damn'), - 'body' => _t('feedback.update.none') - ); + if (self::isGit()) { + if (self::hasGitUpdate()) { + $version = 'git'; + } else { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('gen.short.damn'), + 'body' => _t('feedback.update.none') + ); + @touch(join_path(DATA_PATH, 'last_update.txt')); + return; + } + } else { + $auto_update_url = FreshRSS_Context::$system_conf->auto_update_url . '?v=' . FRESHRSS_VERSION; + Minz_Log::debug('HTTP GET ' . $auto_update_url); + $c = curl_init($auto_update_url); + curl_setopt($c, CURLOPT_RETURNTRANSFER, true); + curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2); + $result = curl_exec($c); + $c_status = curl_getinfo($c, CURLINFO_HTTP_CODE); + $c_error = curl_error($c); + curl_close($c); + + if ($c_status !== 200) { + Minz_Log::warning( + 'Error during update (HTTP code ' . $c_status . '): ' . $c_error + ); + + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('gen.short.damn'), + 'body' => _t('feedback.update.server_not_found', $auto_update_url) + ); + return; + } - @touch(join_path(DATA_PATH, 'last_update.txt')); + $res_array = explode("\n", $result, 2); + $status = $res_array[0]; + if (strpos($status, 'UPDATE') !== 0) { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('gen.short.damn'), + 'body' => _t('feedback.update.none') + ); + @touch(join_path(DATA_PATH, 'last_update.txt')); + return; + } - return; + $script = $res_array[1]; + $version = explode(' ', $status, 2); + $version = $version[1]; } - $script = $res_array[1]; if (file_put_contents(UPDATE_FILENAME, $script) !== false) { - $version = explode(' ', $status, 2); - $version = $version[1]; @file_put_contents(join_path(DATA_PATH, 'last_update.txt'), $version); - Minz_Request::forward(array('c' => 'update'), true); } else { $this->view->message = array( @@ -111,10 +166,13 @@ class FreshRSS_update_Controller extends Minz_ActionController { Minz_Request::forward(array('c' => 'update'), true); } - require(UPDATE_FILENAME); - if (Minz_Request::param('post_conf', false)) { - $res = do_post_update(); + if (self::isGit()) { + $res = !self::hasGitUpdate(); + } else { + require(UPDATE_FILENAME); + $res = do_post_update(); + } Minz_ExtensionManager::callHook('post_update'); @@ -126,14 +184,21 @@ class FreshRSS_update_Controller extends Minz_ActionController { Minz_Request::bad(_t('feedback.update.error', $res), array('c' => 'update', 'a' => 'index')); } - } - - if (Minz_Request::isPost()) { - save_info_update(); - } + } else { + $res = false; - if (!need_info_update()) { - $res = apply_update(); + if (self::isGit()) { + $res = self::gitPull(); + } else { + if (Minz_Request::isPost()) { + save_info_update(); + } + if (!need_info_update()) { + $res = apply_update(); + } else { + return; + } + } if ($res === true) { Minz_Request::forward(array( -- cgit v1.2.3