diff options
| author | 2014-09-26 14:50:33 +0200 | |
|---|---|---|
| committer | 2014-09-26 14:50:33 +0200 | |
| commit | f0fb1fbb07347d3a2fd7b853bff1f91807cd2d89 (patch) | |
| tree | 4566beb83704d5b2bbcb128749da042cdaa4e619 /app | |
| parent | 94ad9cf073962d9ff8076ef8e0db35e513d565a7 (diff) | |
| parent | c14162221365077bcaeecde7127806190490dd58 (diff) | |
Merge branch 'dev' into beta
Diffstat (limited to 'app')
43 files changed, 1576 insertions, 946 deletions
diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index bb96bfae3..231865bd7 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -1,11 +1,22 @@ <?php +/** + * Controller to handle every configuration options. + */ class FreshRSS_configure_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. + * + * @todo see if the category default configuration is needed here or if + * we can move it to the categorize action + */ public function firstAction() { if (!$this->view->loginOk) { Minz_Error::error( 403, - array('error' => array(Minz_Translate::t('access_denied'))) + array('error' => array(_t('access_denied'))) ); } @@ -13,6 +24,18 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $catDAO->checkDefault(); } + /** + * This action handles the category configuration page + * + * It displays the category configuration page. + * If this action is reached through a POST request, it loops through + * every category to check for modification then add a new category if + * needed then sends a notification to the user. + * If a category name is emptied, the category is deleted and all + * related feeds are moved to the default category. Related user queries + * are deleted too. + * If a category name is changed, it is updated. + */ public function categorizeAction() { $feedDAO = FreshRSS_Factory::createFeedDao(); $catDAO = new FreshRSS_CategoryDAO(); @@ -34,6 +57,10 @@ class FreshRSS_configure_Controller extends Minz_ActionController { } elseif ($ids[$key] != $defaultId) { $feedDAO->changeCategory($ids[$key], $defaultId); $catDAO->deleteCategory($ids[$key]); + + // Remove related queries. + $this->view->conf->remove_query_by_get('c_' . $ids[$key]); + $this->view->conf->save(); } } @@ -50,22 +77,37 @@ class FreshRSS_configure_Controller extends Minz_ActionController { } invalidateHttpCache(); - $notif = array( - 'type' => 'good', - 'content' => Minz_Translate::t('categories_updated') - ); - Minz_Session::_param('notification', $notif); - - Minz_Request::forward(array('c' => 'configure', 'a' => 'categorize'), true); + Minz_Request::good(_t('categories_updated'), + array('c' => 'configure', 'a' => 'categorize')); } $this->view->categories = $catDAO->listCategories(false); $this->view->defaultCategory = $catDAO->getDefault(); $this->view->feeds = $feedDAO->listFeeds(); - Minz_View::prependTitle(Minz_Translate::t('categories_management') . ' · '); + Minz_View::prependTitle(_t('categories_management') . ' · '); } + /** + * This action handles the feed configuration page. + * + * It displays the feed configuration page. + * If this action is reached through a POST request, it stores all new + * configuraiton values then sends a notification to the user. + * + * The options available on the page are: + * - name + * - description + * - website URL + * - feed URL + * - category id (default: default category id) + * - CSS path to article on website + * - display in main stream (default: 0) + * - HTTP authentication + * - number of article to retain (default: -2) + * - refresh frequency (default: -2) + * Default values are empty strings unless specified. + */ public function feedAction() { $catDAO = new FreshRSS_CategoryDAO(); $this->view->categories = $catDAO->listCategories(false); @@ -85,7 +127,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { if (!$this->view->flux) { Minz_Error::error( 404, - array('error' => array(Minz_Translate::t('page_not_found'))) + array('error' => array(_t('page_not_found'))) ); } else { if (Minz_Request::isPost() && $this->view->flux) { @@ -117,12 +159,12 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->flux->faviconPrepare(); $notif = array( 'type' => 'good', - 'content' => Minz_Translate::t('feed_updated') + 'content' => _t('feed_updated') ); } else { $notif = array( 'type' => 'bad', - 'content' => Minz_Translate::t('error_occurred_update') + 'content' => _t('error_occurred_update') ); } invalidateHttpCache(); @@ -131,21 +173,41 @@ class FreshRSS_configure_Controller extends Minz_ActionController { Minz_Request::forward(array('c' => 'configure', 'a' => 'feed', 'params' => array('id' => $id)), true); } - Minz_View::prependTitle(Minz_Translate::t('rss_feed_management') . ' — ' . $this->view->flux->name() . ' · '); + Minz_View::prependTitle(_t('rss_feed_management') . ' — ' . $this->view->flux->name() . ' · '); } } else { - Minz_View::prependTitle(Minz_Translate::t('rss_feed_management') . ' · '); + Minz_View::prependTitle(_t('rss_feed_management') . ' · '); } } + /** + * This action handles the display configuration page. + * + * It displays the display configuration page. + * If this action is reached through a POST request, it stores all new + * configuration values then sends a notification to the user. + * + * The options available on the page are: + * - language (default: en) + * - theme (default: Origin) + * - content width (default: thin) + * - display of read action in header + * - display of favorite action in header + * - display of date in header + * - display of open action in header + * - display of read action in footer + * - display of favorite action in footer + * - display of sharing action in footer + * - display of tags in footer + * - display of date in footer + * - display of open action in footer + * - html5 notification timeout (default: 0) + * Default values are false unless specified. + */ public function displayAction() { if (Minz_Request::isPost()) { $this->view->conf->_language(Minz_Request::param('language', 'en')); - $themeId = Minz_Request::param('theme', ''); - if ($themeId == '') { - $themeId = FreshRSS_Themes::defaultTheme; - } - $this->view->conf->_theme($themeId); + $this->view->conf->_theme(Minz_Request::param('theme', FreshRSS_Themes::$defaultTheme)); $this->view->conf->_content_width(Minz_Request::param('content_width', 'thin')); $this->view->conf->_topline_read(Minz_Request::param('topline_read', false)); $this->view->conf->_topline_favorite(Minz_Request::param('topline_favorite', false)); @@ -157,26 +219,49 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->conf->_bottomline_tags(Minz_Request::param('bottomline_tags', false)); $this->view->conf->_bottomline_date(Minz_Request::param('bottomline_date', false)); $this->view->conf->_bottomline_link(Minz_Request::param('bottomline_link', false)); + $this->view->conf->_html5_notif_timeout(Minz_Request::param('html5_notif_timeout', 0)); $this->view->conf->save(); Minz_Session::_param('language', $this->view->conf->language); Minz_Translate::reset(); invalidateHttpCache(); - $notif = array( - 'type' => 'good', - 'content' => Minz_Translate::t('configuration_updated') - ); - Minz_Session::_param('notification', $notif); - - Minz_Request::forward(array('c' => 'configure', 'a' => 'display'), true); + Minz_Request::good(_t('configuration_updated'), + array('c' => 'configure', 'a' => 'display')); } $this->view->themes = FreshRSS_Themes::get(); - Minz_View::prependTitle(Minz_Translate::t('display_configuration') . ' · '); + Minz_View::prependTitle(_t('display_configuration') . ' · '); } + /** + * This action handles the reading configuration page. + * + * It displays the reading configuration page. + * If this action is reached through a POST request, it stores all new + * configuration values then sends a notification to the user. + * + * The options available on the page are: + * - number of posts per page (default: 10) + * - view mode (default: normal) + * - default article view (default: all) + * - load automatically articles + * - display expanded articles + * - display expanded categories + * - hide categories and feeds without unread articles + * - jump on next category or feed when marked as read + * - image lazy loading + * - stick open articles to the top + * - display a confirmation when reading all articles + * - article order (default: DESC) + * - mark articles as read when: + * - displayed + * - opened on site + * - scrolled + * - received + * Default values are false unless specified. + */ public function readingAction() { if (Minz_Request::isPost()) { $this->view->conf->_posts_per_page(Minz_Request::param('posts_per_page', 10)); @@ -203,18 +288,20 @@ class FreshRSS_configure_Controller extends Minz_ActionController { Minz_Translate::reset(); invalidateHttpCache(); - $notif = array( - 'type' => 'good', - 'content' => Minz_Translate::t('configuration_updated') - ); - Minz_Session::_param('notification', $notif); - - Minz_Request::forward(array('c' => 'configure', 'a' => 'reading'), true); + Minz_Request::good(_t('configuration_updated'), + array('c' => 'configure', 'a' => 'reading')); } - Minz_View::prependTitle(Minz_Translate::t('reading_configuration') . ' · '); + Minz_View::prependTitle(_t('reading_configuration') . ' · '); } + /** + * This action handles the sharing configuration page. + * + * It displays the sharing configuration page. + * If this action is reached through a POST request, it stores all + * configuration values then sends a notification to the user. + */ public function sharingAction() { if (Minz_Request::isPost()) { $params = Minz_Request::params(); @@ -222,25 +309,31 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->conf->save(); invalidateHttpCache(); - $notif = array( - 'type' => 'good', - 'content' => Minz_Translate::t('configuration_updated') - ); - Minz_Session::_param('notification', $notif); - - Minz_Request::forward(array('c' => 'configure', 'a' => 'sharing'), true); + Minz_Request::good(_t('configuration_updated'), + array('c' => 'configure', 'a' => 'sharing')); } - Minz_View::prependTitle(Minz_Translate::t('sharing') . ' · '); + Minz_View::prependTitle(_t('sharing') . ' · '); } + /** + * This action handles the shortcut configuration page. + * + * It displays the shortcut configuration page. + * If this action is reached through a POST request, it stores all new + * configuration values then sends a notification to the user. + * + * The authorized values for shortcuts are letters (a to z), numbers (0 + * to 9), function keys (f1 to f12), backspace, delete, down, end, enter, + * escape, home, insert, left, page down, page up, return, right, space, + * tab and up. + */ public function shortcutAction() { $list_keys = array('a', 'b', 'backspace', 'c', 'd', 'delete', 'down', 'e', 'end', 'enter', 'escape', 'f', 'g', 'h', 'home', 'i', 'insert', 'j', 'k', 'l', 'left', 'm', 'n', 'o', 'p', 'page_down', 'page_up', 'q', 'r', 'return', 'right', 's', 'space', 't', 'tab', 'u', 'up', 'v', 'w', 'x', 'y', - 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', - '9', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', + 'z', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12'); $this->view->list_keys = $list_keys; @@ -258,44 +351,50 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->conf->save(); invalidateHttpCache(); - $notif = array( - 'type' => 'good', - 'content' => Minz_Translate::t('shortcuts_updated') - ); - Minz_Session::_param('notification', $notif); - - Minz_Request::forward(array('c' => 'configure', 'a' => 'shortcut'), true); + Minz_Request::good(_t('shortcuts_updated'), + array('c' => 'configure', 'a' => 'shortcut')); } - Minz_View::prependTitle(Minz_Translate::t('shortcuts') . ' · '); + Minz_View::prependTitle(_t('shortcuts') . ' · '); } + /** + * This action display the user configuration page + * + * @todo move that action in the user controller + */ public function usersAction() { - Minz_View::prependTitle(Minz_Translate::t('users') . ' · '); + Minz_View::prependTitle(_t('users') . ' · '); } + /** + * This action handles the archive configuration page. + * + * It displays the archive configuration page. + * If this action is reached through a POST request, it stores all new + * configuration values then sends a notification to the user. + * + * The options available on that page are: + * - duration to retain old article (default: 3) + * - number of article to retain per feed (default: 0) + * - refresh frequency (default: -2) + * + * @todo explain why the default value is -2 but this value does not + * exist in the drop-down list + */ public function archivingAction() { if (Minz_Request::isPost()) { - $old = Minz_Request::param('old_entries', 3); - $keepHistoryDefault = Minz_Request::param('keep_history_default', 0); - $ttlDefault = Minz_Request::param('ttl_default', -2); - - $this->view->conf->_old_entries($old); - $this->view->conf->_keep_history_default($keepHistoryDefault); - $this->view->conf->_ttl_default($ttlDefault); + $this->view->conf->_old_entries(Minz_Request::param('old_entries', 3)); + $this->view->conf->_keep_history_default(Minz_Request::param('keep_history_default', 0)); + $this->view->conf->_ttl_default(Minz_Request::param('ttl_default', -2)); $this->view->conf->save(); invalidateHttpCache(); - $notif = array( - 'type' => 'good', - 'content' => Minz_Translate::t('configuration_updated') - ); - Minz_Session::_param('notification', $notif); - - Minz_Request::forward(array('c' => 'configure', 'a' => 'archiving'), true); + Minz_Request::good(_t('configuration_updated'), + array('c' => 'configure', 'a' => 'archiving')); } - Minz_View::prependTitle(Minz_Translate::t('archiving_configuration') . ' · '); + Minz_View::prependTitle(_t('archiving_configuration') . ' · '); $entryDAO = FreshRSS_Factory::createEntryDao(); $this->view->nb_total = $entryDAO->count(); @@ -305,28 +404,35 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->size_total = $entryDAO->size(true); } } - + + /** + * This action handles the user queries configuration page. + * + * If this action is reached through a POST request, it stores all new + * configuration values then sends a notification to the user then + * redirect to the same page. + * If this action is not reached through a POST request, it displays the + * configuration page and verifies that every user query is runable by + * checking if categories and feeds are still in use. + */ public function queriesAction() { if (Minz_Request::isPost()) { $queries = Minz_Request::param('queries', array()); foreach ($queries as $key => $query) { if (!$query['name']) { - $query['name'] = Minz_Translate::t('query_number', $key + 1); + $query['name'] = _t('query_number', $key + 1); } } $this->view->conf->_queries($queries); $this->view->conf->save(); - $notif = array( - 'type' => 'good', - 'content' => Minz_Translate::t('configuration_updated') - ); - Minz_Session::_param('notification', $notif); - - Minz_Request::forward(array('c' => 'configure', 'a' => 'queries'), true); + Minz_Request::good(_t('configuration_updated'), + array('c' => 'configure', 'a' => 'queries')); } else { $this->view->query_get = array(); + $cat_dao = new FreshRSS_CategoryDAO(); + $feed_dao = FreshRSS_Factory::createFeedDao(); foreach ($this->view->conf->queries as $key => $query) { if (!isset($query['get'])) { continue; @@ -334,51 +440,83 @@ class FreshRSS_configure_Controller extends Minz_ActionController { switch ($query['get'][0]) { case 'c': - $dao = new FreshRSS_CategoryDAO(); - $category = $dao->searchById(substr($query['get'], 2)); + $category = $cat_dao->searchById(substr($query['get'], 2)); + + $deprecated = true; + $cat_name = ''; + if ($category) { + $cat_name = $category->name(); + $deprecated = false; + } + $this->view->query_get[$key] = array( 'type' => 'category', - 'name' => $category->name(), + 'name' => $cat_name, + 'deprecated' => $deprecated, ); break; case 'f': - $dao = FreshRSS_Factory::createFeedDao(); - $feed = $dao->searchById(substr($query['get'], 2)); + $feed = $feed_dao->searchById(substr($query['get'], 2)); + + $deprecated = true; + $feed_name = ''; + if ($feed) { + $feed_name = $feed->name(); + $deprecated = false; + } + $this->view->query_get[$key] = array( 'type' => 'feed', - 'name' => $feed->name(), + 'name' => $feed_name, + 'deprecated' => $deprecated, ); break; case 's': $this->view->query_get[$key] = array( 'type' => 'favorite', 'name' => 'favorite', + 'deprecated' => false, ); break; case 'a': $this->view->query_get[$key] = array( 'type' => 'all', 'name' => 'all', + 'deprecated' => false, ); break; } } } - Minz_View::prependTitle(Minz_Translate::t('queries') . ' · '); + Minz_View::prependTitle(_t('queries') . ' · '); } - + + /** + * This action handles the creation of a user query. + * + * It gets the GET parameters and stores them in the configuration query + * storage. Before it is saved, the unwanted parameters are unset to keep + * lean data. + */ public function addQueryAction() { + $whitelist = array('get', 'order', 'name', 'search', 'state'); $queries = $this->view->conf->queries; $query = Minz_Request::params(); - $query['name'] = Minz_Translate::t('query_number', count($queries) + 1); - unset($query['output']); - unset($query['token']); + $query['name'] = _t('query_number', count($queries) + 1); + foreach ($query as $key => $value) { + if (!in_array($key, $whitelist)) { + unset($query[$key]); + } + } + if (!empty($query['state']) && $query['state'] & FreshRSS_Entry::STATE_STRICT) { + $query['state'] -= FreshRSS_Entry::STATE_STRICT; + } $queries[] = $query; $this->view->conf->_queries($queries); $this->view->conf->save(); - // Minz_Request::forward(array('params' => $query), true); - Minz_Request::forward(array('c' => 'configure', 'a' => 'queries'), true); + Minz_Request::good(_t('query_created', $query['name']), + array('c' => 'configure', 'a' => 'queries')); } } diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index ac43587ea..ab66d9198 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -45,6 +45,10 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $entryDAO = FreshRSS_Factory::createEntryDao(); if ($id == false) { + if (!Minz_Request::isPost()) { + return; + } + if (!$get) { $entryDAO->markReadEntries ($idMax); } else { diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 3326b2059..c7cc25fbb 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -383,7 +383,16 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feedDAO = FreshRSS_Factory::createFeedDao(); if ($type == 'category') { + // List feeds to remove then related user queries. + $feeds = $feedDAO->listByCategory($id); + if ($feedDAO->deleteFeedByCategory ($id)) { + // Remove related queries + foreach ($feeds as $feed) { + $this->view->conf->remove_query_by_get('f_' . $feed->id()); + } + $this->view->conf->save(); + $notif = array ( 'type' => 'good', 'content' => Minz_Translate::t ('category_emptied') @@ -397,6 +406,10 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } else { if ($feedDAO->deleteFeed ($id)) { + // Remove related queries + $this->view->conf->remove_query_by_get('f_' . $id); + $this->view->conf->save(); + $notif = array ( 'type' => 'good', 'content' => Minz_Translate::t ('feed_deleted') @@ -412,10 +425,13 @@ class FreshRSS_feed_Controller extends Minz_ActionController { Minz_Session::_param ('notification', $notif); - if ($type == 'category') { - Minz_Request::forward (array ('c' => 'configure', 'a' => 'categorize'), true); + $redirect_url = Minz_Request::param('r', false, true); + if ($redirect_url) { + Minz_Request::forward($redirect_url); + } elseif ($type == 'category') { + Minz_Request::forward(array ('c' => 'configure', 'a' => 'categorize'), true); } else { - Minz_Request::forward (array ('c' => 'configure', 'a' => 'feed'), true); + Minz_Request::forward(array ('c' => 'configure', 'a' => 'feed'), true); } } } diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 5adf3878a..f329766b8 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -109,7 +109,6 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { // A *very* basic guess file type function. Only based on filename // That's could be improved but should be enough, at least for a first // implementation. - // TODO: improve this function? if (substr_compare($filename, '.zip', -4) === 0) { return 'zip'; @@ -119,8 +118,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } elseif (substr_compare($filename, '.json', -5) === 0 && strpos($filename, 'starred') !== false) { return 'json_starred'; - } elseif (substr_compare($filename, '.json', -5) === 0 && - strpos($filename, 'feed_') === 0) { + } elseif (substr_compare($filename, '.json', -5) === 0) { return 'json_feed'; } else { return 'unknown'; @@ -239,13 +237,27 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { ); $error = false; + $article_to_feed = array(); + + // First, we check feeds of articles are in DB (and add them if needed). foreach ($article_object['items'] as $item) { $feed = $this->addFeedArticles($item['origin'], $google_compliant); if (is_null($feed)) { $error = true; + } else { + $article_to_feed[$item['id']] = $feed->id(); + } + } + + // Then, articles are imported. + $prepared_statement = $this->entryDAO->addEntryPrepare(); + $this->entryDAO->beginTransaction(); + foreach ($article_object['items'] as $item) { + if (!isset($article_to_feed[$item['id']])) { continue; } + $feed_id = $article_to_feed[$item['id']]; $author = isset($item['author']) ? $item['author'] : ''; $key_content = ($google_compliant && !isset($item['content'])) ? 'summary' : 'content'; @@ -257,21 +269,21 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } $entry = new FreshRSS_Entry( - $feed->id(), $item['id'], $item['title'], $author, + $feed_id, $item['id'], $item['title'], $author, $item[$key_content]['content'], $item['alternate'][0]['href'], $item['published'], $is_read, $starred ); + $entry->_id(min(time(), $entry->date(true)) . uSecString()); $entry->_tags($tags); - //FIME: Use entryDAO->addEntryPrepare(). Do not call entryDAO->listLastGuidsByFeed() for each entry. Consider using a transaction. - $id = $this->entryDAO->addEntryObject( - $entry, $this->view->conf, $feed->keepHistory() - ); + $values = $entry->toArray(); + $id = $this->entryDAO->addEntry($values, $prepared_statement); if (!$error && ($id === false)) { $error = true; } } + $this->entryDAO->commit(); return $error; } diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index b0b051119..e8e26b142 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -83,6 +83,11 @@ class FreshRSS_index_Controller extends Minz_ActionController { $nb = Minz_Request::param ('nb', $this->view->conf->posts_per_page); $first = Minz_Request::param ('next', ''); + $ajax_request = Minz_Request::param('ajax', false); + if ($output === 'reader') { + $nb = max(1, round($nb / 2)); + } + if ($this->view->state === FreshRSS_Entry::STATE_NOT_READ) { //Any unread article in this category at all? switch ($getType) { case 'a': @@ -332,6 +337,10 @@ class FreshRSS_index_Controller extends Minz_ActionController { } public function formLoginAction () { + if ($this->view->loginOk) { + Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); + } + if (Minz_Request::isPost()) { $ok = false; $nonce = Minz_Session::param('nonce'); @@ -415,4 +424,75 @@ class FreshRSS_index_Controller extends Minz_ActionController { self::deleteLongTermCookie(); Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } + + public function resetAuthAction() { + Minz_View::prependTitle(_t('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(); + + if (Minz_Request::isPost()) { + $nonce = Minz_Session::param('nonce'); + $username = Minz_Request::param('username', ''); + $c = Minz_Request::param('challenge', ''); + if (!(ctype_alnum($username) && ctype_graph($c) && ctype_alnum($nonce))) { + Minz_Log::debug('Invalid credential parameters:' . + ' user=' . $username . + ' challenge=' . $c . + ' nonce=' . $nonce); + Minz_Request::bad(_t('invalid_login'), + array('c' => 'index', 'a' => 'resetAuth')); + } + + if (!function_exists('password_verify')) { + include_once(LIB_PATH . '/password_compat.php'); + } + + $s = $conf->passwordHash; + $ok = password_verify($nonce . $s, $c); + if ($ok) { + Minz_Configuration::_authType('form'); + $ok = Minz_Configuration::writeFile(); + + if ($ok) { + Minz_Request::good(_t('auth_form_set')); + } else { + Minz_Request::bad(_t('auth_form_not_set'), + array('c' => 'index', 'a' => 'resetAuth')); + } + } else { + Minz_Log::debug('Password mismatch for user ' . $username . + ', nonce=' . $nonce . ', c=' . $c); + + Minz_Request::bad(_t('invalid_login'), + array('c' => 'index', 'a' => 'resetAuth')); + } + } + } } diff --git a/app/Controllers/statsController.php b/app/Controllers/statsController.php index 98f46f0d2..256543f37 100644 --- a/app/Controllers/statsController.php +++ b/app/Controllers/statsController.php @@ -1,7 +1,21 @@ <?php +/** + * Controller to handle application statistics. + */ class FreshRSS_stats_Controller extends Minz_ActionController { + /** + * This action handles the statistic main page. + * + * It displays the statistic main page. + * The values computed to display the page are: + * - repartition of read/unread/favorite/not favorite + * - number of article per day + * - number of feed by category + * - number of article by category + * - list of most prolific feed + */ public function indexAction() { $statsDAO = FreshRSS_Factory::createStatsDAO(); Minz_View::appendScript(Minz_Url::display('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js'))); @@ -12,6 +26,17 @@ class FreshRSS_stats_Controller extends Minz_ActionController { $this->view->topFeed = $statsDAO->calculateTopFeed(); } + /** + * This action handles the idle feed statistic page. + * + * It displays the list of idle feed for different period. The supported + * periods are: + * - last year + * - last 6 months + * - last 3 months + * - last month + * - last week + */ public function idleAction() { $statsDAO = FreshRSS_Factory::createStatsDAO(); $feeds = $statsDAO->calculateFeedLastDate(); @@ -56,6 +81,18 @@ class FreshRSS_stats_Controller extends Minz_ActionController { $this->view->idleFeeds = $idleFeeds; } + /** + * This action handles the article repartition statistic page. + * + * It displays the number of article and the average of article for the + * following periods: + * - hour of the day + * - day of the week + * - month + * + * @todo verify that the metrics used here make some sense. Especially + * for the average. + */ public function repartitionAction() { $statsDAO = FreshRSS_Factory::createStatsDAO(); $categoryDAO = new FreshRSS_CategoryDAO(); @@ -67,10 +104,18 @@ class FreshRSS_stats_Controller extends Minz_ActionController { $this->view->days = $statsDAO->getDays(); $this->view->months = $statsDAO->getMonths(); $this->view->repartitionHour = $statsDAO->calculateEntryRepartitionPerFeedPerHour($id); + $this->view->averageHour = $statsDAO->calculateEntryAveragePerFeedPerHour($id); $this->view->repartitionDayOfWeek = $statsDAO->calculateEntryRepartitionPerFeedPerDayOfWeek($id); + $this->view->averageDayOfWeek = $statsDAO->calculateEntryAveragePerFeedPerDayOfWeek($id); $this->view->repartitionMonth = $statsDAO->calculateEntryRepartitionPerFeedPerMonth($id); + $this->view->averageMonth = $statsDAO->calculateEntryAveragePerFeedPerMonth($id); } + /** + * 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 (!$this->view->loginOk) { Minz_Error::error( diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php new file mode 100644 index 000000000..da5bddc65 --- /dev/null +++ b/app/Controllers/updateController.php @@ -0,0 +1,129 @@ +<?php + +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)) { + Minz_Error::error( + 403, + array('error' => array(_t('access_denied'))) + ); + } + + 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; + $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; + } + } + + public function indexAction() { + 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->update_to_apply = true; + $this->view->message = array( + 'status' => 'good', + 'title' => _t('ok'), + 'body' => _t('update_can_apply') + ); + } + } + + public function checkAction() { + $this->view->change_view('update', 'index'); + + 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; + } + + $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); + $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'), + 'body' => _t('update_server_not_found', FRESHRSS_UPDATE_WEBSITE) + ); + return; + } + + $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') + ); + + @file_put_contents(DATA_PATH . '/last_update.txt', time()); + + return; + } + + $script = $res_array[1]; + if (file_put_contents(UPDATE_FILENAME, $script) !== false) { + Minz_Request::forward(array('c' => 'update')); + } else { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('update_problem', 'Cannot save the update script') + ); + } + } + + public function applyAction() { + if (!file_exists(UPDATE_FILENAME) || !is_writable(FRESHRSS_PATH)) { + Minz_Request::forward(array('c' => 'update'), true); + } + + require(UPDATE_FILENAME); + + if (Minz_Request::isPost()) { + save_info_update(); + } + + if (!need_info_update()) { + $res = apply_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')); + } + } + } +} diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 30f711e20..cdf8962cb 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -6,8 +6,7 @@ class FreshRSS extends Minz_FrontController { } $loginOk = $this->accessControl(Minz_Session::param('currentUser', '')); $this->loadParamsView(); - if (Minz_Request::isPost() && (empty($_SERVER['HTTP_REFERER']) || - Minz_Request::getDomainName() !== parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST))) { + if (Minz_Request::isPost() && !is_referer_from_same_domain()) { $loginOk = false; //Basic protection against XSRF attacks Minz_Error::error( 403, @@ -140,11 +139,21 @@ class FreshRSS extends Minz_FrontController { } } - private function loadStylesAndScripts ($loginOk) { + private function loadStylesAndScripts($loginOk) { $theme = FreshRSS_Themes::load($this->conf->theme); if ($theme) { foreach($theme['files'] as $file) { - Minz_View::appendStyle (Minz_Url::display ('/themes/' . $theme['id'] . '/' . $file . '?' . @filemtime(PUBLIC_PATH . '/themes/' . $theme['id'] . '/' . $file))); + if ($file[0] === '_') { + $theme_id = 'base-theme'; + $filename = substr($file, 1); + } else { + $theme_id = $theme['id']; + $filename = $file; + } + $filetime = @filemtime(PUBLIC_PATH . '/themes/' . $theme_id . '/' . $filename); + Minz_View::appendStyle(Minz_Url::display( + '/themes/' . $theme_id . '/' . $filename . '?' . $filetime + )); } } diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index 4c804a9fb..95f819779 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -45,6 +45,8 @@ class FreshRSS_Configuration { 'load_more' => 'm', 'auto_share' => 's', 'focus_search' => 'a', + 'user_filter' => 'u', + 'help' => 'f1', ), 'topline_read' => true, 'topline_favorite' => true, @@ -58,6 +60,7 @@ class FreshRSS_Configuration { 'bottomline_link' => true, 'sharing' => array(), 'queries' => array(), + 'html5_notif_timeout' => 0, ); private $available_languages = array( @@ -120,6 +123,16 @@ class FreshRSS_Configuration { return $this->available_languages; } + public function remove_query_by_get($get) { + $final_queries = array(); + foreach ($this->queries as $key => $query) { + if (empty($query['get']) || $query['get'] !== $get) { + $final_queries[$key] = $query; + } + } + $this->_queries($final_queries); + } + public function _language($value) { if (!isset($this->available_languages[$value])) { $value = 'en'; @@ -138,7 +151,18 @@ class FreshRSS_Configuration { } } public function _default_view ($value) { - $this->data['default_view'] = $value === FreshRSS_Entry::STATE_ALL ? FreshRSS_Entry::STATE_ALL : FreshRSS_Entry::STATE_NOT_READ; + switch ($value) { + case FreshRSS_Entry::STATE_ALL: + // left blank on purpose + case FreshRSS_Entry::STATE_NOT_READ: + // left blank on purpose + case FreshRSS_Entry::STATE_STRICT + FreshRSS_Entry::STATE_NOT_READ: + $this->data['default_view'] = $value; + break; + default: + $this->data['default_view'] = FreshRSS_Entry::STATE_ALL; + break; + } } public function _display_posts ($value) { $this->data['display_posts'] = ((bool)$value) && $value !== 'no'; @@ -209,6 +233,7 @@ class FreshRSS_Configuration { } public function _sharing ($values) { $this->data['sharing'] = array(); + $unique = array(); foreach ($values as $value) { if (!is_array($value)) { continue; @@ -234,7 +259,11 @@ class FreshRSS_Configuration { $value['name'] = $value['type']; } - $this->data['sharing'][] = $value; + $json_value = json_encode($value); + if (!in_array($json_value, $unique)) { + $unique[] = $json_value; + $this->data['sharing'][] = $value; + } } } public function _queries ($values) { @@ -261,6 +290,12 @@ class FreshRSS_Configuration { $this->data['content_width'] = 'thin'; } } + + public function _html5_notif_timeout ($value) { + $value = intval($value); + $this->data['html5_notif_timeout'] = $value >= 0 ? $value : 0; + } + public function _token($value) { $this->data['token'] = $value; } diff --git a/app/Models/Entry.php b/app/Models/Entry.php index 0bf1f2616..9d7dd5dc4 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -6,6 +6,7 @@ class FreshRSS_Entry extends Minz_Model { const STATE_NOT_READ = 2; const STATE_FAVORITE = 4; const STATE_NOT_FAVORITE = 8; + const STATE_STRICT = 16; private $id = 0; private $guid; diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 75a8aeba4..c1f87ee34 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -333,6 +333,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { if ($state & FreshRSS_Entry::STATE_NOT_READ) { if (!($state & FreshRSS_Entry::STATE_READ)) { $where .= 'AND e1.is_read=0 '; + } elseif ($state & FreshRSS_Entry::STATE_STRICT) { + $where .= 'AND e1.is_read=0 '; } } elseif ($state & FreshRSS_Entry::STATE_READ) { diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 756b1f008..b89ae2045 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -331,7 +331,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $id_max = intval($date_min) . '000000'; $stm->bindParam(':id_feed', $id, PDO::PARAM_INT); - $stm->bindParam(':id_max', $id_max, PDO::PARAM_INT); + $stm->bindParam(':id_max', $id_max, PDO::PARAM_STR); $stm->bindParam(':keep', $keep, PDO::PARAM_INT); if ($stm && $stm->execute()) { diff --git a/app/Models/StatsDAO.php b/app/Models/StatsDAO.php index 89be76a26..40505ab3e 100644 --- a/app/Models/StatsDAO.php +++ b/app/Models/StatsDAO.php @@ -152,6 +152,74 @@ SQL; } /** + * Calculates the average number of article per hour per feed + * + * @param integer $feed id + * @return integer + */ + public function calculateEntryAveragePerFeedPerHour($feed = null) { + return $this->calculateEntryAveragePerFeedPerPeriod(1/24, $feed); + } + + /** + * Calculates the average number of article per day of week per feed + * + * @param integer $feed id + * @return integer + */ + public function calculateEntryAveragePerFeedPerDayOfWeek($feed = null) { + return $this->calculateEntryAveragePerFeedPerPeriod(7, $feed); + } + + /** + * Calculates the average number of article per month per feed + * + * @param integer $feed id + * @return integer + */ + public function calculateEntryAveragePerFeedPerMonth($feed = null) { + return $this->calculateEntryAveragePerFeedPerPeriod(30, $feed); + } + + /** + * Calculates the average number of article per feed + * + * @param float $period number used to divide the number of day in the period + * @param integer $feed id + * @return integer + */ + protected function calculateEntryAveragePerFeedPerPeriod($period, $feed = null) { + if ($feed) { + $restrict = "WHERE e.id_feed = {$feed}"; + } else { + $restrict = ''; + } + $sql = <<<SQL +SELECT COUNT(1) AS count +, MIN(date) AS date_min +, MAX(date) AS date_max +FROM {$this->prefix}entry AS e +{$restrict} +SQL; + $stm = $this->bd->prepare($sql); + $stm->execute(); + $res = $stm->fetch(PDO::FETCH_NAMED); + $date_min = new \DateTime(); + $date_min->setTimestamp($res['date_min']); + $date_max = new \DateTime(); + $date_max->setTimestamp($res['date_max']); + $interval = $date_max->diff($date_min, true); + $interval_in_days = $interval->format('%a'); + if ($interval_in_days <= 0) { + // Surely only one article. + // We will return count / (period/period) == count. + $interval_in_days = $period; + } + + return round($res['count'] / ($interval_in_days / $period), 2); + } + + /** * Initialize an array for statistics depending on a range * * @param integer $min @@ -247,6 +315,7 @@ SQL; SELECT MAX(f.id) as id , MAX(f.name) AS name , MAX(date) AS last_date +, COUNT(*) AS nb_articles FROM {$this->prefix}feed AS f, {$this->prefix}entry AS e WHERE f.id = e.id_feed diff --git a/app/Models/StatsDAOSQLite.php b/app/Models/StatsDAOSQLite.php index 6cb54ddf6..3b1256de1 100644 --- a/app/Models/StatsDAOSQLite.php +++ b/app/Models/StatsDAOSQLite.php @@ -53,6 +53,7 @@ SQL; $stm->execute(); $res = $stm->fetchAll(PDO::FETCH_NAMED); + $repartition = array(); foreach ($res as $value) { $repartition[(int) $value['period']] = (int) $value['count']; } diff --git a/app/Models/Themes.php b/app/Models/Themes.php index 538eb6554..68fc17a2b 100644 --- a/app/Models/Themes.php +++ b/app/Models/Themes.php @@ -96,6 +96,7 @@ class FreshRSS_Themes extends Minz_Model { 'search' => '🔍', 'share' => '♺', 'starred' => '★', + 'stats' => '%', 'tag' => '⚐', 'up' => '△', 'view-normal' => '☰', diff --git a/app/i18n/en.php b/app/i18n/en.php index be0cdc642..beba02c4d 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -5,6 +5,7 @@ return array ( 'login' => 'Login', 'keep_logged_in' => 'Keep me logged in <small>(1 month)</small>', 'login_with_persona' => 'Login with Persona', + 'login_persona_problem' => 'Connection problem with Persona?', 'logout' => 'Logout', 'search' => 'Search words or #tags', 'search_short' => 'Search', @@ -42,9 +43,11 @@ return array ( 'query_state_15' => 'Display all articles', 'query_number' => 'Query n°%d', 'add_query' => 'Add a query', + 'query_created' => 'Query "%s" has been created.', 'no_query' => 'You haven’t created any user query yet.', 'query_filter' => 'Filter applied:', 'no_query_filter' => 'No filter', + 'query_deprecated' => 'This query is no longer valid. The referenced category or feed has been deleted.', 'about' => 'About', 'stats' => 'Statistics', 'stats_idle' => 'Idle feeds', @@ -92,6 +95,7 @@ return array ( 'rss_view' => 'RSS feed', 'show_all_articles' => 'Show all articles', 'show_not_reads' => 'Show only unread', + 'show_adaptive' => 'Adjust showing', 'show_read' => 'Show only read', 'show_favorite' => 'Show only favorites', 'show_not_favorite' => 'Show all but favorites', @@ -111,7 +115,7 @@ return array ( 'access_denied' => 'You don’t have permission to access this page', 'page_not_found' => 'You are looking for a page which doesn’t exist', 'error_occurred' => 'An error occurred', - 'error_occurred_update' => 'Nothing was changed', + 'error_occurred_update' => 'Nothing was changed', 'default_category' => 'Uncategorized', 'categories_updated' => 'Categories have been updated', @@ -152,10 +156,13 @@ return array ( 'public' => 'Public', 'invalid_login' => 'Login is invalid', + 'file_is_nok' => 'Check permissions on <em>%s</em> directory. HTTP server must have rights to write into.', + // VIEWS 'save' => 'Save', 'delete' => 'Delete', 'cancel' => 'Cancel', + 'submit' => 'Submit', 'back_to_rss_feeds' => '← Go back to your RSS feeds', 'feeds_moved_category_deleted' => 'When you delete a category, their feeds are automatically classified under <em>%s</em>.', @@ -180,6 +187,9 @@ return array ( 'auto_share' => 'Share', 'auto_share_help' => 'If there is only one sharing mode, it is used. Else modes are accessible by their number.', 'focus_search' => 'Access search box', + '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.', + 'help' => 'Display documentation', 'file_to_import' => 'File to import<br />(OPML, Json or Zip)', 'file_to_import_no_zip' => 'File to import<br />(OPML or Json)', @@ -197,13 +207,15 @@ return array ( 'informations' => 'Information', 'damn' => 'Damn!', + 'ok' => 'Ok!', + 'attention' => 'Be careful!', 'feed_in_error' => 'This feed has encountered a problem. Please verify that it is always reachable then actualize it.', 'feed_empty' => 'This feed is empty. Please verify that it is still maintained.', 'feed_description' => 'Description', 'website_url' => 'Website URL', 'feed_url' => 'Feed URL', 'articles' => 'articles', - 'number_articles' => 'Number of articles', + 'number_articles' => '%d articles', 'by_feed' => 'by feed', 'by_default' => 'By default', 'keep_history' => 'Minimum number of articles to keep', @@ -225,7 +237,7 @@ return array ( 'not_yet_implemented' => 'Not yet implemented', 'access_protected_feeds' => 'Connection allows to access HTTP protected RSS feeds', 'no_selected_feed' => 'No feed selected.', - 'think_to_add' => '<a href="./?c=configure&a=feed">You may add some feeds</a>.', + 'think_to_add' => 'You may add some feeds.', 'current_user' => 'Current user', 'default_user' => 'Username of the default user <small>(maximum 16 alphanumeric characters)</small>', @@ -237,7 +249,7 @@ return array ( 'unsafe_autologin' => 'Allow unsafe automatic login using the format: ', 'api_enabled' => 'Allow <abbr>API</abbr> access <small>(required for mobile apps)</small>', 'auth_token' => 'Authentication token', - 'explain_token' => 'Allows to access RSS output of the default user without authentication.<br /><kbd>%s?output=rss&token=%s</kbd>', + 'explain_token' => 'Allows to access RSS output of the default user without authentication.<br /><kbd>%s?output=rss&token=%s</kbd>', 'login_configuration' => 'Login', 'is_admin' => 'is administrator', 'auth_type' => 'Authentication method', @@ -248,6 +260,7 @@ return array ( 'users_list' => 'List of users', 'create_user' => 'Create new user', 'username' => 'Username', + 'username_admin' => 'Administrator username', 'password' => 'Password', 'create' => 'Create', 'user_created' => 'User %s has been created', @@ -256,31 +269,35 @@ return array ( 'language' => 'Language', 'month' => 'months', 'archiving_configuration' => 'Archiving', - 'delete_articles_every' => 'Remove articles after', + 'delete_articles_every' => 'Remove articles after', 'purge_now' => 'Purge now', 'purge_completed' => 'Purge completed (%d articles deleted)', 'archiving_configuration_help' => 'More options are available in the individual stream settings', 'reading_configuration' => 'Reading', 'display_configuration' => 'Display', 'articles_per_page' => 'Number of articles per page', + 'number_divided_when_reader' => 'Divided by 2 in the reading view.', 'default_view' => 'Default view', + 'articles_to_display' => 'Articles to display', 'sort_order' => 'Sort order', 'auto_load_more' => 'Load next articles at the page bottom', 'display_articles_unfolded' => 'Show articles unfolded by default', 'display_categories_unfolded' => 'Show categories folded by default', - 'hide_read_feeds' => 'Hide categories & feeds with no unread article (only in “unread articles” display mode)', + 'hide_read_feeds' => 'Hide categories & feeds with no unread article (does not work with “Show all articles” configuration)', 'after_onread' => 'After “mark all as read”,', 'jump_next' => 'jump to next unread sibling (feed or category)', 'article_icons' => 'Article icons', 'top_line' => 'Top line', 'bottom_line' => 'Bottom line', + 'html5_notif_timeout' => 'HTML5 notification timeout', + 'seconds_(0_means_no_timeout)' => 'seconds (0 means no timeout)', 'img_with_lazyload' => 'Use "lazy load" mode to load pictures', 'sticky_post' => 'Stick the article to the top when opened', 'reading_confirm' => 'Display a confirmation dialog on “mark all as read” actions', 'auto_read_when' => 'Mark article as read…', 'article_viewed' => 'when article is viewed', 'article_open_on_website' => 'when article is opened on its original website', - 'scroll' => 'during page scrolls', + 'scroll' => 'while scrolling', 'upon_reception' => 'upon reception of the article', 'your_shaarli' => 'Your Shaarli', 'your_wallabag' => 'Your wallabag', @@ -340,7 +357,7 @@ return array ( 'agpl3' => '<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL 3</a>', 'freshrss_description' => 'FreshRSS is a RSS feeds aggregator to self-host like <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> or <a href="http://projet.idleman.fr/leed/">Leed</a>. It is light and easy to take in hand while being powerful and configurable tool.', 'credits' => 'Credits', - 'credits_content' => 'Some design elements come from <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> although FreshRSS doesn’t use this framework. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Icons</a> come from <a href="https://www.gnome.org/">GNOME project</a>. <em>Open Sans</em> font police used has been created by <a href="https://www.google.com/webfonts/specimen/Open+Sans">Steve Matteson</a>. Favicons are collected with <a href="https://getfavicon.appspot.com/">getFavicon API</a>. FreshRSS is based on <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, a PHP framework.', + 'credits_content' => 'Some design elements come from <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> although FreshRSS doesn’t use this framework. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Icons</a> come from <a href="https://www.gnome.org/">GNOME project</a>. <em>Open Sans</em> font police has been created by <a href="https://www.google.com/webfonts/specimen/Open+Sans">Steve Matteson</a>. Favicons are collected with <a href="https://getfavicon.appspot.com/">getFavicon API</a>. FreshRSS is based on <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, a PHP framework.', 'version' => 'Version', 'logs' => 'Logs', @@ -351,6 +368,7 @@ return array ( 'login_required' => 'Login required:', 'confirm_action' => 'Are you sure you want to perform this action? It cannot be cancelled!', + 'confirm_action_feed_cat' => 'Are you sure you want to perform this action? You may lost related favorites and user queries. It cannot be cancelled!', 'notif_title_new_articles' => 'FreshRSS: new articles!', 'notif_body_new_articles' => 'There are \d new articles to read on FreshRSS.', @@ -414,4 +432,23 @@ return array ( 'stats_entry_per_category' => 'Entries per category', 'stats_top_feed' => 'Top ten feeds', 'stats_entry_count' => 'Entry count', + 'stats_no_idle' => 'There is no idle feed!', + + 'update' => 'Update', + 'update_system' => 'Update system', + 'update_check' => 'Check for new updates', + 'update_last' => 'Last verification: %s', + 'update_can_apply' => 'An update is available.', + 'update_apply' => 'Apply', + 'update_server_not_found' => 'Update server cannot be found. [%s]', + 'no_update' => 'No update to apply', + 'update_problem' => 'The update process has encountered an error: %s', + 'update_finished' => 'Update completed!', + + 'auth_reset' => 'Authentication reset', + 'auth_will_reset' => 'Authentication system will be reset: a form will be used instead of Persona.', + 'auth_not_persona' => 'Only Persona system can be reset.', + 'auth_no_password_set' => 'Administrator password hasn’t been set. This feature isn’t available.', + 'auth_form_set' => 'Form is now your default authentication system.', + 'auth_form_not_set' => 'A problem occured during authentication system configuration. Please retry later.', ); diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 08f12234e..b0fbf15ae 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -5,6 +5,7 @@ return array ( 'login' => 'Connexion', 'keep_logged_in' => 'Rester connecté <small>(1 mois)</small>', 'login_with_persona' => 'Connexion avec Persona', + 'login_persona_problem' => 'Problème de connexion à Persona ?', 'logout' => 'Déconnexion', 'search' => 'Rechercher des mots ou des #tags', 'search_short' => 'Rechercher', @@ -42,9 +43,11 @@ return array ( 'query_state_15' => 'Afficher tous les articles', 'query_number' => 'Filtre n°%d', 'add_query' => 'Créer un filtre', + 'query_created' => 'Le filtre "%s" a bien été créé.', 'no_query' => 'Vous n’avez pas encore créé de filtre.', 'query_filter' => 'Filtres appliqués :', 'no_query_filter' => 'Aucun filtre appliqué', + 'query_deprecated' => 'Ce filtre n’est plus valide. La catégorie ou le flux concerné a été supprimé.', 'about' => 'À propos', 'stats' => 'Statistiques', 'stats_idle' => 'Flux inactifs', @@ -54,11 +57,11 @@ return array ( 'stats_entry_per_day_of_week' => 'Par jour de la semaine', 'stats_entry_per_month' => 'Par mois', - 'last_week' => 'La dernière semaine', - 'last_month' => 'Le dernier mois', - 'last_3_month' => 'Les derniers trois mois', - 'last_6_month' => 'Les derniers six mois', - 'last_year' => 'La dernière année', + 'last_week' => 'Depuis la semaine dernière', + 'last_month' => 'Depuis le mois dernier', + 'last_3_month' => 'Depuis les trois derniers mois', + 'last_6_month' => 'Depuis les six derniers mois', + 'last_year' => 'Depuis l’année dernière', 'your_rss_feeds' => 'Vos flux RSS', 'add_rss_feed' => 'Ajouter un flux RSS', @@ -92,6 +95,7 @@ return array ( 'rss_view' => 'Flux RSS', 'show_all_articles' => 'Afficher tous les articles', 'show_not_reads' => 'Afficher les non lus', + 'show_adaptive' => 'Adapter l’affichage', 'show_read' => 'Afficher les lus', 'show_favorite' => 'Afficher les favoris', 'show_not_favorite' => 'Afficher tout sauf les favoris', @@ -152,10 +156,13 @@ return array ( 'public' => 'Public', 'invalid_login' => 'L’identifiant est invalide !', + 'file_is_nok' => 'Veuillez vérifier les droits sur le répertoire <em>%s</em>. Le serveur HTTP doit être capable d’écrire dedans.', + // VIEWS 'save' => 'Enregistrer', 'delete' => 'Supprimer', 'cancel' => 'Annuler', + 'submit' => 'Valider', 'back_to_rss_feeds' => '← Retour à vos flux RSS', 'feeds_moved_category_deleted' => 'Lors de la suppression d’une catégorie, ses flux seront automatiquement classés dans <em>%s</em>.', @@ -180,6 +187,9 @@ return array ( 'auto_share' => 'Partager', 'auto_share_help' => 'S’il n’y a qu’un mode de partage, celui ci est utilisé automatiquement. Sinon ils sont accessibles par leur numéro.', 'focus_search' => 'Accéder à la recherche', + '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.', + 'help' => 'Afficher la documentation', 'file_to_import' => 'Fichier à importer<br />(OPML, Json ou Zip)', 'file_to_import_no_zip' => 'Fichier à importer<br />(OPML ou Json)', @@ -197,13 +207,15 @@ return array ( 'informations' => 'Informations', 'damn' => 'Arf !', + 'ok' => 'Ok !', + 'attention' => 'Attention !', 'feed_in_error' => 'Ce flux a rencontré un problème. Veuillez vérifier qu’il est toujours accessible puis actualisez-le.', 'feed_empty' => 'Ce flux est vide. Veuillez vérifier qu’il est toujours maintenu.', 'feed_description' => 'Description', 'website_url' => 'URL du site', 'feed_url' => 'URL du flux', 'articles' => 'articles', - 'number_articles' => 'Nombre d’articles', + 'number_articles' => '%d articles', 'by_feed' => 'par flux', 'by_default' => 'Par défaut', 'keep_history' => 'Nombre minimum d’articles à conserver', @@ -225,7 +237,7 @@ return array ( 'not_yet_implemented' => 'Pas encore implémenté', 'access_protected_feeds' => 'La connexion permet d’accéder aux flux protégés par une authentification HTTP.', 'no_selected_feed' => 'Aucun flux sélectionné.', - 'think_to_add' => '<a href="./?c=configure&a=feed">Vous pouvez ajouter des flux</a>.', + 'think_to_add' => 'Vous pouvez ajouter des flux.', 'current_user' => 'Utilisateur actuel', 'password_form' => 'Mot de passe<br /><small>(pour connexion par formulaire)</small>', @@ -237,7 +249,7 @@ return array ( 'unsafe_autologin' => 'Autoriser les connexions automatiques non-sûres au format : ', 'api_enabled' => 'Autoriser l’accès par <abbr>API</abbr> <small>(nécessaire pour les applis mobiles)</small>', 'auth_token' => 'Jeton d’identification', - 'explain_token' => 'Permet d’accéder à la sortie RSS de l’utilisateur par défaut sans besoin de s’authentifier.<br /><kbd>%s?output=rss&token=%s</kbd>', + 'explain_token' => 'Permet d’accéder à la sortie RSS de l’utilisateur par défaut sans besoin de s’authentifier.<br /><kbd>%s?output=rss&token=%s</kbd>', 'login_configuration' => 'Identification', 'is_admin' => 'est administrateur', 'auth_type' => 'Méthode d’authentification', @@ -248,6 +260,7 @@ return array ( 'users_list' => 'Liste des utilisateurs', 'create_user' => 'Créer un nouvel utilisateur', 'username' => 'Nom d’utilisateur', + 'username_admin' => 'Nom d’utilisateur administrateur', 'password' => 'Mot de passe', 'create' => 'Créer', 'user_created' => 'L’utilisateur %s a été créé.', @@ -263,17 +276,21 @@ return array ( 'reading_configuration' => 'Lecture', 'display_configuration' => 'Affichage', 'articles_per_page' => 'Nombre d’articles par page', + 'number_divided_when_reader' => 'Divisé par 2 dans la vue de lecture.', 'default_view' => 'Vue par défaut', + 'articles_to_display' => 'Articles à afficher', 'sort_order' => 'Ordre de tri', 'auto_load_more' => 'Charger les articles suivants en bas de page', 'display_articles_unfolded' => 'Afficher les articles dépliés par défaut', 'display_categories_unfolded' => 'Afficher les catégories pliées par défaut', - 'hide_read_feeds' => 'Cacher les catégories & flux sans article non-lu (uniquement en affichage “articles non lus”)', + 'hide_read_feeds' => 'Cacher les catégories & flux sans article non-lu (ne fonctionne pas avec la configuration “Afficher tous les articles”)', 'after_onread' => 'Après “marquer tout comme lu”,', 'jump_next' => 'sauter au prochain voisin non lu (flux ou catégorie)', 'article_icons' => 'Icônes d’article', 'top_line' => 'Ligne du haut', 'bottom_line' => 'Ligne du bas', + 'html5_notif_timeout' => 'Temps d’affichage de la notification HTML5', + 'seconds_(0_means_no_timeout)' => 'secondes (0 signifie aucun timeout ) ', 'img_with_lazyload' => 'Utiliser le mode “chargement différé” pour les images', 'sticky_post' => 'Aligner l’article en haut quand il est ouvert', 'reading_confirm' => 'Afficher une confirmation lors des actions “marquer tout comme lu”', @@ -351,6 +368,7 @@ return array ( 'login_required' => 'Accès protégé par mot de passe :', 'confirm_action' => 'Êtes-vous sûr(e) de vouloir continuer ? Cette action ne peut être annulée !', + 'confirm_action_feed_cat' => 'Êtes-vous sûr(e) de vouloir continuer ? Vous pourriez perdre les favoris et les filtres associés. Cette action ne peut être annulée !', 'notif_title_new_articles' => 'FreshRSS : nouveaux articles !', 'notif_body_new_articles' => 'Il y a \d nouveaux articles à lire sur FreshRSS.', @@ -414,4 +432,23 @@ return array ( 'stats_entry_per_category' => 'Articles par catégorie', 'stats_top_feed' => 'Les dix plus gros flux', 'stats_entry_count' => 'Nombre d’articles', + 'stats_no_idle' => 'Il n’y a aucun flux inactif !', + + 'update' => 'Mise à jour', + 'update_system' => 'Système de mise à jour', + 'update_check' => 'Vérifier les mises à jour', + 'update_last' => 'Dernière vérification : %s', + 'update_can_apply' => 'Une mise à jour est disponible.', + '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', + 'update_finished' => 'La mise à jour est terminée !', + + 'auth_reset' => 'Réinitialisation de l’authentification', + 'auth_will_reset' => 'Le système d’authentification va être réinitialisé : un formulaire sera utilisé à la place de Persona.', + 'auth_not_persona' => 'Seul le système d’authentification Persona peut être réinitialisé.', + 'auth_no_password_set' => 'Aucun mot de passe administrateur n’a été précisé. Cette fonctionnalité n’est pas disponible.', + 'auth_form_set' => 'Le formulaire est désormais votre système d’authentification.', + 'auth_form_not_set' => 'Un problème est survenu lors de la configuration de votre système d’authentification. Veuillez réessayer plus tard.', ); diff --git a/app/i18n/install.en.php b/app/i18n/install.en.php index 50208fcef..c422de90f 100644 --- a/app/i18n/install.en.php +++ b/app/i18n/install.en.php @@ -42,6 +42,8 @@ return array ( 'data_is_ok' => 'Permissions on data directory are good', 'persona_is_ok' => 'Permissions on Mozilla Persona directory are good', 'file_is_nok' => 'Check permissions on <em>%s</em> directory. HTTP server must have rights to write into', + 'http_referer_is_ok' => 'Your HTTP REFERER is known and corresponds to your server.', + 'http_referer_is_nok' => 'Please check that you are not altering your HTTP REFERER.', 'fix_errors_before' => 'Fix errors before skip to the next step.', 'general_conf_is_ok' => 'General configuration has been saved.', diff --git a/app/i18n/install.fr.php b/app/i18n/install.fr.php index 9c039f904..785c02459 100644 --- a/app/i18n/install.fr.php +++ b/app/i18n/install.fr.php @@ -42,6 +42,8 @@ return array ( 'data_is_ok' => 'Les droits sur le répertoire de data sont bons', 'persona_is_ok' => 'Les droits sur le répertoire de Mozilla Persona sont bons', 'file_is_nok' => 'Veuillez vérifier les droits sur le répertoire <em>%s</em>. Le serveur HTTP doit être capable d’écrire dedans', + 'http_referer_is_ok' => 'Le HTTP REFERER est connu et semble correspondre à votre serveur.', + 'http_referer_is_nok' => 'Veuillez vérifier que vous ne modifiez pas votre HTTP REFERER.', 'fix_errors_before' => 'Veuillez corriger les erreurs avant de passer à l’étape suivante.', 'general_conf_is_ok' => 'La configuration générale a été enregistrée.', diff --git a/app/install.php b/app/install.php index eaa1100c1..4449cd063 100644 --- a/app/install.php +++ b/app/install.php @@ -9,10 +9,10 @@ session_name('FreshRSS'); session_set_cookie_params(0, dirname(empty($_SERVER['REQUEST_URI']) ? '/' : dirname($_SERVER['REQUEST_URI'])), null, false, true); session_start(); -if (isset ($_GET['step'])) { - define ('STEP', (int)$_GET['step']); +if (isset($_GET['step'])) { + define('STEP',(int)$_GET['step']); } else { - define ('STEP', 1); + define('STEP', 0); } define('SQL_CREATE_DB', 'CREATE DATABASE IF NOT EXISTS %1$s DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;'); @@ -23,88 +23,28 @@ if (STEP === 3 && isset($_POST['type'])) { if (isset($_SESSION['bd_type'])) { switch ($_SESSION['bd_type']) { - case 'mysql': - include(APP_PATH . '/SQL/install.sql.mysql.php'); - break; - case 'sqlite': - include(APP_PATH . '/SQL/install.sql.sqlite.php'); - break; + case 'mysql': + include(APP_PATH . '/SQL/install.sql.mysql.php'); + break; + case 'sqlite': + include(APP_PATH . '/SQL/install.sql.sqlite.php'); + break; } } -//<updates> -define('SQL_BACKUP006', 'RENAME TABLE `%1$scategory` TO `%1$scategory006`, `%1$sfeed` TO `%1$sfeed006`, `%1$sentry` TO `%1$sentry006`;'); - -define('SQL_SHOW_COLUMNS_UPDATEv006', 'SHOW columns FROM `%1$sentry006` LIKE "id2";'); - -define('SQL_UPDATEv006', ' -ALTER TABLE `%1$scategory006` ADD id2 SMALLINT; - -SET @i = 0; -UPDATE `%1$scategory006` SET id2=(@i:=@i+1) ORDER BY id; - -ALTER TABLE `%1$sfeed006` ADD id2 SMALLINT, ADD category2 SMALLINT; - -SET @i = 0; -UPDATE `%1$sfeed006` SET id2=(@i:=@i+1) ORDER BY name; - -UPDATE `%1$sfeed006` f -INNER JOIN `%1$scategory006` c ON f.category = c.id -SET f.category2 = c.id2; - -INSERT IGNORE INTO `%2$scategory` (name) -SELECT name -FROM `%1$scategory006` -ORDER BY id2; - -INSERT IGNORE INTO `%2$sfeed` (url, category, name, website, description, priority, pathEntries, httpAuth, keep_history) -SELECT url, category2, name, website, description, priority, pathEntries, httpAuth, IF(keep_history = 1, -1, -2) -FROM `%1$sfeed006` -ORDER BY id2; - -ALTER TABLE `%1$sentry006` ADD id2 bigint; - -UPDATE `%1$sentry006` SET id2 = ((date * 1000000) + (rand() * 100000000)); - -INSERT IGNORE INTO `%2$sentry` (id, guid, title, author, link, date, is_read, is_favorite, id_feed, tags) -SELECT e0.id2, e0.guid, e0.title, e0.author, e0.link, e0.date, e0.is_read, e0.is_favorite, f0.id2, e0.tags -FROM `%1$sentry006` e0 -INNER JOIN `%1$sfeed006` f0 ON e0.id_feed = f0.id; -'); - -define('SQL_CONVERT_SELECTv006', ' -SELECT e0.id2, e0.content -FROM `%1$sentry006` e0 -INNER JOIN `%2$sentry` e1 ON e0.id2 = e1.id -WHERE e1.content_bin IS NULL'); - -define('SQL_CONVERT_UPDATEv006', 'UPDATE `%1$sentry` SET ' - . (isset($_SESSION['bd_type']) && $_SESSION['bd_type'] === 'mysql' ? 'content_bin=COMPRESS(?)' : 'content=?') - . ' WHERE id=?;'); - -define('SQL_DROP_BACKUPv006', 'DROP TABLE IF EXISTS `%1$sentry006`, `%1$sfeed006`, `%1$scategory006`;'); - -define('SQL_UPDATE_CACHED_VALUES', ' -UPDATE `%1$sfeed` f -INNER JOIN ( - SELECT e.id_feed, - COUNT(CASE WHEN e.is_read = 0 THEN 1 END) AS nbUnreads, - COUNT(e.id) AS nbEntries - FROM `%1$sentry` e - GROUP BY e.id_feed -) x ON x.id_feed=f.id -SET f.cache_nbEntries=x.nbEntries, f.cache_nbUnreads=x.nbUnreads -'); - -define('SQL_UPDATE_HISTORYv007b', 'UPDATE `%1$sfeed` SET keep_history = CASE WHEN keep_history = 0 THEN -2 WHEN keep_history = 1 THEN -1 ELSE keep_history END;'); +function param($key, $default = false) { + if (isset($_POST[$key])) { + return $_POST[$key]; + } else { + return $default; + } +} -define('SQL_GET_FEEDS', 'SELECT id, url, website FROM `%1$sfeed`;'); -//</updates> // gestion internationalisation -$translates = array (); +$translates = array(); $actual = 'en'; -function initTranslate () { +function initTranslate() { global $translates; global $actual; @@ -121,83 +61,95 @@ function initTranslate () { } } -function getBetterLanguage ($fallback) { - $available = availableLanguages (); +function getBetterLanguage($fallback) { + $available = availableLanguages(); $accept = $_SERVER['HTTP_ACCEPT_LANGUAGE']; - $language = strtolower (substr ($accept, 0, 2)); + $language = strtolower(substr($accept, 0, 2)); - if (isset ($available[$language])) { + if (isset($available[$language])) { return $language; } else { return $fallback; } } -function availableLanguages () { - return array ( + +function availableLanguages() { + return array( 'en' => 'English', 'fr' => 'Français' ); } -function _t ($key) { + +function _t($key) { global $translates; $translate = $key; - if (isset ($translates[$key])) { + if (isset($translates[$key])) { $translate = $translates[$key]; } - $args = func_get_args (); + $args = func_get_args(); unset($args[0]); - return vsprintf ($translate, $args); + return vsprintf($translate, $args); } + /*** SAUVEGARDES ***/ -function saveLanguage () { - if (!empty ($_POST)) { - if (!isset ($_POST['language'])) { +function saveLanguage() { + if (!empty($_POST)) { + if (!isset($_POST['language'])) { return false; } $_SESSION['language'] = $_POST['language']; - header ('Location: index.php?step=1'); + header('Location: index.php?step=1'); } } -function saveStep2 () { - if (!empty ($_POST)) { - if (empty ($_POST['title']) || - empty ($_POST['old_entries']) || - empty ($_POST['auth_type']) || - empty ($_POST['default_user'])) { - return false; - } - $_SESSION['salt'] = sha1(uniqid(mt_rand(), true).implode('', stat(__FILE__))); - $_SESSION['title'] = substr(trim($_POST['title']), 0, 25); - $_SESSION['old_entries'] = $_POST['old_entries']; - if ((!ctype_digit($_SESSION['old_entries'])) || ($_SESSION['old_entries'] < 1)) { - $_SESSION['old_entries'] = 3; - } - $_SESSION['mail_login'] = filter_var($_POST['mail_login'], FILTER_VALIDATE_EMAIL); - $_SESSION['default_user'] = substr(preg_replace('/[^a-zA-Z0-9]/', '', $_POST['default_user']), 0, 16); - $_SESSION['auth_type'] = $_POST['auth_type']; - if (!empty($_POST['passwordPlain'])) { +function saveStep2() { + if (!empty($_POST)) { + $_SESSION['title'] = substr(trim(param('title', _t('freshrss'))), 0, 25); + $_SESSION['old_entries'] = param('old_entries', 3); + $_SESSION['auth_type'] = param('auth_type', 'form'); + $_SESSION['default_user'] = substr(preg_replace('/[^a-zA-Z0-9]/', '', param('default_user', '')), 0, 16); + $_SESSION['mail_login'] = filter_var(param('mail_login', ''), FILTER_VALIDATE_EMAIL); + + $password_plain = param('passwordPlain', false); + if ($password_plain !== false) { if (!function_exists('password_hash')) { include_once(LIB_PATH . '/password_compat.php'); } - $passwordHash = password_hash($_POST['passwordPlain'], PASSWORD_BCRYPT, array('cost' => BCRYPT_COST)); + $passwordHash = password_hash($password_plain, PASSWORD_BCRYPT, array('cost' => BCRYPT_COST)); $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js $_SESSION['passwordHash'] = $passwordHash; } + if (empty($_SESSION['title']) || + empty($_SESSION['old_entries']) || + empty($_SESSION['auth_type']) || + empty($_SESSION['default_user'])) { + return false; + } + + if (($_SESSION['auth_type'] === 'form' && empty($_SESSION['passwordHash'])) || + ($_SESSION['auth_type'] === 'persona' && empty($_SESSION['mail_login']))) { + return false; + } + + $_SESSION['salt'] = sha1(uniqid(mt_rand(), true).implode('', stat(__FILE__))); + if ((!ctype_digit($_SESSION['old_entries'])) ||($_SESSION['old_entries'] < 1)) { + $_SESSION['old_entries'] = 3; + } + $token = ''; if ($_SESSION['mail_login']) { $token = sha1($_SESSION['salt'] . $_SESSION['mail_login']); } - $config_array = array ( + $config_array = array( 'language' => $_SESSION['language'], - 'theme' => $_SESSION['theme'], + 'theme' => 'Origine', 'old_entries' => $_SESSION['old_entries'], 'mail_login' => $_SESSION['mail_login'], 'passwordHash' => $_SESSION['passwordHash'], @@ -214,12 +166,12 @@ function saveStep2 () { file_put_contents($personaFile, $_SESSION['default_user']); } - header ('Location: index.php?step=3'); + header('Location: index.php?step=3'); } } -function saveStep3 () { - if (!empty ($_POST)) { +function saveStep3() { + if (!empty($_POST)) { if ($_SESSION['bd_type'] === 'sqlite') { $_SESSION['bd_base'] = $_SESSION['default_user']; $_SESSION['bd_host'] = ''; @@ -228,10 +180,10 @@ function saveStep3 () { $_SESSION['bd_prefix'] = ''; $_SESSION['bd_prefix_user'] = ''; //No prefix for SQLite } else { - if (empty ($_POST['type']) || - empty ($_POST['host']) || - empty ($_POST['user']) || - empty ($_POST['base'])) { + if (empty($_POST['type']) || + empty($_POST['host']) || + empty($_POST['user']) || + empty($_POST['base'])) { $_SESSION['bd_error'] = 'Missing parameters!'; } $_SESSION['bd_base'] = substr($_POST['base'], 0, 64); @@ -239,7 +191,7 @@ function saveStep3 () { $_SESSION['bd_user'] = $_POST['user']; $_SESSION['bd_password'] = $_POST['pass']; $_SESSION['bd_prefix'] = substr($_POST['prefix'], 0, 16); - $_SESSION['bd_prefix_user'] = $_SESSION['bd_prefix'] . (empty($_SESSION['default_user']) ? '' : ($_SESSION['default_user'] . '_')); + $_SESSION['bd_prefix_user'] = $_SESSION['bd_prefix'] .(empty($_SESSION['default_user']) ? '' :($_SESSION['default_user'] . '_')); } $ini_array = array( @@ -268,15 +220,11 @@ function saveStep3 () { @unlink(DATA_PATH . '/config.php'); //To avoid access-rights problems file_put_contents(DATA_PATH . '/config.php', "<?php\n return " . var_export($ini_array, true) . ';'); - if (file_exists(DATA_PATH . '/config.php') && file_exists(DATA_PATH . '/application.ini')) { - @unlink(DATA_PATH . '/application.ini'); //v0.6 - } - - $res = checkBD (); + $res = checkBD(); if ($res) { $_SESSION['bd_error'] = ''; - header ('Location: index.php?step=4'); + header('Location: index.php?step=4'); } elseif (empty($_SESSION['bd_error'])) { $_SESSION['bd_error'] = 'Unknown error!'; } @@ -284,309 +232,84 @@ function saveStep3 () { invalidateHttpCache(); } -function updateDatabase($perform = false) { - $needs = array('bd_type', 'bd_host', 'bd_base', 'bd_user', 'bd_password', 'bd_prefix', 'bd_prefix_user'); - foreach ($needs as $need) { - if (!isset($_SESSION[$need])) { - return false; - } - } - - try { - $str = ''; - switch ($_SESSION['bd_type']) { - case 'mysql': - $str = 'mysql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base']; - $driver_options = array( - PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8', - ); - break; - case 'sqlite': - return false; //No update for SQLite needed so far - default: - return false; - } - - $c = new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); - - $stm = $c->prepare(SQL_SHOW_TABLES); - $stm->execute(); - $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); - if (!in_array($_SESSION['bd_prefix'] . 'entry006', $res)) { - return false; - } - - $sql = sprintf(SQL_SHOW_COLUMNS_UPDATEv006, $_SESSION['bd_prefix']); - $stm = $c->prepare($sql); - $stm->execute(); - $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); - if (!in_array('id2', $res)) { - if (!$perform) { - return true; - } - $sql = sprintf(SQL_UPDATEv006, $_SESSION['bd_prefix'], $_SESSION['bd_prefix_user']); - $stm = $c->prepare($sql, array(PDO::ATTR_EMULATE_PREPARES => true)); - $stm->execute(); - } - - $sql = sprintf(SQL_CONVERT_SELECTv006, $_SESSION['bd_prefix'], $_SESSION['bd_prefix_user']); - if (!$perform) { - $sql .= ' LIMIT 1'; - } - $stm = $c->prepare($sql); - $stm->execute(); - if (!$perform) { - $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); - return count($res) > 0; - } else { - @set_time_limit(300); - } - - $c2 = new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); - $sql = sprintf(SQL_CONVERT_UPDATEv006, $_SESSION['bd_prefix_user']); - $stm2 = $c2->prepare($sql); - while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { - $id = $row['id2']; - $content = unserialize(gzinflate(base64_decode($row['content']))); - $stm2->execute(array($content, $id)); - } - - return true; - } catch (PDOException $e) { - return false; - } - return false; -} - function newPdo() { switch ($_SESSION['bd_type']) { - case 'mysql': - $str = 'mysql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base']; - $driver_options = array( - PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8', - ); - break; - case 'sqlite': - $str = 'sqlite:' . DATA_PATH . '/' . $_SESSION['default_user'] . '.sqlite'; - $driver_options = array( - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - ); - break; - default: - return false; + case 'mysql': + $str = 'mysql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base']; + $driver_options = array( + PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8', + ); + break; + case 'sqlite': + $str = 'sqlite:' . DATA_PATH . '/' . $_SESSION['default_user'] . '.sqlite'; + $driver_options = array( + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + ); + break; + default: + return false; } return new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); } -function postUpdate() { - $c = newPdo(); - - if ($_SESSION['bd_type'] !== 'sqlite') { //No update for SQLite needed yet - $sql = sprintf(SQL_UPDATE_HISTORYv007b, $_SESSION['bd_prefix_user']); - $stm = $c->prepare($sql); - $stm->execute(); +function deleteInstall() { + $res = unlink(DATA_PATH . '/do-install.txt'); - $sql = sprintf(SQL_UPDATE_CACHED_VALUES, $_SESSION['bd_prefix_user']); - $stm = $c->prepare($sql); - $stm->execute(); - } - - //<favicons> - $sql = sprintf(SQL_GET_FEEDS, $_SESSION['bd_prefix_user']); - $stm = $c->prepare($sql); - $stm->execute(); - $res = $stm->fetchAll(PDO::FETCH_ASSOC); - foreach ($res as $feed) { - if (empty($feed['url'])) { - continue; - } - $hash = hash('crc32b', $_SESSION['salt'] . $feed['url']); - @file_put_contents(DATA_PATH . '/favicons/' . $hash . '.txt', - empty($feed['website']) ? $feed['url'] : $feed['website']); - } - //</favicons> -} - -function deleteInstall () { - $res = unlink (DATA_PATH . '/do-install.txt'); - if ($res) { - header ('Location: index.php'); - } - - $needs = array('bd_type', 'bd_host', 'bd_base', 'bd_user', 'bd_password', 'bd_prefix'); - foreach ($needs as $need) { - if (!isset($_SESSION[$need])) { - return false; - } - } - - try { - $c = newPdo(); - $sql = sprintf(SQL_DROP_BACKUPv006, $_SESSION['bd_prefix']); - $stm = $c->prepare($sql); - $stm->execute(); - - return true; - } catch (PDOException $e) { + if (!$res) { return false; } - return false; -} -function moveOldFiles() { - $mvs = array( - '/app/configuration/application.ini' => '/data/application.ini', //v0.6 - '/public/data/Configuration.array.php' => '/data/Configuration.array.php', //v0.6 - ); - $ok = true; - foreach ($mvs as $fFrom => $fTo) { - if (file_exists(FRESHRSS_PATH . $fFrom)) { - if (copy(FRESHRSS_PATH . $fFrom, FRESHRSS_PATH . $fTo)) { - @unlink(FRESHRSS_PATH . $fFrom); - } else { - $ok = false; - } - } - } - return $ok; + header('Location: index.php'); } -function delTree($dir) { //http://php.net/rmdir#110489 - if (!is_dir($dir)) { - return true; - } - $files = array_diff(scandir($dir), array('.', '..')); - foreach ($files as $file) { - $f = $dir . '/' . $file; - if (is_dir($f)) { - @chmod($f, 0777); - delTree($f); - } - else unlink($f); - } - return rmdir($dir); -} /*** VÉRIFICATIONS ***/ -function checkStep () { - $s0 = checkStep0 (); - $s1 = checkStep1 (); - $s2 = checkStep2 (); - $s3 = checkStep3 (); +function checkStep() { + $s0 = checkStep0(); + $s1 = checkStep1(); + $s2 = checkStep2(); + $s3 = checkStep3(); if (STEP > 0 && $s0['all'] != 'ok') { - header ('Location: index.php?step=0'); + header('Location: index.php?step=0'); } elseif (STEP > 1 && $s1['all'] != 'ok') { - header ('Location: index.php?step=1'); + header('Location: index.php?step=1'); } elseif (STEP > 2 && $s2['all'] != 'ok') { - header ('Location: index.php?step=2'); + header('Location: index.php?step=2'); } elseif (STEP > 3 && $s3['all'] != 'ok') { - header ('Location: index.php?step=3'); + header('Location: index.php?step=3'); } $_SESSION['actualize_feeds'] = true; } -function checkStep0 () { - moveOldFiles(); - - if (file_exists(DATA_PATH . '/config.php')) { - $ini_array = include(DATA_PATH . '/config.php'); - } elseif (file_exists(DATA_PATH . '/application.ini')) { //v0.6 - $ini_array = parse_ini_file(DATA_PATH . '/application.ini', true); - $ini_array['general']['title'] = empty($ini_array['general']['title']) ? '' : stripslashes($ini_array['general']['title']); - } else { - $ini_array = null; - } - if ($ini_array) { - $ini_general = isset($ini_array['general']) ? $ini_array['general'] : null; - if ($ini_general) { - $keys = array('environment', 'salt', 'title', 'default_user', 'allow_anonymous', 'allow_anonymous_refresh', 'auth_type', 'api_enabled', 'unsafe_autologin_enabled'); - foreach ($keys as $key) { - if ((empty($_SESSION[$key])) && isset($ini_general[$key])) { - $_SESSION[$key] = $ini_general[$key]; - } - } - } - $ini_db = isset($ini_array['db']) ? $ini_array['db'] : null; - if ($ini_db) { - $keys = array('type', 'host', 'user', 'password', 'base', 'prefix'); - foreach ($keys as $key) { - if ((!isset($_SESSION['bd_' . $key])) && isset($ini_db[$key])) { - $_SESSION['bd_' . $key] = $ini_db[$key]; - } - } - } - } +function checkStep0() { + $languages = availableLanguages(); + $language = isset($_SESSION['language']) && + isset($languages[$_SESSION['language']]); - if (isset($_SESSION['default_user']) && file_exists(DATA_PATH . '/' . $_SESSION['default_user'] . '_user.php')) { - $userConfig = include(DATA_PATH . '/' . $_SESSION['default_user'] . '_user.php'); - } elseif (file_exists(DATA_PATH . '/Configuration.array.php')) { - $userConfig = include(DATA_PATH . '/Configuration.array.php'); //v0.6 - if (empty($_SESSION['auth_type'])) { - $_SESSION['auth_type'] = empty($userConfig['mail_login']) ? 'none' : 'persona'; - } - if (!isset($_SESSION['allow_anonymous'])) { - $_SESSION['allow_anonymous'] = empty($userConfig['anon_access']) ? false : ($userConfig['anon_access'] === 'yes'); - } - } else { - $userConfig = array(); - } - if (empty($_SESSION['auth_type'])) { //v0.7b - $_SESSION['auth_type'] = ''; - } - - $keys = array('language', 'theme', 'old_entries', 'mail_login', 'passwordHash'); - foreach ($keys as $key) { - if ((!isset($_SESSION[$key])) && isset($userConfig[$key])) { - $_SESSION[$key] = $userConfig[$key]; - } - } - - $languages = availableLanguages (); - $language = isset ($_SESSION['language']) && - isset ($languages[$_SESSION['language']]); - - if (empty($_SESSION['passwordHash'])) { //v0.7b - $_SESSION['passwordHash'] = ''; - } - if (empty($_SESSION['theme'])) { - $_SESSION['theme'] = 'Origine'; - } else { - switch (strtolower($_SESSION['theme'])) { - case 'default': //v0.7b - $_SESSION['theme'] = 'Origine'; - break; - case 'flat-design': //v0.7b - $_SESSION['theme'] = 'Flat'; - break; - case 'default_dark': //v0.7b - $_SESSION['theme'] = 'Dark'; - break; - } - } - - return array ( + return array( 'language' => $language ? 'ok' : 'ko', 'all' => $language ? 'ok' : 'ko' ); } -function checkStep1 () { - $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'); +function checkStep1() { + $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 = $pdo_mysql || $pdo_sqlite; - $pcre = extension_loaded ('pcre'); - $ctype = extension_loaded ('ctype'); + $pcre = extension_loaded('pcre'); + $ctype = extension_loaded('ctype'); $dom = class_exists('DOMDocument'); - $data = DATA_PATH && is_writable (DATA_PATH); - $cache = CACHE_PATH && is_writable (CACHE_PATH); - $log = LOG_PATH && is_writable (LOG_PATH); - $favicons = is_writable (DATA_PATH . '/favicons'); - $persona = is_writable (DATA_PATH . '/persona'); - - return array ( + $data = DATA_PATH && is_writable(DATA_PATH); + $cache = CACHE_PATH && is_writable(CACHE_PATH); + $log = LOG_PATH && is_writable(LOG_PATH); + $favicons = is_writable(DATA_PATH . '/favicons'); + $persona = is_writable(DATA_PATH . '/persona'); + $http_referer = is_referer_from_same_domain(); + + return array( 'php' => $php ? 'ok' : 'ko', 'minz' => $minz ? 'ok' : 'ko', 'curl' => $curl ? 'ok' : 'ko', @@ -601,44 +324,57 @@ function checkStep1 () { 'log' => $log ? 'ok' : 'ko', 'favicons' => $favicons ? 'ok' : 'ko', 'persona' => $persona ? 'ok' : 'ko', - 'all' => $php && $minz && $curl && $pdo && $pcre && $ctype && $dom && $data && $cache && $log && $favicons && $persona ? 'ok' : 'ko' + 'http_referer' => $http_referer ? 'ok' : 'ko', + 'all' => $php && $minz && $curl && $pdo && $pcre && $ctype && $dom && + $data && $cache && $log && $favicons && $persona && $http_referer ? + 'ok' : 'ko' ); } -function checkStep2 () { - $conf = !empty($_SESSION['salt']) && - !empty($_SESSION['title']) && +function checkStep2() { + $conf = !empty($_SESSION['title']) && !empty($_SESSION['old_entries']) && isset($_SESSION['mail_login']) && !empty($_SESSION['default_user']); + + $form = ( + isset($_SESSION['auth_type']) && + ($_SESSION['auth_type'] != 'form' || !empty($_SESSION['passwordHash'])) + ); + + $persona = ( + isset($_SESSION['auth_type']) && + ($_SESSION['auth_type'] != 'persona' || !empty($_SESSION['mail_login'])) + ); + $defaultUser = empty($_POST['default_user']) ? null : $_POST['default_user']; if ($defaultUser === null) { $defaultUser = empty($_SESSION['default_user']) ? '' : $_SESSION['default_user']; } $data = is_writable(DATA_PATH . '/' . $defaultUser . '_user.php'); - if ($data) { - @unlink(DATA_PATH . '/Configuration.array.php'); //v0.6 - } - return array ( + return array( 'conf' => $conf ? 'ok' : 'ko', + 'form' => $form ? 'ok' : 'ko', + 'persona' => $persona ? 'ok' : 'ko', 'data' => $data ? 'ok' : 'ko', - 'all' => $conf && $data ? 'ok' : 'ko' + 'all' => $conf && $form && $persona && $data ? 'ok' : 'ko' ); } -function checkStep3 () { + +function checkStep3() { $conf = is_writable(DATA_PATH . '/config.php'); - $bd = isset ($_SESSION['bd_type']) && - isset ($_SESSION['bd_host']) && - isset ($_SESSION['bd_user']) && - isset ($_SESSION['bd_password']) && - isset ($_SESSION['bd_base']) && - isset ($_SESSION['bd_prefix']) && - isset ($_SESSION['bd_error']); + $bd = isset($_SESSION['bd_type']) && + isset($_SESSION['bd_host']) && + isset($_SESSION['bd_user']) && + isset($_SESSION['bd_password']) && + isset($_SESSION['bd_base']) && + isset($_SESSION['bd_prefix']) && + isset($_SESSION['bd_error']); $conn = empty($_SESSION['bd_error']); - return array ( + return array( 'bd' => $bd ? 'ok' : 'ko', 'conn' => $conn ? 'ok' : 'ko', 'conf' => $conf ? 'ok' : 'ko', @@ -646,51 +382,41 @@ function checkStep3 () { ); } -function checkBD () { +function checkBD() { $ok = false; try { $str = ''; $driver_options = null; switch ($_SESSION['bd_type']) { - case 'mysql': - $driver_options = array( - PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8' - ); - - try { // on ouvre une connexion juste pour créer la base si elle n'existe pas - $str = 'mysql:host=' . $_SESSION['bd_host'] . ';'; - $c = new PDO ($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); - $sql = sprintf (SQL_CREATE_DB, $_SESSION['bd_base']); - $res = $c->query ($sql); - } catch (PDOException $e) { - } - - // on écrase la précédente connexion en sélectionnant la nouvelle BDD - $str = 'mysql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base']; - break; - case 'sqlite': - $str = 'sqlite:' . DATA_PATH . '/' . $_SESSION['default_user'] . '.sqlite'; - $driver_options = array( - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - ); - break; - default: - return false; - } - - $c = new PDO ($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); + case 'mysql': + $driver_options = array( + PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8' + ); - if ($_SESSION['bd_type'] !== 'sqlite') { //No SQL backup for SQLite - $stm = $c->prepare(SQL_SHOW_TABLES); - $stm->execute(); - $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); - if (in_array($_SESSION['bd_prefix'] . 'entry', $res) && !in_array($_SESSION['bd_prefix'] . 'entry006', $res)) { - $sql = sprintf(SQL_BACKUP006, $_SESSION['bd_prefix']); //v0.6 - $res = $c->query($sql); //Backup tables + try { // on ouvre une connexion juste pour créer la base si elle n'existe pas + $str = 'mysql:host=' . $_SESSION['bd_host'] . ';'; + $c = new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); + $sql = sprintf(SQL_CREATE_DB, $_SESSION['bd_base']); + $res = $c->query($sql); + } catch (PDOException $e) { } + + // on écrase la précédente connexion en sélectionnant la nouvelle BDD + $str = 'mysql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base']; + break; + case 'sqlite': + $str = 'sqlite:' . DATA_PATH . '/' . $_SESSION['default_user'] . '.sqlite'; + $driver_options = array( + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + ); + break; + default: + return false; } + $c = new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); + if (defined('SQL_CREATE_TABLES')) { $sql = sprintf(SQL_CREATE_TABLES, $_SESSION['bd_prefix_user'], _t('default_category')); $stm = $c->prepare($sql); @@ -719,20 +445,20 @@ function checkBD () { } /*** AFFICHAGE ***/ -function printStep0 () { +function printStep0() { global $actual; ?> - <?php $s0 = checkStep0 (); if ($s0['all'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('language_defined'); ?></p> + <?php $s0 = checkStep0(); if ($s0['all'] == 'ok') { ?> + <p class="alert alert-success"><span class="alert-head"><?php echo _t('ok'); ?></span> <?php echo _t('language_defined'); ?></p> <?php } ?> <form action="index.php?step=0" method="post"> - <legend><?php echo _t ('choose_language'); ?></legend> + <legend><?php echo _t('choose_language'); ?></legend> <div class="form-group"> - <label class="group-name" for="language"><?php echo _t ('language'); ?></label> + <label class="group-name" for="language"><?php echo _t('language'); ?></label> <div class="group-controls"> <select name="language" id="language"> - <?php $languages = availableLanguages (); ?> + <?php $languages = availableLanguages(); ?> <?php foreach ($languages as $short => $lib) { ?> <option value="<?php echo $short; ?>"<?php echo $actual == $short ? ' selected="selected"' : ''; ?>><?php echo $lib; ?></option> <?php } ?> @@ -742,10 +468,10 @@ function printStep0 () { <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t ('save'); ?></button> - <button type="reset" class="btn"><?php echo _t ('cancel'); ?></button> + <button type="submit" class="btn btn-important"><?php echo _t('save'); ?></button> + <button type="reset" class="btn"><?php echo _t('cancel'); ?></button> <?php if ($s0['all'] == 'ok') { ?> - <a class="btn btn-important next-step" href="?step=1"><?php echo _t ('next_step'); ?></a> + <a class="btn btn-important next-step" href="?step=1"><?php echo _t('next_step'); ?></a> <?php } ?> </div> </div> @@ -753,133 +479,144 @@ function printStep0 () { <?php } -function printStep1 () { - $res = checkStep1 (); +function printStep1() { + $res = checkStep1(); ?> - <noscript><p class="alert alert-warn"><span class="alert-head"><?php echo _t ('attention'); ?></span> <?php echo _t ('javascript_is_better'); ?></p></noscript> + <noscript><p class="alert alert-warn"><span class="alert-head"><?php echo _t('attention'); ?></span> <?php echo _t('javascript_is_better'); ?></p></noscript> <?php if ($res['php'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('php_is_ok', PHP_VERSION); ?></p> + <p class="alert alert-success"><span class="alert-head"><?php echo _t('ok'); ?></span> <?php echo _t('php_is_ok', PHP_VERSION); ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('php_is_nok', PHP_VERSION, '5.2.1'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?php echo _t('damn'); ?></span> <?php echo _t('php_is_nok', PHP_VERSION, '5.2.1'); ?></p> <?php } ?> <?php if ($res['minz'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('minz_is_ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?php echo _t('ok'); ?></span> <?php echo _t('minz_is_ok'); ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('minz_is_nok', LIB_PATH . '/Minz'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?php echo _t('damn'); ?></span> <?php echo _t('minz_is_nok', LIB_PATH . '/Minz'); ?></p> <?php } ?> <?php if ($res['pdo'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('pdo_is_ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?php echo _t('ok'); ?></span> <?php echo _t('pdo_is_ok'); ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('pdo_is_nok'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?php echo _t('damn'); ?></span> <?php echo _t('pdo_is_nok'); ?></p> <?php } ?> <?php if ($res['curl'] == 'ok') { ?> <?php $version = curl_version(); ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('curl_is_ok', $version['version']); ?></p> + <p class="alert alert-success"><span class="alert-head"><?php echo _t('ok'); ?></span> <?php echo _t('curl_is_ok', $version['version']); ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('curl_is_nok'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?php echo _t('damn'); ?></span> <?php echo _t('curl_is_nok'); ?></p> <?php } ?> <?php if ($res['pcre'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('pcre_is_ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?php echo _t('ok'); ?></span> <?php echo _t('pcre_is_ok'); ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('pcre_is_nok'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?php echo _t('damn'); ?></span> <?php echo _t('pcre_is_nok'); ?></p> <?php } ?> <?php if ($res['ctype'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('ctype_is_ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?php echo _t('ok'); ?></span> <?php echo _t('ctype_is_ok'); ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('ctype_is_nok'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?php echo _t('damn'); ?></span> <?php echo _t('ctype_is_nok'); ?></p> <?php } ?> <?php if ($res['dom'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('dom_is_ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?php echo _t('ok'); ?></span> <?php echo _t('dom_is_ok'); ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('dom_is_nok'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?php echo _t('damn'); ?></span> <?php echo _t('dom_is_nok'); ?></p> <?php } ?> <?php if ($res['data'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('data_is_ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?php echo _t('ok'); ?></span> <?php echo _t('data_is_ok'); ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('file_is_nok', DATA_PATH); ?></p> + <p class="alert alert-error"><span class="alert-head"><?php echo _t('damn'); ?></span> <?php echo _t('file_is_nok', DATA_PATH); ?></p> <?php } ?> <?php if ($res['cache'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('cache_is_ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?php echo _t('ok'); ?></span> <?php echo _t('cache_is_ok'); ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('file_is_nok', CACHE_PATH); ?></p> + <p class="alert alert-error"><span class="alert-head"><?php echo _t('damn'); ?></span> <?php echo _t('file_is_nok', CACHE_PATH); ?></p> <?php } ?> <?php if ($res['log'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('log_is_ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?php echo _t('ok'); ?></span> <?php echo _t('log_is_ok'); ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('file_is_nok', LOG_PATH); ?></p> + <p class="alert alert-error"><span class="alert-head"><?php echo _t('damn'); ?></span> <?php echo _t('file_is_nok', LOG_PATH); ?></p> <?php } ?> <?php if ($res['favicons'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('favicons_is_ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?php echo _t('ok'); ?></span> <?php echo _t('favicons_is_ok'); ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('file_is_nok', DATA_PATH . '/favicons'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?php echo _t('damn'); ?></span> <?php echo _t('file_is_nok', DATA_PATH . '/favicons'); ?></p> <?php } ?> <?php if ($res['persona'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('persona_is_ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?php echo _t('ok'); ?></span> <?php echo _t('persona_is_ok'); ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('file_is_nok', DATA_PATH . '/persona'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?php echo _t('damn'); ?></span> <?php echo _t('file_is_nok', DATA_PATH . '/persona'); ?></p> + <?php } ?> + + <?php if ($res['http_referer'] == 'ok') { ?> + <p class="alert alert-success"><span class="alert-head"><?php echo _t('ok'); ?></span> <?php echo _t('http_referer_is_ok'); ?></p> + <?php } else { ?> + <p class="alert alert-error"><span class="alert-head"><?php echo _t('damn'); ?></span> <?php echo _t('http_referer_is_nok'); ?></p> <?php } ?> <?php if ($res['all'] == 'ok') { ?> - <a class="btn btn-important next-step" href="?step=2"><?php echo _t ('next_step'); ?></a> + <a class="btn btn-important next-step" href="?step=2"><?php echo _t('next_step'); ?></a> <?php } else { ?> - <p class="alert alert-error"><?php echo _t ('fix_errors_before'); ?></p> + <p class="alert alert-error"><?php echo _t('fix_errors_before'); ?></p> <?php } ?> <?php } -function printStep2 () { +function printStep2() { ?> - <?php $s2 = checkStep2 (); if ($s2['all'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('general_conf_is_ok'); ?></p> + <?php $s2 = checkStep2(); if ($s2['all'] == 'ok') { ?> + <p class="alert alert-success"><span class="alert-head"><?php echo _t('ok'); ?></span> <?php echo _t('general_conf_is_ok'); ?></p> + <?php } elseif (!empty($_POST)) { ?> + <p class="alert alert-error"><?php echo _t('fix_errors_before'); ?></p> <?php } ?> <form action="index.php?step=2" method="post"> - <legend><?php echo _t ('general_configuration'); ?></legend> + <legend><?php echo _t('general_configuration'); ?></legend> <div class="form-group"> - <label class="group-name" for="title"><?php echo _t ('title'); ?></label> + <label class="group-name" for="title"><?php echo _t('title'); ?></label> <div class="group-controls"> - <input type="text" id="title" name="title" value="<?php echo isset ($_SESSION['title']) ? $_SESSION['title'] : _t ('freshrss'); ?>" /> + <input type="text" id="title" name="title" value="<?php echo isset($_SESSION['title']) ? $_SESSION['title'] : _t('freshrss'); ?>" /> </div> </div> <div class="form-group"> - <label class="group-name" for="old_entries"><?php echo _t ('delete_articles_every'); ?></label> + <label class="group-name" for="old_entries"><?php echo _t('delete_articles_every'); ?></label> <div class="group-controls"> - <input type="number" id="old_entries" name="old_entries" required="required" min="1" max="1200" value="<?php echo isset ($_SESSION['old_entries']) ? $_SESSION['old_entries'] : '3'; ?>" /> <?php echo _t ('month'); ?> + <input type="number" id="old_entries" name="old_entries" required="required" min="1" max="1200" value="<?php echo isset($_SESSION['old_entries']) ? $_SESSION['old_entries'] : '3'; ?>" /> <?php echo _t('month'); ?> </div> </div> <div class="form-group"> - <label class="group-name" for="default_user"><?php echo _t ('default_user'); ?></label> + <label class="group-name" for="default_user"><?php echo _t('default_user'); ?></label> <div class="group-controls"> - <input type="text" id="default_user" name="default_user" required="required" size="16" maxlength="16" pattern="[0-9a-zA-Z]{1,16}" value="<?php echo isset ($_SESSION['default_user']) ? $_SESSION['default_user'] : ''; ?>" placeholder="<?php echo httpAuthUser() == '' ? 'user1' : httpAuthUser(); ?>" /> + <input type="text" id="default_user" name="default_user" required="required" size="16" maxlength="16" pattern="[0-9a-zA-Z]{1,16}" value="<?php echo isset($_SESSION['default_user']) ? $_SESSION['default_user'] : ''; ?>" placeholder="<?php echo httpAuthUser() == '' ? 'user1' : httpAuthUser(); ?>" /> </div> </div> <div class="form-group"> <label class="group-name" for="auth_type"><?php echo _t('auth_type'); ?></label> <div class="group-controls"> - <select id="auth_type" name="auth_type" required="required"> - <?php if (!in_array($_SESSION['auth_type'], array('form', 'persona', 'http_auth', 'none'))) { ?> - <option selected="selected"></option> - <?php } ?> - <option value="form"<?php echo $_SESSION['auth_type'] === 'form' ? ' selected="selected"' : '', cryptAvailable() ? '' : ' disabled="disabled"'; ?>><?php echo _t('auth_form'); ?></option> - <option value="persona"<?php echo $_SESSION['auth_type'] === 'persona' ? ' selected="selected"' : ''; ?>><?php echo _t('auth_persona'); ?></option> - <option value="http_auth"<?php echo $_SESSION['auth_type'] === 'http_auth' ? ' selected="selected"' : '', httpAuthUser() == '' ? ' disabled="disabled"' : ''; ?>><?php echo _t('http_auth'); ?> (REMOTE_USER = '<?php echo httpAuthUser(); ?>')</option> - <option value="none"<?php echo $_SESSION['auth_type'] === 'none' ? ' selected="selected"' : ''; ?>><?php echo _t('auth_none'); ?></option> + <select id="auth_type" name="auth_type" required="required" onchange="auth_type_change(true)"> + <?php + function no_auth($auth_type) { + return !in_array($auth_type, array('form', 'persona', 'http_auth', 'none')); + } + $auth_type = isset($_SESSION['auth_type']) ? $_SESSION['auth_type'] : ''; + ?> + <option value="form"<?php echo $auth_type === 'form' || no_auth($auth_type) ? ' selected="selected"' : '', cryptAvailable() ? '' : ' disabled="disabled"'; ?>><?php echo _t('auth_form'); ?></option> + <option value="persona"<?php echo $auth_type === 'persona' ? ' selected="selected"' : ''; ?>><?php echo _t('auth_persona'); ?></option> + <option value="http_auth"<?php echo $auth_type === 'http_auth' ? ' selected="selected"' : '', httpAuthUser() == '' ? ' disabled="disabled"' : ''; ?>><?php echo _t('http_auth'); ?>(REMOTE_USER = '<?php echo httpAuthUser(); ?>')</option> + <option value="none"<?php echo $auth_type === 'none' ? ' selected="selected"' : ''; ?>><?php echo _t('auth_none'); ?></option> </select> </div> </div> @@ -887,25 +624,73 @@ function printStep2 () { <div class="form-group"> <label class="group-name" for="passwordPlain"><?php echo _t('password_form'); ?></label> <div class="group-controls"> - <input type="password" id="passwordPlain" name="passwordPlain" pattern=".{7,}" autocomplete="off" /> + <div class="stick"> + <input type="password" id="passwordPlain" name="passwordPlain" pattern=".{7,}" autocomplete="off" <?php echo $auth_type === 'form' ? ' required="required"' : ''; ?> /> + <a class="btn toggle-password" data-toggle="passwordPlain"><?php echo FreshRSS_Themes::icon('key'); ?></a> + </div> <noscript><b><?php echo _t('javascript_should_be_activated'); ?></b></noscript> </div> </div> <div class="form-group"> - <label class="group-name" for="mail_login"><?php echo _t ('persona_connection_email'); ?></label> + <label class="group-name" for="mail_login"><?php echo _t('persona_connection_email'); ?></label> <div class="group-controls"> - <input type="email" id="mail_login" name="mail_login" value="<?php echo isset ($_SESSION['mail_login']) ? $_SESSION['mail_login'] : ''; ?>" placeholder="alice@example.net" /> - <noscript><b><?php echo _t ('javascript_should_be_activated'); ?></b></noscript> + <input type="email" id="mail_login" name="mail_login" value="<?php echo isset($_SESSION['mail_login']) ? $_SESSION['mail_login'] : ''; ?>" placeholder="alice@example.net" <?php echo $auth_type === 'persona' ? ' required="required"' : ''; ?> /> + <noscript><b><?php echo _t('javascript_should_be_activated'); ?></b></noscript> </div> </div> + <script> + function toggle_password() { + var button = this; + var passwordField = document.getElementById(button.getAttribute('data-toggle')); + + passwordField.setAttribute('type', 'text'); + button.className += ' active'; + + setTimeout(function() { + passwordField.setAttribute('type', 'password'); + button.className = button.className.replace(/(?:^|\s)active(?!\S)/g , ''); + }, 2000); + + return false; + } + toggles = document.getElementsByClassName('toggle-password'); + for (var i = 0 ; i < toggles.length ; i++) { + toggles[i].addEventListener('click', toggle_password); + } + + function auth_type_change(focus) { + var auth_value = document.getElementById('auth_type').value, + password_input = document.getElementById('passwordPlain'), + mail_input = document.getElementById('mail_login'); + + if (auth_value === 'form') { + password_input.required = true; + mail_input.required = false; + if (focus) { + password_input.focus(); + } + } else if (auth_value === 'persona') { + password_input.required = false; + mail_input.required = true; + if (focus) { + mail_input.focus(); + } + } else { + password_input.required = false; + mail_input.required = false; + } + } + auth_type_change(false); + </script> + <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t ('save'); ?></button> - <button type="reset" class="btn"><?php echo _t ('cancel'); ?></button> + <button type="submit" class="btn btn-important"><?php echo _t('save'); ?></button> + <button type="reset" class="btn"><?php echo _t('cancel'); ?></button> <?php if ($s2['all'] == 'ok') { ?> - <a class="btn btn-important next-step" href="?step=3"><?php echo _t ('next_step'); ?></a> + <a class="btn btn-important next-step" href="?step=3"><?php echo _t('next_step'); ?></a> <?php } ?> </div> </div> @@ -913,29 +698,29 @@ function printStep2 () { <?php } -function printStep3 () { +function printStep3() { ?> - <?php $s3 = checkStep3 (); if ($s3['all'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('bdd_conf_is_ok'); ?></p> + <?php $s3 = checkStep3(); if ($s3['all'] == 'ok') { ?> + <p class="alert alert-success"><span class="alert-head"><?php echo _t('ok'); ?></span> <?php echo _t('bdd_conf_is_ok'); ?></p> <?php } elseif ($s3['conn'] == 'ko') { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('bdd_conf_is_ko'), (empty($_SESSION['bd_error']) ? '' : ' : ' . $_SESSION['bd_error']); ?></p> + <p class="alert alert-error"><span class="alert-head"><?php echo _t('damn'); ?></span> <?php echo _t('bdd_conf_is_ko'),(empty($_SESSION['bd_error']) ? '' : ' : ' . $_SESSION['bd_error']); ?></p> <?php } ?> <form action="index.php?step=3" method="post"> - <legend><?php echo _t ('bdd_configuration'); ?></legend> + <legend><?php echo _t('bdd_configuration'); ?></legend> <div class="form-group"> - <label class="group-name" for="type"><?php echo _t ('bdd_type'); ?></label> + <label class="group-name" for="type"><?php echo _t('bdd_type'); ?></label> <div class="group-controls"> <select name="type" id="type" onchange="mySqlShowHide()"> <?php if (extension_loaded('pdo_mysql')) {?> <option value="mysql" - <?php echo (isset($_SESSION['bd_type']) && $_SESSION['bd_type'] === 'mysql') ? 'selected="selected"' : ''; ?>> + <?php echo(isset($_SESSION['bd_type']) && $_SESSION['bd_type'] === 'mysql') ? 'selected="selected"' : ''; ?>> MySQL </option> <?php }?> <?php if (extension_loaded('pdo_sqlite')) {?> <option value="sqlite" - <?php echo (isset($_SESSION['bd_type']) && $_SESSION['bd_type'] === 'sqlite') ? 'selected="selected"' : ''; ?>> + <?php echo(isset($_SESSION['bd_type']) && $_SESSION['bd_type'] === 'sqlite') ? 'selected="selected"' : ''; ?>> SQLite </option> <?php }?> @@ -945,37 +730,37 @@ function printStep3 () { <div id="mysql"> <div class="form-group"> - <label class="group-name" for="host"><?php echo _t ('host'); ?></label> + <label class="group-name" for="host"><?php echo _t('host'); ?></label> <div class="group-controls"> - <input type="text" id="host" name="host" pattern="[0-9A-Za-z_.-]{1,64}" value="<?php echo isset ($_SESSION['bd_host']) ? $_SESSION['bd_host'] : 'localhost'; ?>" /> + <input type="text" id="host" name="host" pattern="[0-9A-Za-z_.-]{1,64}" value="<?php echo isset($_SESSION['bd_host']) ? $_SESSION['bd_host'] : 'localhost'; ?>" /> </div> </div> <div class="form-group"> - <label class="group-name" for="user"><?php echo _t ('username'); ?></label> + <label class="group-name" for="user"><?php echo _t('username'); ?></label> <div class="group-controls"> - <input type="text" id="user" name="user" maxlength="16" pattern="[0-9A-Za-z_.-]{1,16}" value="<?php echo isset ($_SESSION['bd_user']) ? $_SESSION['bd_user'] : ''; ?>" /> + <input type="text" id="user" name="user" maxlength="16" pattern="[0-9A-Za-z_.-]{1,16}" value="<?php echo isset($_SESSION['bd_user']) ? $_SESSION['bd_user'] : ''; ?>" /> </div> </div> <div class="form-group"> - <label class="group-name" for="pass"><?php echo _t ('password'); ?></label> + <label class="group-name" for="pass"><?php echo _t('password'); ?></label> <div class="group-controls"> - <input type="password" id="pass" name="pass" value="<?php echo isset ($_SESSION['bd_password']) ? $_SESSION['bd_password'] : ''; ?>" /> + <input type="password" id="pass" name="pass" value="<?php echo isset($_SESSION['bd_password']) ? $_SESSION['bd_password'] : ''; ?>" /> </div> </div> <div class="form-group"> - <label class="group-name" for="base"><?php echo _t ('bdd'); ?></label> + <label class="group-name" for="base"><?php echo _t('bdd'); ?></label> <div class="group-controls"> - <input type="text" id="base" name="base" maxlength="64" pattern="[0-9A-Za-z_]{1,64}" value="<?php echo isset ($_SESSION['bd_base']) ? $_SESSION['bd_base'] : ''; ?>" placeholder="freshrss" /> + <input type="text" id="base" name="base" maxlength="64" pattern="[0-9A-Za-z_]{1,64}" value="<?php echo isset($_SESSION['bd_base']) ? $_SESSION['bd_base'] : ''; ?>" placeholder="freshrss" /> </div> </div> <div class="form-group"> - <label class="group-name" for="prefix"><?php echo _t ('prefix'); ?></label> + <label class="group-name" for="prefix"><?php echo _t('prefix'); ?></label> <div class="group-controls"> - <input type="text" id="prefix" name="prefix" maxlength="16" pattern="[0-9A-Za-z_]{1,16}" value="<?php echo isset ($_SESSION['bd_prefix']) ? $_SESSION['bd_prefix'] : 'freshrss_'; ?>" /> + <input type="text" id="prefix" name="prefix" maxlength="16" pattern="[0-9A-Za-z_]{1,16}" value="<?php echo isset($_SESSION['bd_prefix']) ? $_SESSION['bd_prefix'] : 'freshrss_'; ?>" /> </div> </div> </div> @@ -988,10 +773,10 @@ function printStep3 () { <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t ('save'); ?></button> - <button type="reset" class="btn"><?php echo _t ('cancel'); ?></button> + <button type="submit" class="btn btn-important"><?php echo _t('save'); ?></button> + <button type="reset" class="btn"><?php echo _t('cancel'); ?></button> <?php if ($s3['all'] == 'ok') { ?> - <a class="btn btn-important next-step" href="?step=4"><?php echo _t ('next_step'); ?></a> + <a class="btn btn-important next-step" href="?step=4"><?php echo _t('next_step'); ?></a> <?php } ?> </div> </div> @@ -999,74 +784,40 @@ function printStep3 () { <?php } -function printStep4 () { +function printStep4() { ?> - <form action="index.php?step=4" method="post"> - <legend><?php echo _t ('version_update'); ?></legend> - - <?php if (updateDatabase(false)) { ?> - <p class="alert alert-warn"><?php echo _t ('update_long'); ?></p> - - <div class="form-group form-actions"> - <div class="group-controls"> - <input type="hidden" name="updateDatabase" value="1" /> - <button type="submit" class="btn btn-important"><?php echo _t ('update_start'); ?></button> - </div> - </div> - - <?php } else { ?> - <p class="alert alert-warn"><?php echo _t ('update_end'); ?></p> - - <div class="form-group form-actions"> - <div class="group-controls"> - <a class="btn btn-important next-step" href="?step=5"><?php echo _t ('next_step'); ?></a> - </div> - </div> - <?php } ?> - </form> -<?php -} - -function printStep5 () { -?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t ('congratulations'); ?></span> <?php echo _t ('installation_is_ok'); ?></p> - <a class="btn btn-important next-step" href="?step=6"><?php echo _t ('finish_installation'); ?></a> + <p class="alert alert-success"><span class="alert-head"><?php echo _t('congratulations'); ?></span> <?php echo _t('installation_is_ok'); ?></p> + <a class="btn btn-important next-step" href="?step=5"><?php echo _t('finish_installation'); ?></a> <?php } -function printStep6 () { +function printStep5() { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t ('oops'); ?></span> <?php echo _t ('install_not_deleted', DATA_PATH . '/do-install.txt'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?php echo _t('oops'); ?></span> <?php echo _t('install_not_deleted', DATA_PATH . '/do-install.txt'); ?></p> <?php } -checkStep (); +checkStep(); -initTranslate (); +initTranslate(); switch (STEP) { case 0: default: - saveLanguage (); + saveLanguage(); break; case 1: break; case 2: - saveStep2 (); + saveStep2(); break; case 3: - saveStep3 (); + saveStep3(); break; case 4: - if (!empty($_POST['updateDatabase'])) { - updateDatabase(true); - } break; case 5: - postUpdate(); - break; -case 6: - deleteInstall (); + deleteInstall(); break; } ?> @@ -1075,28 +826,27 @@ case 6: <head> <meta charset="utf-8"> <meta name="viewport" content="initial-scale=1.0"> - <title><?php echo _t ('freshrss_installation'); ?></title> - <link rel="stylesheet" type="text/css" media="all" href="../themes/Origine/template.css" /> + <title><?php echo _t('freshrss_installation'); ?></title> + <link rel="stylesheet" type="text/css" media="all" href="../themes/base-theme/template.css" /> <link rel="stylesheet" type="text/css" media="all" href="../themes/Origine/origine.css" /> </head> <body> <div class="header"> <div class="item title"> - <h1><a href="index.php"><?php echo _t ('freshrss'); ?></a></h1> - <h2><?php echo _t ('installation_step', STEP); ?></h2> + <h1><a href="index.php"><?php echo _t('freshrss'); ?></a></h1> + <h2><?php echo _t('installation_step', STEP); ?></h2> </div> </div> <div id="global"> <ul class="nav nav-list aside"> - <li class="nav-header"><?php echo _t ('steps'); ?></li> - <li class="item<?php echo STEP == 0 ? ' active' : ''; ?>"><a href="?step=0"><?php echo _t ('language'); ?></a></li> - <li class="item<?php echo STEP == 1 ? ' active' : ''; ?>"><a href="?step=1"><?php echo _t ('checks'); ?></a></li> - <li class="item<?php echo STEP == 2 ? ' active' : ''; ?>"><a href="?step=2"><?php echo _t ('general_configuration'); ?></a></li> - <li class="item<?php echo STEP == 3 ? ' active' : ''; ?>"><a href="?step=3"><?php echo _t ('bdd_configuration'); ?></a></li> - <li class="item<?php echo STEP == 4 ? ' active' : ''; ?>"><a href="?step=4"><?php echo _t ('version_update'); ?></a></li> - <li class="item<?php echo STEP == 5 ? ' active' : ''; ?>"><a href="?step=5"><?php echo _t ('this_is_the_end'); ?></a></li> + <li class="nav-header"><?php echo _t('steps'); ?></li> + <li class="item<?php echo STEP == 0 ? ' active' : ''; ?>"><a href="?step=0"><?php echo _t('language'); ?></a></li> + <li class="item<?php echo STEP == 1 ? ' active' : ''; ?>"><a href="?step=1"><?php echo _t('checks'); ?></a></li> + <li class="item<?php echo STEP == 2 ? ' active' : ''; ?>"><a href="?step=2"><?php echo _t('general_configuration'); ?></a></li> + <li class="item<?php echo STEP == 3 ? ' active' : ''; ?>"><a href="?step=3"><?php echo _t('bdd_configuration'); ?></a></li> + <li class="item<?php echo STEP == 4 ? ' active' : ''; ?>"><a href="?step=5"><?php echo _t('this_is_the_end'); ?></a></li> </ul> <div class="post"> @@ -1104,25 +854,22 @@ case 6: switch (STEP) { case 0: default: - printStep0 (); + printStep0(); break; case 1: - printStep1 (); + printStep1(); break; case 2: - printStep2 (); + printStep2(); break; case 3: - printStep3 (); + printStep3(); break; case 4: - printStep4 (); + printStep4(); break; case 5: - printStep5 (); - break; - case 6: - printStep6 (); + printStep5(); break; } ?> diff --git a/app/layout/aside_configure.phtml b/app/layout/aside_configure.phtml index e66f2f64c..d5c9bf4c9 100644 --- a/app/layout/aside_configure.phtml +++ b/app/layout/aside_configure.phtml @@ -1,25 +1,33 @@ <ul class="nav nav-list aside"> - <li class="nav-header"><?php echo Minz_Translate::t ('configuration'); ?></li> - <li class="item<?php echo Minz_Request::actionName () == 'display' ? ' active' : ''; ?>"> - <a href="<?php echo _url ('configure', 'display'); ?>"><?php echo Minz_Translate::t ('display_configuration'); ?></a> + <li class="nav-header"><?php echo _t('configuration'); ?></li> + <li class="item<?php echo Minz_Request::actionName() === 'display' ? ' active' : ''; ?>"> + <a href="<?php echo _url('configure', 'display'); ?>"><?php echo _t('display_configuration'); ?></a> </li> - <li class="item<?php echo Minz_Request::actionName () == 'reading' ? ' active' : ''; ?>"> - <a href="<?php echo _url ('configure', 'reading'); ?>"><?php echo Minz_Translate::t ('reading_configuration'); ?></a> + <li class="item<?php echo Minz_Request::actionName() === 'reading' ? ' active' : ''; ?>"> + <a href="<?php echo _url('configure', 'reading'); ?>"><?php echo _t('reading_configuration'); ?></a> </li> - <li class="item<?php echo Minz_Request::actionName () == 'archiving' ? ' active' : ''; ?>"> - <a href="<?php echo _url ('configure', 'archiving'); ?>"><?php echo Minz_Translate::t ('archiving_configuration'); ?></a> + <li class="item<?php echo Minz_Request::actionName() === 'archiving' ? ' active' : ''; ?>"> + <a href="<?php echo _url('configure', 'archiving'); ?>"><?php echo _t('archiving_configuration'); ?></a> </li> - <li class="item<?php echo Minz_Request::actionName () == 'sharing' ? ' active' : ''; ?>"> - <a href="<?php echo _url ('configure', 'sharing'); ?>"><?php echo Minz_Translate::t ('sharing'); ?></a> + <li class="item<?php echo Minz_Request::actionName() === 'sharing' ? ' active' : ''; ?>"> + <a href="<?php echo _url('configure', 'sharing'); ?>"><?php echo _t('sharing'); ?></a> </li> - <li class="item<?php echo Minz_Request::actionName () == 'shortcut' ? ' active' : ''; ?>"> - <a href="<?php echo _url ('configure', 'shortcut'); ?>"><?php echo Minz_Translate::t ('shortcuts'); ?></a> + <li class="item<?php echo Minz_Request::actionName() === 'shortcut' ? ' active' : ''; ?>"> + <a href="<?php echo _url('configure', 'shortcut'); ?>"><?php echo _t('shortcuts'); ?></a> </li> - <li class="item<?php echo Minz_Request::actionName () == 'queries' ? ' active' : ''; ?>"> - <a href="<?php echo _url ('configure', 'queries'); ?>"><?php echo Minz_Translate::t ('queries'); ?></a> + <li class="item<?php echo Minz_Request::actionName() === 'queries' ? ' active' : ''; ?>"> + <a href="<?php echo _url('configure', 'queries'); ?>"><?php echo _t('queries'); ?></a> </li> <li class="separator"></li> - <li class="item<?php echo Minz_Request::actionName () == 'users' ? ' active' : ''; ?>"> - <a href="<?php echo _url ('configure', 'users'); ?>"><?php echo Minz_Translate::t ('users'); ?></a> + <li class="item<?php echo Minz_Request::actionName() === 'users' ? ' active' : ''; ?>"> + <a href="<?php echo _url('configure', 'users'); ?>"><?php echo _t('users'); ?></a> </li> + <?php + $current_user = Minz_Session::param('currentUser', ''); + if (Minz_Configuration::isAdmin($current_user)) { + ?> + <li class="item<?php echo Minz_Request::controllerName() === 'update' ? ' active' : ''; ?>"> + <a href="<?php echo _url('update', 'index'); ?>"><?php echo _t('update'); ?></a> + </li> + <?php } ?> </ul> diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml index 432d6fdb7..aac3c0896 100644 --- a/app/layout/aside_flux.phtml +++ b/app/layout/aside_flux.phtml @@ -1,16 +1,18 @@ <div class="aside aside_flux<?php if ($this->conf->hide_read_feeds && ($this->state & FreshRSS_Entry::STATE_NOT_READ) && !($this->state & FreshRSS_Entry::STATE_READ)) echo ' state_unread'; ?>" id="aside_flux"> - <a class="toggle_aside" href="#close"><?php echo FreshRSS_Themes::icon('close'); ?></a> + <a class="toggle_aside" href="#close"><?php echo _i('close'); ?></a> <ul class="categories"> <?php if ($this->loginOk) { ?> + <form id="mark-read-aside" method="post" style="display: none"></form> + <li> - <div class="stick"> - <a class="btn btn-important" href="<?php echo _url ('configure', 'feed'); ?>"><?php echo Minz_Translate::t ('subscription_management'); ?></a> - <a class="btn btn-important" href="<?php echo _url ('configure', 'categorize'); ?>" title="<?php echo Minz_Translate::t ('categories_management'); ?>"><?php echo FreshRSS_Themes::icon('category-white'); ?></a> + <div class="stick configure-feeds"> + <a class="btn btn-important" href="<?php echo _url('configure', 'feed'); ?>"><?php echo _t('subscription_management'); ?></a> + <a class="btn btn-important" href="<?php echo _url('configure', 'categorize'); ?>" title="<?php echo _t('categories_management'); ?>"><?php echo _i('category-white'); ?></a> </div> </li> <?php } elseif (Minz_Configuration::needsLogin()) { ?> - <li><a href="<?php echo _url ('index', 'about'); ?>"><?php echo Minz_Translate::t ('about_freshrss'); ?></a></li> + <li><a href="<?php echo _url('index', 'about'); ?>"><?php echo _t('about_freshrss'); ?></a></li> <?php } ?> <?php @@ -22,8 +24,8 @@ <li> <div class="category all<?php echo $this->get_c == 'a' ? ' active' : ''; ?>"> <a data-unread="<?php echo formatNumber($this->nb_not_read); ?>" class="btn<?php echo $this->get_c == 'a' ? ' active' : ''; ?>" href="<?php echo Minz_Url::display($arUrl); ?>"> - <?php echo FreshRSS_Themes::icon('all'); ?> - <?php echo Minz_Translate::t ('main_stream'); ?> + <?php echo _i('all'); ?> + <?php echo _t('main_stream'); ?> </a> </div> </li> @@ -31,44 +33,42 @@ <li> <div class="category favorites<?php echo $this->get_c == 's' ? ' active' : ''; ?>"> <a data-unread="<?php echo formatNumber($this->nb_favorites['unread']); ?>" class="btn<?php echo $this->get_c == 's' ? ' active' : ''; ?>" href="<?php $arUrl['params']['get'] = 's'; echo Minz_Url::display($arUrl); ?>"> - <?php echo FreshRSS_Themes::icon('bookmark'); ?> - <?php echo Minz_Translate::t('favorite_feeds', formatNumber($this->nb_favorites['all'])); ?> + <?php echo _i('bookmark'); ?> + <?php echo _t('favorite_feeds', formatNumber($this->nb_favorites['all'])); ?> </a> </div> </li> <?php foreach ($this->cat_aside as $cat) { - $feeds = $cat->feeds (); - if (!empty ($feeds)) { + $feeds = $cat->feeds(); + if (!empty($feeds)) { $c_active = false; - if ($this->conf->display_categories) { - if ($this->get_c == $cat->id () && $this->get_f) { - $c_active = true; - } - } else { - if ($this->get_c == $cat->id ()) { - $c_active = true; + $c_show = false; + if ($this->get_c == $cat->id()) { + $c_active = true; + if (!$this->conf->display_categories || $this->get_f) { + $c_show = true; } } ?><li data-unread="<?php echo $cat->nbNotRead(); ?>"<?php if ($c_active) echo ' class="active"'; ?>><?php ?><div class="category stick<?php echo $c_active ? ' active' : ''; ?>"><?php - ?><a data-unread="<?php echo formatNumber($cat->nbNotRead()); ?>" class="btn<?php echo $c_active ? ' active' : ''; ?>" href="<?php $arUrl['params']['get'] = 'c_' . $cat->id(); echo Minz_Url::display($arUrl); ?>"><?php echo $cat->name (); ?></a><?php - ?><a class="btn dropdown-toggle" href="#"><?php echo FreshRSS_Themes::icon($c_active ? 'up' : 'down'); ?></a><?php + ?><a data-unread="<?php echo formatNumber($cat->nbNotRead()); ?>" class="btn<?php echo $c_active ? ' active' : ''; ?>" href="<?php $arUrl['params']['get'] = 'c_' . $cat->id(); echo Minz_Url::display($arUrl); ?>"><?php echo $cat->name(); ?></a><?php + ?><a class="btn dropdown-toggle" href="#"><?php echo _i($c_show ? 'up' : 'down'); ?></a><?php ?></div><?php - ?><ul class="feeds<?php echo $c_active ? ' active' : ''; ?>"><?php + ?><ul class="feeds<?php echo $c_show ? ' active' : ''; ?>"><?php foreach ($feeds as $feed) { - $feed_id = $feed->id (); - $nbEntries = $feed->nbEntries (); + $feed_id = $feed->id(); + $nbEntries = $feed->nbEntries(); $f_active = ($this->get_f == $feed_id); - ?><li id="f_<?php echo $feed_id; ?>" class="item<?php echo $f_active ? ' active' : ''; ?><?php echo $feed->inError () ? ' error' : ''; ?><?php echo $nbEntries == 0 ? ' empty' : ''; ?>" data-unread="<?php echo $feed->nbNotRead(); ?>"><?php + ?><li id="f_<?php echo $feed_id; ?>" class="item<?php echo $f_active ? ' active' : ''; ?><?php echo $feed->inError() ? ' error' : ''; ?><?php echo $nbEntries == 0 ? ' empty' : ''; ?>" data-unread="<?php echo $feed->nbNotRead(); ?>"><?php ?><div class="dropdown"><?php ?><div class="dropdown-target"></div><?php - ?><a class="dropdown-toggle" data-fweb="<?php echo $feed->website (); ?>"><?php echo FreshRSS_Themes::icon('configure'); ?></a><?php + ?><a class="dropdown-toggle" data-fweb="<?php echo $feed->website(); ?>"><?php echo _i('configure'); ?></a><?php /* feed_config_template */ ?></div><?php - ?> <img class="favicon" src="<?php echo $feed->favicon (); ?>" alt="✇" /> <?php - ?><a class="feed" data-unread="<?php echo formatNumber($feed->nbNotRead()); ?>" data-priority="<?php echo $feed->priority (); ?>" href="<?php $arUrl['params']['get'] = 'f_' . $feed_id; echo Minz_Url::display($arUrl); ?>"><?php echo $feed->name(); ?></a><?php + ?> <img class="favicon" src="<?php echo $feed->favicon(); ?>" alt="✇" /> <?php + ?><a class="feed" data-unread="<?php echo formatNumber($feed->nbNotRead()); ?>" data-priority="<?php echo $feed->priority(); ?>" href="<?php $arUrl['params']['get'] = 'f_' . $feed_id; echo Minz_Url::display($arUrl); ?>"><?php echo $feed->name(); ?></a><?php ?></li><?php } ?></ul><?php @@ -82,14 +82,22 @@ <script id="feed_config_template" type="text/html"> <ul class="dropdown-menu"> <li class="dropdown-close"><a href="#close">❌</a></li> - <li class="item"><a href="<?php echo _url ('index', 'index', 'get', 'f_!!!!!!'); ?>"><?php echo Minz_Translate::t ('filter'); ?></a></li> - <li class="item"><a href="<?php echo _url ('stats', 'repartition', 'id', '!!!!!!'); ?>"><?php echo Minz_Translate::t ('stats'); ?></a></li> - <li class="item"><a target="_blank" href="http://example.net/"><?php echo Minz_Translate::t ('see_website'); ?></a></li> + <li class="item"><a href="<?php echo _url('index', 'index', 'get', 'f_!!!!!!'); ?>"><?php echo _t('filter'); ?></a></li> + <?php if ($this->loginOk) { ?> + <li class="item"><a href="<?php echo _url('stats', 'repartition', 'id', '!!!!!!'); ?>"><?php echo _t('stats'); ?></a></li> + <?php } ?> + <li class="item"><a target="_blank" href="http://example.net/"><?php echo _t('see_website'); ?></a></li> <?php if ($this->loginOk) { ?> <li class="separator"></li> - <li class="item"><a href="<?php echo _url ('configure', 'feed', 'id', '!!!!!!'); ?>"><?php echo Minz_Translate::t ('administration'); ?></a></li> - <li class="item"><a href="<?php echo _url ('feed', 'actualize', 'id', '!!!!!!'); ?>"><?php echo Minz_Translate::t ('actualize'); ?></a></li> - <li class="item"><a href="<?php echo _url ('entry', 'read', 'get', 'f_!!!!!!'); ?>"><?php echo Minz_Translate::t ('mark_read'); ?></a></li> + <li class="item"><a href="<?php echo _url('configure', 'feed', 'id', '!!!!!!'); ?>"><?php echo _t('administration'); ?></a></li> + <li class="item"><a href="<?php echo _url('feed', 'actualize', 'id', '!!!!!!'); ?>"><?php echo _t('actualize'); ?></a></li> + <li class="item"> + <?php $confirm = $this->conf->reading_confirm ? 'confirm' : ''; ?> + <button class="read_all as-link <?php echo $confirm; ?>" + form="mark-read-aside" + formaction="<?php echo _url('entry', 'read', 'get', 'f_!!!!!!'); ?>" + type="submit"><?php echo _t('mark_read'); ?></button> + </li> <?php } ?> </ul> </script> diff --git a/app/layout/header.phtml b/app/layout/header.phtml index 2e42bfdea..028e63d8a 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -75,6 +75,12 @@ if (Minz_Configuration::canLogIn()) { <li class="item"><a href="<?php echo _url('configure', 'queries'); ?>"><?php echo _t('queries'); ?></a></li> <li class="separator"></li> <li class="item"><a href="<?php echo _url('configure', 'users'); ?>"><?php echo _t('users'); ?></a></li> + <?php + $current_user = Minz_Session::param('currentUser', ''); + if (Minz_Configuration::isAdmin($current_user)) { + ?> + <li class="item"><a href="<?php echo _url('update', 'index'); ?>"><?php echo _t('update'); ?></a></li> + <?php } ?> <li class="separator"></li> <li class="item"><a href="<?php echo _url('stats', 'index'); ?>"><?php echo _t('stats'); ?></a></li> <li class="item"><a href="<?php echo _url('index', 'logs'); ?>"><?php echo _t('logs'); ?></a></li> diff --git a/app/layout/layout.phtml b/app/layout/layout.phtml index 96a88d245..f95f45b5e 100644 --- a/app/layout/layout.phtml +++ b/app/layout/layout.phtml @@ -13,6 +13,7 @@ if (!empty($this->nextId)) { $params = Minz_Request::params(); $params['next'] = $this->nextId; + $params['ajax'] = 1; ?> <link id="prefetch" rel="next prefetch" href="<?php echo Minz_Url::display(array('c' => Minz_Request::controllerName(), 'a' => Minz_Request::actionName(), 'params' => $params)); ?>" /> <?php } ?> diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index 25833c16d..a9e6614e7 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -3,11 +3,11 @@ ?> <div class="nav_menu"> <?php if ($actual_view === 'normal') { ?> - <a class="btn toggle_aside" href="#aside_flux"><?php echo FreshRSS_Themes::icon('category'); ?></a> + <a class="btn toggle_aside" href="#aside_flux"><?php echo _i('category'); ?></a> <?php } ?> <?php if ($this->loginOk) { ?> - <div class="stick"> + <div id="nav_menu_actions" class="stick"> <?php $url_state = $this->url; @@ -24,9 +24,9 @@ <a id="toggle-read" class="btn <?php echo $class; ?>" aria-checked="<?php echo $checked; ?>" - href="<?php echo Minz_Url::display ($url_state); ?>" - title="<?php echo Minz_Translate::t ('show_read'); ?>"> - <?php echo FreshRSS_Themes::icon('read'); ?> + href="<?php echo Minz_Url::display($url_state); ?>" + title="<?php echo _t('show_read'); ?>"> + <?php echo _i('read'); ?> </a> <?php @@ -43,13 +43,13 @@ <a id="toggle-unread" class="btn <?php echo $class; ?>" aria-checked="<?php echo $checked; ?>" - href="<?php echo Minz_Url::display ($url_state); ?>" - title="<?php echo Minz_Translate::t ('show_not_reads'); ?>"> - <?php echo FreshRSS_Themes::icon('unread'); ?> + href="<?php echo Minz_Url::display($url_state); ?>" + title="<?php echo _t('show_not_reads'); ?>"> + <?php echo _i('unread'); ?> </a> <?php - if ($this->state & FreshRSS_Entry::STATE_FAVORITE) { + if ($this->state & FreshRSS_Entry::STATE_FAVORITE || $this->get_c == 's') { $url_state['params']['state'] = $this->state & ~FreshRSS_Entry::STATE_FAVORITE; $checked = 'true'; $class = 'active'; @@ -62,9 +62,9 @@ <a id="toggle-favorite" class="btn <?php echo $class; ?>" aria-checked="<?php echo $checked; ?>" - href="<?php echo Minz_Url::display ($url_state); ?>" - title="<?php echo Minz_Translate::t ('show_favorite'); ?>"> - <?php echo FreshRSS_Themes::icon('starred'); ?> + href="<?php echo Minz_Url::display($url_state); ?>" + title="<?php echo _t('show_favorite'); ?>"> + <?php echo _i('starred'); ?> </a> <?php @@ -81,22 +81,25 @@ <a id="toggle-not-favorite" class="btn <?php echo $class; ?>" aria-checked="<?php echo $checked; ?>" - href="<?php echo Minz_Url::display ($url_state); ?>" - title="<?php echo Minz_Translate::t ('show_not_favorite'); ?>"> - <?php echo FreshRSS_Themes::icon('non-starred'); ?> + href="<?php echo Minz_Url::display($url_state); ?>" + title="<?php echo _t('show_not_favorite'); ?>"> + <?php echo _i('non-starred'); ?> </a> <div class="dropdown"> <div id="dropdown-query" class="dropdown-target"></div> - <a class="dropdown-toggle btn" href="#dropdown-query"><?php echo FreshRSS_Themes::icon('down'); ?></a> + <a class="dropdown-toggle btn" href="#dropdown-query"><?php echo _i('down'); ?></a> <ul class="dropdown-menu"> <li class="dropdown-close"><a href="#close">❌</a></li> - <li class="dropdown-header"><?php echo Minz_Translate::t('queries'); ?> <a class="no-mobile" href="<?php echo _url('configure', 'queries'); ?>"><?php echo FreshRSS_Themes::icon('configure'); ?></a></li> + <li class="dropdown-header"> + <?php echo _t('queries'); ?> + <a class="no-mobile" href="<?php echo _url('configure', 'queries'); ?>"><?php echo _i('configure'); ?></a> + </li> <?php foreach ($this->conf->queries as $query) { ?> - <li class="item"> + <li class="item query"> <a href="<?php echo $query['url']; ?>"><?php echo $query['name']; ?></a> </li> <?php } ?> @@ -110,58 +113,58 @@ $url_query['c'] = 'configure'; $url_query['a'] = 'addQuery'; ?> - <li class="item no-mobile"><a href="<?php echo Minz_Url::display($url_query); ?>"><?php echo FreshRSS_Themes::icon('bookmark-add'); ?> <?php echo Minz_Translate::t('add_query'); ?></a></li> + <li class="item no-mobile"><a href="<?php echo Minz_Url::display($url_query); ?>"><?php echo _i('bookmark-add'); ?> <?php echo _t('add_query'); ?></a></li> </ul> </div> </div> <?php $get = false; - $string_mark = Minz_Translate::t ('mark_all_read'); + $string_mark = _t('mark_all_read'); if ($this->get_f) { $get = 'f_' . $this->get_f; - $string_mark = Minz_Translate::t ('mark_feed_read'); + $string_mark = _t('mark_feed_read'); } elseif ($this->get_c && $this->get_c != 'a') { if ($this->get_c === 's') { $get = 's'; } else { $get = 'c_' . $this->get_c; } - $string_mark = Minz_Translate::t ('mark_cat_read'); + $string_mark = _t('mark_cat_read'); } $nextGet = $get; - if ($this->conf->onread_jump_next && (strlen ($get) > 2)) { + if ($this->conf->onread_jump_next && strlen($get) > 2) { $anotherUnreadId = ''; $foundCurrent = false; switch ($get[0]) { - case 'c': - foreach ($this->cat_aside as $cat) { - if ($cat->id () == $this->get_c) { - $foundCurrent = true; - continue; - } - if ($cat->nbNotRead () <= 0) continue; - $anotherUnreadId = $cat->id (); - if ($foundCurrent) break; + case 'c': + foreach ($this->cat_aside as $cat) { + if ($cat->id() == $this->get_c) { + $foundCurrent = true; + continue; } - $nextGet = empty ($anotherUnreadId) ? 'a' : 'c_' . $anotherUnreadId; - break; - case 'f': - foreach ($this->cat_aside as $cat) { - if ($cat->id () == $this->get_c) { - foreach ($cat->feeds () as $feed) { - if ($feed->id () == $this->get_f) { - $foundCurrent = true; - continue; - } - if ($feed->nbNotRead () <= 0) continue; - $anotherUnreadId = $feed->id (); - if ($foundCurrent) break; + if ($cat->nbNotRead() <= 0) continue; + $anotherUnreadId = $cat->id(); + if ($foundCurrent) break; + } + $nextGet = empty($anotherUnreadId) ? 'a' : 'c_' . $anotherUnreadId; + break; + case 'f': + foreach ($this->cat_aside as $cat) { + if ($cat->id() == $this->get_c) { + foreach ($cat->feeds() as $feed) { + if ($feed->id() == $this->get_f) { + $foundCurrent = true; + continue; } - break; + if ($feed->nbNotRead() <= 0) continue; + $anotherUnreadId = $feed->id(); + if ($foundCurrent) break; } + break; } - $nextGet = empty ($anotherUnreadId) ? 'c_' . $this->get_c : 'f_' . $anotherUnreadId; - break; + } + $nextGet = empty($anotherUnreadId) ? 'c_' . $this->get_c : 'f_' . $anotherUnreadId; + break; } } @@ -177,30 +180,54 @@ $arUrl = array('c' => 'entry', 'a' => 'read', 'params' => array('get' => $get, 'nextGet' => $nextGet, 'idMax' => $idMax)); $output = Minz_Request::param('output', ''); - if (($output != '') && ($this->conf->view_mode !== $output)) { + if ($output != '' && $this->conf->view_mode !== $output) { $arUrl['params']['output'] = $output; } $markReadUrl = Minz_Url::display($arUrl); - Minz_Session::_param ('markReadUrl', $markReadUrl); + Minz_Session::_param('markReadUrl', $markReadUrl); ?> + <form id="mark-read-menu" method="post" style="display: none"></form> + <div class="stick" id="nav_menu_read_all"> - <a class="read_all btn<?php if ($this->conf->reading_confirm) {echo ' confirm';} ?>" href="<?php echo $markReadUrl; ?>"><?php echo Minz_Translate::t ('mark_read'); ?></a> + <?php $confirm = $this->conf->reading_confirm ? 'confirm' : ''; ?> + <button class="read_all btn <?php echo $confirm; ?>" + form="mark-read-menu" + formaction="<?php echo $markReadUrl; ?>" + type="submit"><?php echo _t('mark_read'); ?></button> + <div class="dropdown"> <div id="dropdown-read" class="dropdown-target"></div> - <a class="dropdown-toggle btn" href="#dropdown-read"><?php echo FreshRSS_Themes::icon('down'); ?></a> + <a class="dropdown-toggle btn" href="#dropdown-read"><?php echo _i('down'); ?></a> <ul class="dropdown-menu"> <li class="dropdown-close"><a href="#close">❌</a></li> - <li class="item"><a href="<?php echo $markReadUrl; ?>"><?php echo $string_mark; ?></a></li> + <li class="item"> + <button class="as-link <?php echo $confirm; ?>" + form="mark-read-menu" + formaction="<?php echo $markReadUrl; ?>" + type="submit"><?php echo $string_mark; ?></button> + </li> <li class="separator"></li> <?php - $today = $this->today; - $one_week = $today - 604800; + $mark_before_today = $arUrl; + $mark_before_today['params']['idMax'] = $this->today . '000000'; + $mark_before_one_week = $arUrl; + $mark_before_one_week['params']['idMax'] = ($this->today - 604800) . '000000'; ?> - <li class="item"><a href="<?php echo _url ('entry', 'read', 'is_read', 1, 'get', $get, 'idMax', $today . '000000'); ?>"><?php echo Minz_Translate::t ('before_one_day'); ?></a></li> - <li class="item"><a href="<?php echo _url ('entry', 'read', 'is_read', 1, 'get', $get, 'idMax', $one_week . '000000'); ?>"><?php echo Minz_Translate::t ('before_one_week'); ?></a></li> + <li class="item"> + <button class="as-link <?php echo $confirm; ?>" + form="mark-read-menu" + formaction="<?php echo Minz_Url::display($mark_before_today); ?>" + type="submit"><?php echo _t('before_one_day'); ?></button> + </li> + <li class="item"> + <button class="as-link <?php echo $confirm; ?>" + form="mark-read-menu" + formaction="<?php echo Minz_Url::display($mark_before_one_week); ?>" + type="submit"><?php echo _t('before_one_week'); ?></button> + </li> </ul> </div> </div> @@ -209,18 +236,18 @@ <?php $url_output = $this->url; ?> <div class="stick" id="nav_menu_views"> <?php $url_output['params']['output'] = 'normal'; ?> - <a class="view_normal btn <?php echo $actual_view == 'normal'? 'active' : ''; ?>" title="<?php echo Minz_Translate::t('normal_view'); ?>" href="<?php echo Minz_Url::display($url_output); ?>"> - <?php echo FreshRSS_Themes::icon("view-normal"); ?> + <a class="view_normal btn <?php echo $actual_view == 'normal'? 'active' : ''; ?>" title="<?php echo _t('normal_view'); ?>" href="<?php echo Minz_Url::display($url_output); ?>"> + <?php echo _i("view-normal"); ?> </a> <?php $url_output['params']['output'] = 'global'; ?> - <a class="view_global btn <?php echo $actual_view == 'global'? 'active' : ''; ?>" title="<?php echo Minz_Translate::t('global_view'); ?>" href="<?php echo Minz_Url::display($url_output); ?>"> - <?php echo FreshRSS_Themes::icon("view-global"); ?> + <a class="view_global btn <?php echo $actual_view == 'global'? 'active' : ''; ?>" title="<?php echo _t('global_view'); ?>" href="<?php echo Minz_Url::display($url_output); ?>"> + <?php echo _i("view-global"); ?> </a> <?php $url_output['params']['output'] = 'reader'; ?> - <a class="view_reader btn <?php echo $actual_view == 'reader'? 'active' : ''; ?>" title="<?php echo Minz_Translate::t('reader_view'); ?>" href="<?php echo Minz_Url::display($url_output); ?>"> - <?php echo FreshRSS_Themes::icon("view-reader"); ?> + <a class="view_reader btn <?php echo $actual_view == 'reader'? 'active' : ''; ?>" title="<?php echo _t('reader_view'); ?>" href="<?php echo Minz_Url::display($url_output); ?>"> + <?php echo _i("view-reader"); ?> </a> <?php @@ -229,27 +256,27 @@ $url_output['params']['token'] = $this->conf->token; } ?> - <a class="view_rss btn" target="_blank" title="<?php echo Minz_Translate::t ('rss_view'); ?>" href="<?php echo Minz_Url::display($url_output); ?>"> - <?php echo FreshRSS_Themes::icon('rss'); ?> + <a class="view_rss btn" target="_blank" title="<?php echo _t('rss_view'); ?>" href="<?php echo Minz_Url::display($url_output); ?>"> + <?php echo _i('rss'); ?> </a> </div> <div class="item search"> - <form action="<?php echo _url ('index', 'index'); ?>" method="get"> - <?php $search = Minz_Request::param ('search', ''); ?> - <input type="search" name="search" class="extend" value="<?php echo $search; ?>" placeholder="<?php echo Minz_Translate::t ('search_short'); ?>" /> + <form action="<?php echo _url('index', 'index'); ?>" method="get"> + <?php $search = Minz_Request::param('search', ''); ?> + <input type="search" name="search" class="extend" value="<?php echo $search; ?>" placeholder="<?php echo _t('search_short'); ?>" /> - <?php $get = Minz_Request::param ('get', ''); ?> + <?php $get = Minz_Request::param('get', ''); ?> <?php if($get != '') { ?> <input type="hidden" name="get" value="<?php echo $get; ?>" /> <?php } ?> - <?php $order = Minz_Request::param ('order', ''); ?> + <?php $order = Minz_Request::param('order', ''); ?> <?php if($order != '') { ?> <input type="hidden" name="order" value="<?php echo $order; ?>" /> <?php } ?> - <?php $state = Minz_Request::param ('state', ''); ?> + <?php $state = Minz_Request::param('state', ''); ?> <?php if($state != '') { ?> <input type="hidden" name="state" value="<?php echo $state; ?>" /> <?php } ?> @@ -269,11 +296,11 @@ $url_order = $this->url; $url_order['params']['order'] = $order; ?> - <a class="btn" href="<?php echo Minz_Url::display ($url_order); ?>" title="<?php echo Minz_Translate::t ($title); ?>"> - <?php echo FreshRSS_Themes::icon($icon); ?> + <a id="toggle-order" class="btn" href="<?php echo Minz_Url::display($url_order); ?>" title="<?php echo _t($title); ?>"> + <?php echo _i($icon); ?> </a> <?php if ($this->loginOk || Minz_Configuration::allowAnonymousRefresh()) { ?> - <a id="actualize" class="btn" href="<?php echo _url ('feed', 'actualize'); ?>"><?php echo FreshRSS_Themes::icon('refresh'); ?></a> + <a id="actualize" class="btn" href="<?php echo _url('feed', 'actualize'); ?>"><?php echo _i('refresh'); ?></a> <?php } ?> </div> diff --git a/app/views/configure/categorize.phtml b/app/views/configure/categorize.phtml index 9bae99b39..23d1c9fa1 100644 --- a/app/views/configure/categorize.phtml +++ b/app/views/configure/categorize.phtml @@ -18,7 +18,13 @@ <input type="text" id="cat_<?php echo $cat->id (); ?>" name="categories[]" value="<?php echo $cat->name (); ?>" /> <?php if ($cat->nbFeed () > 0) { ?> - <button type="submit" class="btn btn-attention confirm" formaction="<?php echo _url ('feed', 'delete', 'id', $cat->id (), 'type', 'category'); ?>"><?php echo Minz_Translate::t ('ask_empty'); ?></button> + <a class="btn" href="<?php echo _url('index', 'index', 'get', 'c_' . $cat->id ()); ?>"> + <?php echo _i('link'); ?> + </a> + <button formaction="<?php echo _url('feed', 'delete', 'id', $cat->id (), 'type', 'category'); ?>" + class="btn btn-attention confirm" + data-str-confirm="<?php echo _t('confirm_action_feed_cat'); ?>" + type="submit"><?php echo _t('ask_empty'); ?></button> <?php } ?> </div> (<?php echo Minz_Translate::t ('number_feeds', $cat->nbFeed ()); ?>) diff --git a/app/views/configure/display.phtml b/app/views/configure/display.phtml index 955fc6747..8eb3a156b 100644 --- a/app/views/configure/display.phtml +++ b/app/views/configure/display.phtml @@ -91,6 +91,13 @@ </tbody> </table><br /> </div> + + <div class="form-group"> + <label class="group-name" for="posts_per_page"><?php echo Minz_Translate::t ('html5_notif_timeout'); ?></label> + <div class="group-controls"> + <input type="number" id="html5_notif_timeout" name="html5_notif_timeout" value="<?php echo $this->conf->html5_notif_timeout; ?>" /> <?php echo Minz_Translate::t ('seconds_(0_means_no_timeout)'); ?> + </div> + </div> <div class="form-group form-actions"> <div class="group-controls"> diff --git a/app/views/configure/feed.phtml b/app/views/configure/feed.phtml index a8dd9a8cb..e96a28739 100644 --- a/app/views/configure/feed.phtml +++ b/app/views/configure/feed.phtml @@ -70,27 +70,33 @@ </label> </div> </div> + <div class="form-group"> + <div class="group-controls"> + <a href="<?php echo _url('stats', 'repartition', 'id', $this->flux->id()); ?>"> + <?php echo _i('stats'); ?> <?php echo _t('stats'); ?> + </a> + </div> + </div> <div class="form-group form-actions"> <div class="group-controls"> - <button class="btn btn-important"><?php echo Minz_Translate::t ('save'); ?></button> - <button class="btn btn-attention confirm" formmethod="post" formaction="<?php echo Minz_Url::display (array ('c' => 'feed', 'a' => 'delete', 'params' => array ('id' => $this->flux->id ()))); ?>"><?php echo Minz_Translate::t ('delete'); ?></button> + <button class="btn btn-important"><?php echo _t('save'); ?></button> + <button class="btn btn-attention confirm" + data-str-confirm="<?php echo _t('confirm_action_feed_cat'); ?>" + formaction="<?php echo _url('feed', 'delete', 'id', $this->flux->id ()); ?>" + formmethod="post"><?php echo _t('delete'); ?></button> </div> </div> <legend><?php echo Minz_Translate::t ('archiving_configuration'); ?></legend> <div class="form-group"> - <label class="group-name"></label> <div class="group-controls"> - <a class="btn" href="<?php echo _url ('feed', 'actualize', 'id', $this->flux->id ()); ?>"> - <?php echo FreshRSS_Themes::icon('refresh'); ?> <?php echo Minz_Translate::t('actualize'); ?> - </a> - </div> - </div> - <div class="form-group"> - <label class="group-name"><?php echo Minz_Translate::t ('number_articles'); ?></label> - <div class="group-controls"> - <span class="control"><?php echo $nbEntries; ?></span> + <div class="stick"> + <input type="text" value="<?php echo _t('number_articles', $nbEntries); ?>" disabled="disabled" /> + <a class="btn" href="<?php echo _url('feed', 'actualize', 'id', $this->flux->id ()); ?>"> + <?php echo _i('refresh'); ?> <?php echo _t('actualize'); ?> + </a> + </div> </div> </div> <div class="form-group"> diff --git a/app/views/configure/queries.phtml b/app/views/configure/queries.phtml index 2895f584c..e778ce078 100644 --- a/app/views/configure/queries.phtml +++ b/app/views/configure/queries.phtml @@ -42,30 +42,37 @@ + (isset($query['get']) ? 1 : 0); // If the only filter is "all" articles, we consider there is no filter $exist = ($exist === 1 && isset($query['get']) && $query['get'] === 'a') ? 0 : $exist; + + $deprecated = (isset($this->query_get[$key]) && + $this->query_get[$key]['deprecated']); ?> <?php if ($exist === 0) { ?> <div class="alert alert-warn"> <div class="alert-head"><?php echo _t('no_query_filter'); ?></div> </div> + <?php } elseif ($deprecated) { ?> + <div class="alert alert-error"> + <div class="alert-head"><?php echo _t('query_deprecated'); ?></div> + </div> <?php } else { ?> <div class="alert alert-success"> <div class="alert-head"><?php echo _t('query_filter'); ?></div> <ul> - <?php if (isset($query['search'])) { $exist = true; ?> + <?php if (isset($query['search'])) { ?> <li class="item"><?php echo _t('query_search', $query['search']); ?></li> <?php } ?> - <?php if (isset($query['state'])) { $exist = true; ?> + <?php if (isset($query['state'])) { ?> <li class="item"><?php echo _t('query_state_' . $query['state']); ?></li> <?php } ?> - <?php if (isset($query['order'])) { $exist = true; ?> + <?php if (isset($query['order'])) { ?> <li class="item"><?php echo _t('query_order_' . strtolower($query['order'])); ?></li> <?php } ?> - <?php if (isset($query['get'])) { $exist = true; ?> + <?php if (isset($query['get'])) { ?> <li class="item"><?php echo _t('query_get_' . $this->query_get[$key]['type'], $this->query_get[$key]['name']); ?></li> <?php } ?> </ul> diff --git a/app/views/configure/reading.phtml b/app/views/configure/reading.phtml index 5a26501a4..8b2da2a28 100644 --- a/app/views/configure/reading.phtml +++ b/app/views/configure/reading.phtml @@ -10,6 +10,7 @@ <label class="group-name" for="posts_per_page"><?php echo Minz_Translate::t ('articles_per_page'); ?></label> <div class="group-controls"> <input type="number" id="posts_per_page" name="posts_per_page" value="<?php echo $this->conf->posts_per_page; ?>" min="5" max="50" /> + <?php echo _i('help'); ?> <?php echo _t('number_divided_when_reader'); ?> </div> </div> @@ -31,14 +32,17 @@ <option value="reader"<?php echo $this->conf->view_mode === 'reader' ? ' selected="selected"' : ''; ?>><?php echo Minz_Translate::t ('reader_view'); ?></option> <option value="global"<?php echo $this->conf->view_mode === 'global' ? ' selected="selected"' : ''; ?>><?php echo Minz_Translate::t ('global_view'); ?></option> </select> - <label class="radio" for="radio_all"> - <input type="radio" name="default_view" id="radio_all" value="<?php echo FreshRSS_Entry::STATE_ALL; ?>"<?php echo $this->conf->default_view === FreshRSS_Entry::STATE_ALL ? ' checked="checked"' : ''; ?> /> - <?php echo Minz_Translate::t ('show_all_articles'); ?> - </label> - <label class="radio" for="radio_not_read"> - <input type="radio" name="default_view" id="radio_not_read" value="<?php echo FreshRSS_Entry::STATE_NOT_READ; ?>"<?php echo $this->conf->default_view === FreshRSS_Entry::STATE_NOT_READ ? ' checked="checked"' : ''; ?> /> - <?php echo Minz_Translate::t ('show_not_reads'); ?> - </label> + </div> + </div> + + <div class="form-group"> + <label class="group-name" for="view_mode"><?php echo _t('articles_to_display'); ?></label> + <div class="group-controls"> + <select name="default_view" id="default_view"> + <option value="<?php echo FreshRSS_Entry::STATE_NOT_READ; ?>"<?php echo $this->conf->default_view === FreshRSS_Entry::STATE_NOT_READ ? ' selected="selected"' : ''; ?>><?php echo _t('show_adaptive'); ?></option> + <option value="<?php echo FreshRSS_Entry::STATE_ALL; ?>"<?php echo $this->conf->default_view === FreshRSS_Entry::STATE_ALL ? ' selected="selected"' : ''; ?>><?php echo _t('show_all_articles'); ?></option> + <option value="<?php echo FreshRSS_Entry::STATE_STRICT + FreshRSS_Entry::STATE_NOT_READ; ?>"<?php echo $this->conf->default_view === FreshRSS_Entry::STATE_STRICT + FreshRSS_Entry::STATE_NOT_READ ? ' selected="selected"' : ''; ?>><?php echo _t('show_not_reads'); ?></option> + </select> </div> </div> diff --git a/app/views/configure/shortcut.phtml b/app/views/configure/shortcut.phtml index bfb13f003..a4029b676 100644 --- a/app/views/configure/shortcut.phtml +++ b/app/views/configure/shortcut.phtml @@ -103,6 +103,21 @@ </div> </div> + <div class="form-group"> + <label class="group-name" for="user_filter_shortcut"><?php echo Minz_Translate::t ('user_filter'); ?></label> + <div class="group-controls"> + <input type="text" id="user_filter_shortcut" name="shortcuts[user_filter]" list="keys" value="<?php echo $s['user_filter']; ?>" /> + <?php echo Minz_Translate::t ('user_filter_help'); ?> + </div> + </div> + + <div class="form-group"> + <label class="group-name" for="help_shortcut"><?php echo Minz_Translate::t ('help'); ?></label> + <div class="group-controls"> + <input type="text" id="help_shortcut" name="shortcuts[help]" list="keys" value="<?php echo $s['help']; ?>" /> + </div> + </div> + <div class="form-group form-actions"> <div class="group-controls"> <button type="submit" class="btn btn-important"><?php echo Minz_Translate::t ('save'); ?></button> diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml index 7144c519a..1139eb446 100644 --- a/app/views/helpers/javascript_vars.phtml +++ b/app/views/helpers/javascript_vars.phtml @@ -4,7 +4,8 @@ echo '"use strict";', "\n"; $mark = $this->conf->mark_when; echo 'var ', - 'hide_posts=', ($this->conf->display_posts || Minz_Request::param('output') === 'reader') ? 'false' : 'true', + 'help_url="', FRESHRSS_WIKI, '"', + ',hide_posts=', ($this->conf->display_posts || Minz_Request::param('output') === 'reader') ? 'false' : 'true', ',display_order="', Minz_Request::param('order', $this->conf->sort_order), '"', ',auto_mark_article=', $mark['article'] ? 'true' : 'false', ',auto_mark_site=', $mark['site'] ? 'true' : 'false', @@ -25,7 +26,9 @@ echo ',shortcuts={', 'collapse_entry:"', $s['collapse_entry'], '",', 'load_more:"', $s['load_more'], '",', 'auto_share:"', $s['auto_share'], '",', - 'focus_search:"', $s['focus_search'], '"', + 'focus_search:"', $s['focus_search'], '",', + 'user_filter:"', $s['user_filter'], '",', + 'help:"', $s['help'], '"', "},\n"; if (Minz_Request::param ('output') === 'global') { @@ -48,9 +51,11 @@ echo 'authType="', $authType, '",', 'url_login="', _url ('index', 'login'), '",', 'url_logout="', _url ('index', 'logout'), '",'; -echo 'str_confirmation="', Minz_Translate::t('confirm_action'), '"', ",\n"; +echo 'str_confirmation_default="', Minz_Translate::t('confirm_action'), '"', ",\n"; echo 'str_notif_title_articles="', Minz_Translate::t('notif_title_new_articles'), '"', ",\n"; echo 'str_notif_body_articles="', Minz_Translate::t('notif_body_new_articles'), '"', ",\n"; +echo 'html5_notif_timeout=', $this->conf->html5_notif_timeout,",\n"; + $autoActualise = Minz_Session::param('actualize_feeds', false); echo 'auto_actualize_feeds=', $autoActualise ? 'true' : 'false', ";\n"; diff --git a/app/views/helpers/pagination.phtml b/app/views/helpers/pagination.phtml index f237e1f3e..cea338364 100755 --- a/app/views/helpers/pagination.phtml +++ b/app/views/helpers/pagination.phtml @@ -1,26 +1,37 @@ <?php - $c = Minz_Request::controllerName (); - $a = Minz_Request::actionName (); - $params = Minz_Request::params (); - $markReadUrl = Minz_Session::param ('markReadUrl'); - Minz_Session::_param ('markReadUrl', false); + $c = Minz_Request::controllerName(); + $a = Minz_Request::actionName(); + $params = Minz_Request::params(); + $markReadUrl = Minz_Session::param('markReadUrl'); + Minz_Session::_param('markReadUrl', false); ?> +<form id="mark-read-pagination" method="post" style="display: none"></form> + <ul class="pagination"> <li class="item pager-next"> <?php if (!empty($this->nextId)) { ?> - <?php $params['next'] = $this->nextId; ?> - <a id="load_more" href="<?php echo Minz_Url::display (array ('c' => $c, 'a' => $a, 'params' => $params)); ?>"><?php echo Minz_Translate::t ('load_more'); ?></a> + <?php + $params['next'] = $this->nextId; + $params['ajax'] = 1; + ?> + <a id="load_more" href="<?php echo Minz_Url::display(array('c' => $c, 'a' => $a, 'params' => $params)); ?>"> + <?php echo _t('load_more'); ?> + </a> <?php } elseif ($markReadUrl) { ?> - <a id="bigMarkAsRead" href="<?php echo $markReadUrl; ?>"<?php if ($this->conf->reading_confirm) { echo ' class="confirm"';} ?>> - <?php echo Minz_Translate::t ('nothing_to_load'); ?><br /> - <span class="bigTick">✓</span><br /> - <?php echo Minz_Translate::t ('mark_all_read'); ?> - </a> + <button id="bigMarkAsRead" + class="as-link <?php echo $this->conf->reading_confirm ? 'confirm' : ''; ?>" + form="mark-read-pagination" + formaction="<?php echo $markReadUrl; ?>" + type="submit"> + <?php echo _t('nothing_to_load'); ?><br /> + <span class="bigTick">✓</span><br /> + <?php echo _t('mark_all_read'); ?> + </button> <?php } else { ?> - <a id="bigMarkAsRead" href="."> - <?php echo Minz_Translate::t ('nothing_to_load'); ?><br /> - </a> + <a id="bigMarkAsRead" href="."> + <?php echo _t('nothing_to_load'); ?><br /> + </a> <?php } ?> </li> </ul> diff --git a/app/views/helpers/view/global_view.phtml b/app/views/helpers/view/global_view.phtml index db937eeae..72bcf4c73 100644 --- a/app/views/helpers/view/global_view.phtml +++ b/app/views/helpers/view/global_view.phtml @@ -1,5 +1,6 @@ <?php $this->partial ('nav_menu'); ?> +<?php if (!empty($this->entries)) { ?> <div id="stream" class="global categories"> <?php $arUrl = array('c' => 'index', 'a' => 'index', 'params' => array()); @@ -43,3 +44,10 @@ <div id="panel"<?php echo $this->conf->display_posts ? '' : ' class="hide_posts"'; ?>> <a class="close" href="#"><?php echo FreshRSS_Themes::icon('close'); ?></a> </div> + +<?php } else { ?> +<div id="stream" class="prompt alert alert-warn global"> + <h2><?php echo _t('no_feed_to_display'); ?></h2> + <a href="<?php echo _url('configure', 'feed'); ?>"><?php echo _t('think_to_add'); ?></a><br /><br /> +</div> +<?php } ?> diff --git a/app/views/helpers/view/normal_view.phtml b/app/views/helpers/view/normal_view.phtml index 87bf2e22a..1dbf14f4c 100644 --- a/app/views/helpers/view/normal_view.phtml +++ b/app/views/helpers/view/normal_view.phtml @@ -183,8 +183,8 @@ if (!empty($this->entries)) { <?php $this->partial ('nav_entries'); ?> <?php } else { ?> -<div id="stream" class="alert alert-warn normal"> - <span class="alert-head"><?php echo Minz_Translate::t ('no_feed_to_display'); ?></span> - <?php echo Minz_Translate::t ('think_to_add'); ?> +<div id="stream" class="prompt alert alert-warn normal"> + <h2><?php echo _t('no_feed_to_display'); ?></h2> + <a href="<?php echo _url('configure', 'feed'); ?>"><?php echo _t('think_to_add'); ?></a><br /><br /> </div> <?php } ?> diff --git a/app/views/helpers/view/reader_view.phtml b/app/views/helpers/view/reader_view.phtml index 665f72849..c80dca519 100644 --- a/app/views/helpers/view/reader_view.phtml +++ b/app/views/helpers/view/reader_view.phtml @@ -37,8 +37,8 @@ if (!empty($this->entries)) { </div> <?php } else { ?> -<div id="stream" class="alert alert-warn reader"> - <span class="alert-head"><?php echo Minz_Translate::t ('no_feed_to_display'); ?></span> - <?php echo Minz_Translate::t ('think_to_add'); ?> +<div id="stream" class="prompt alert alert-warn reader"> + <h2><?php echo _t('no_feed_to_display'); ?></h2> + <a href="<?php echo _url('configure', 'feed'); ?>"><?php echo _t('think_to_add'); ?></a><br /><br /> </div> <?php } ?> diff --git a/app/views/index/formLogin.phtml b/app/views/index/formLogin.phtml index b79c1b614..b05cdced4 100644 --- a/app/views/index/formLogin.phtml +++ b/app/views/index/formLogin.phtml @@ -3,7 +3,7 @@ switch (Minz_Configuration::authType()) { case 'form': - ?><form id="loginForm" method="post" action="<?php echo _url('index', 'formLogin'); ?>"> + ?><form id="crypto-form" method="post" action="<?php echo _url('index', 'formLogin'); ?>"> <div> <label for="username"><?php echo _t('username'); ?></label> <input type="text" id="username" name="username" size="16" required="required" maxlength="16" pattern="[0-9a-zA-Z]{1,16}" autofocus="autofocus" /> @@ -29,8 +29,15 @@ case 'persona': ?><p> - <?php echo _i('login'); ?> - <a class="signin" href="#"><?php echo _t('login_with_persona'); ?></a> + <a class="signin btn btn-important" href="#"> + <?php echo _i('login'); ?> + <?php echo _t('login_with_persona'); ?> + </a><br /><br /> + + <?php echo _i('help'); ?> + <small> + <a href="<?php echo _url('index', 'resetAuth'); ?>"><?php echo _t('login_persona_problem'); ?></a> + </small> </p><?php break; } ?> diff --git a/app/views/index/resetAuth.phtml b/app/views/index/resetAuth.phtml new file mode 100644 index 000000000..6d4282c14 --- /dev/null +++ b/app/views/index/resetAuth.phtml @@ -0,0 +1,33 @@ +<div class="prompt"> + <h1><?php echo _t('auth_reset'); ?></h1> + + <?php if (!empty($this->message)) { ?> + <p class="alert <?php echo $this->message['status'] === 'bad' ? 'alert-error' : 'alert-warn'; ?>"> + <span class="alert-head"><?php echo $this->message['title']; ?></span><br /> + <?php echo $this->message['body']; ?> + </p> + <?php } ?> + + <?php if (!$this->no_form) { ?> + <form id="crypto-form" method="post" action="<?php echo _url('index', 'resetAuth'); ?>"> + <p class="alert alert-warn"> + <span class="alert-head"><?php echo _t('attention'); ?></span><br /> + <?php echo _t('auth_will_reset'); ?> + </p> + + <div> + <label for="username"><?php echo _t('username_admin'); ?></label> + <input type="text" id="username" name="username" size="16" required="required" maxlength="16" pattern="[0-9a-zA-Z]{1,16}" autofocus="autofocus" /> + </div> + <div> + <label for="passwordPlain"><?php echo _t('password'); ?></label> + <input type="password" id="passwordPlain" required="required" /> + <input type="hidden" id="challenge" name="challenge" /><br /> + <noscript><strong><?php echo _t('javascript_should_be_activated'); ?></strong></noscript> + </div> + <div> + <button id="loginButton" type="submit" class="btn btn-important"><?php echo _t('submit'); ?></button> + </div> + </form> + <?php } ?> +</div> diff --git a/app/views/stats/idle.phtml b/app/views/stats/idle.phtml index 2ba5237f7..6f3d4a117 100644 --- a/app/views/stats/idle.phtml +++ b/app/views/stats/idle.phtml @@ -1,25 +1,48 @@ <?php $this->partial('aside_stats'); ?> -<div class="post content"> +<div class="post"> <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('back_to_rss_feeds'); ?></a> <h1><?php echo _t('stats_idle'); ?></h1> <?php + $current_url = urlencode(Minz_Url::display( + array('c' => 'stats', 'a' => 'idle'), + 'php', true + )); + $nothing = true; foreach ($this->idleFeeds as $period => $feeds) { if (!empty($feeds)) { + $nothing = false; ?> <div class="stat"> <h2><?php echo _t($period); ?></h2> - <ul> - <?php foreach ($feeds as $feed) { ?> - <li><a href="<?php echo _url('configure', 'feed', 'id', $feed['id']); ?>" title="<?php echo date('Y-m-d', $feed['last_date']); ?>"><?php echo $feed['name']; ?></a></li> - <?php } ?> + <form id="form-delete" method="post" style="display: none"></form> + + <?php foreach ($feeds as $feed) { ?> + <ul class="horizontal-list"> + <li class="item"> + <div class="stick"> + <a class="btn" href="<?php echo _url('index', 'index', 'get', 'f_' . $feed['id']); ?>"><?php echo _i('link'); ?> <?php echo _t('filter'); ?></a> + <a class="btn" href="<?php echo _url('configure', 'feed', 'id', $feed['id']); ?>"><?php echo _i('configure'); ?> <?php echo _t('administration'); ?></a> + <button class="btn btn-attention confirm" form="form-delete" formaction="<?php echo _url('feed', 'delete', 'id', $feed['id'], 'r', $current_url); ?>"><?php echo _t('delete'); ?></button> + </div> + </li> + <li class="item"> + <span title="<?php echo timestamptodate($feed['last_date'], false); ?>"><?php echo $feed['name']; ?> (<?php echo _t('number_articles', $feed['nb_articles']); ?>)</span> + </li> </ul> + <?php } ?> </div> <?php } } + + if ($nothing) { ?> + <p class="alert alert-warn"> + <span class="alert-head"><?php echo _t('stats_no_idle'); ?></span> + </p> + <?php } ?> </div> diff --git a/app/views/stats/index.phtml b/app/views/stats/index.phtml index a48181fe4..412e77e16 100644 --- a/app/views/stats/index.phtml +++ b/app/views/stats/index.phtml @@ -1,11 +1,11 @@ <?php $this->partial('aside_stats'); ?> -<div class="post content"> +<div class="post"> <a href="<?php echo _url ('index', 'index'); ?>"><?php echo _t ('back_to_rss_feeds'); ?></a> - + <h1><?php echo _t ('stats_main'); ?></h1> - <div class="stat"> + <div class="stat half"> <h2><?php echo _t ('stats_entry_repartition'); ?></h2> <table> <thead> @@ -38,26 +38,9 @@ </tr> </tbody> </table> - </div> - - <div class="stat"> - <h2><?php echo _t ('stats_entry_per_day'); ?></h2> - <div id="statsEntryPerDay" style="height: 300px"></div> - </div> - - <div class="stat"> - <h2><?php echo _t ('stats_feed_per_category'); ?></h2> - <div id="statsFeedPerCategory" style="height: 300px"></div> - <div id="statsFeedPerCategoryLegend"></div> - </div> - - <div class="stat"> - <h2><?php echo _t ('stats_entry_per_category'); ?></h2> - <div id="statsEntryPerCategory" style="height: 300px"></div> - <div id="statsEntryPerCategoryLegend"></div> - </div> - - <div class="stat"> + </div><!-- + + --><div class="stat half"> <h2><?php echo _t ('stats_top_feed'); ?></h2> <table> <thead> @@ -70,7 +53,7 @@ <tbody> <?php foreach ($this->topFeed as $feed): ?> <tr> - <td><?php echo $feed['name']; ?></td> + <td><a href="<?php echo _url('stats', 'repartition', 'id', $feed['id']); ?>"><?php echo $feed['name']; ?></a></td> <td><?php echo $feed['category']; ?></td> <td class="numeric"><?php echo formatNumber($feed['count']); ?></td> </tr> @@ -78,6 +61,23 @@ </tbody> </table> </div> + + <div class="stat"> + <h2><?php echo _t ('stats_entry_per_day'); ?></h2> + <div id="statsEntryPerDay" style="height: 300px"></div> + </div> + + <div class="stat half"> + <h2><?php echo _t ('stats_feed_per_category'); ?></h2> + <div id="statsFeedPerCategory" style="height: 300px"></div> + <div id="statsFeedPerCategoryLegend"></div> + </div><!-- + + --><div class="stat half"> + <h2><?php echo _t ('stats_entry_per_category'); ?></h2> + <div id="statsEntryPerCategory" style="height: 300px"></div> + <div id="statsEntryPerCategoryLegend"></div> + </div> </div> <script> diff --git a/app/views/stats/repartition.phtml b/app/views/stats/repartition.phtml index 9d2eb28e4..b425c1458 100644 --- a/app/views/stats/repartition.phtml +++ b/app/views/stats/repartition.phtml @@ -1,6 +1,6 @@ <?php $this->partial('aside_stats'); ?> -<div class="post content"> +<div class="post "> <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('back_to_rss_feeds'); ?></a> <h1><?php echo _t('stats_repartition'); ?></h1> @@ -10,12 +10,12 @@ <?php foreach ($this->categories as $category) { $feeds = $category->feeds(); if (!empty($feeds)) { - echo '<optgroup label=', $category->name(), '>'; + echo '<optgroup label="', $category->name(), '">'; foreach ($feeds as $feed) { if ($this->feed && $feed->id() == $this->feed->id()){ - echo '<option value ="', $feed->id(), '" selected data-url="', _url('stats', 'repartition', 'id', $feed->id()), '">', $feed->name(), '</option>'; + echo '<option value="', $feed->id(), '" selected="selected" data-url="', _url('stats', 'repartition', 'id', $feed->id()), '">', $feed->name(), '</option>'; } else { - echo '<option value ="', $feed->id(), '" data-url="', _url('stats', 'repartition', 'id', $feed->id()), '">', $feed->name(), '</option>'; + echo '<option value="', $feed->id(), '" data-url="', _url('stats', 'repartition', 'id', $feed->id()), '">', $feed->name(), '</option>'; } } echo '</optgroup>'; @@ -24,8 +24,8 @@ </select> <?php if ($this->feed) {?> - <a href="<?php echo _url('configure', 'feed', 'id', $this->feed->id()); ?>"> - <?php echo _t('administration'); ?> + <a class="btn" href="<?php echo _url('configure', 'feed', 'id', $this->feed->id()); ?>"> + <?php echo _i('configure'); ?> <?php echo _t('administration'); ?> </a> <?php }?> @@ -34,12 +34,12 @@ <div id="statsEntryPerHour" style="height: 300px"></div> </div> - <div class="stat"> + <div class="stat half"> <h2><?php echo _t('stats_entry_per_day_of_week'); ?></h2> <div id="statsEntryPerDayOfWeek" style="height: 300px"></div> - </div> + </div><!-- - <div class="stat"> + --><div class="stat half"> <h2><?php echo _t('stats_entry_per_month'); ?></h2> <div id="statsEntryPerMonth" style="height: 300px"></div> </div> @@ -56,11 +56,22 @@ function initStats() { return; } // Entry per hour + var avg_h = []; + for (var i = -1; i <= 24; i++) { + avg_h.push([i, <?php echo $this->averageHour?>]); + } Flotr.draw(document.getElementById('statsEntryPerHour'), - [<?php echo $this->repartitionHour ?>], + [{ + data: <?php echo $this->repartitionHour ?>, + bars: {horizontal: false, show: true} + }, { + data: avg_h, + lines: {show: true}, + label: <?php echo $this->averageHour?>, + yaxis: 2 + }], { grid: {verticalLines: false}, - bars: {horizontal: false, show: true}, xaxis: {noTicks: 23, tickFormatter: function(x) { var x = parseInt(x); @@ -70,14 +81,26 @@ function initStats() { max: 23.9, tickDecimals: 0}, yaxis: {min: 0}, + y2axis: {showLabels: false}, mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}} }); // Entry per day of week + var avg_dow = []; + for (var i = -1; i <= 7; i++) { + avg_dow.push([i, <?php echo $this->averageDayOfWeek?>]); + } Flotr.draw(document.getElementById('statsEntryPerDayOfWeek'), - [<?php echo $this->repartitionDayOfWeek ?>], + [{ + data: <?php echo $this->repartitionDayOfWeek ?>, + bars: {horizontal: false, show: true} + }, { + data: avg_dow, + lines: {show: true}, + label: <?php echo $this->averageDayOfWeek?>, + yaxis: 2 + }], { grid: {verticalLines: false}, - bars: {horizontal: false, show: true}, xaxis: {noTicks: 6, tickFormatter: function(x) { var x = parseInt(x), @@ -88,14 +111,26 @@ function initStats() { max: 6.9, tickDecimals: 0}, yaxis: {min: 0}, + y2axis: {showLabels: false}, mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}} }); // Entry per month + var avg_m = []; + for (var i = 0; i <= 13; i++) { + avg_m.push([i, <?php echo $this->averageMonth?>]); + } Flotr.draw(document.getElementById('statsEntryPerMonth'), - [<?php echo $this->repartitionMonth ?>], + [{ + data: <?php echo $this->repartitionMonth ?>, + bars: {horizontal: false, show: true} + }, { + data: avg_m, + lines: {show: true}, + label: <?php echo $this->averageMonth?>, + yaxis: 2 + }], { grid: {verticalLines: false}, - bars: {horizontal: false, show: true}, xaxis: {noTicks: 12, tickFormatter: function(x) { var x = parseInt(x), @@ -106,6 +141,7 @@ function initStats() { max: 12.9, tickDecimals: 0}, yaxis: {min: 0}, + y2axis: {showLabels: false}, mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}} }); diff --git a/app/views/update/apply.phtml b/app/views/update/apply.phtml new file mode 100644 index 000000000..30566c7ab --- /dev/null +++ b/app/views/update/apply.phtml @@ -0,0 +1,9 @@ +<?php $this->partial('aside_configure'); ?> + +<div class="post"> + <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('back_to_rss_feeds'); ?></a> + + <h1><?php echo _t('update_system'); ?></h1> + + <?php ask_info_update(); ?> +</div> diff --git a/app/views/update/index.phtml b/app/views/update/index.phtml new file mode 100644 index 000000000..401f6acd6 --- /dev/null +++ b/app/views/update/index.phtml @@ -0,0 +1,36 @@ +<?php $this->partial('aside_configure'); ?> + +<div class="post"> + <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('back_to_rss_feeds'); ?></a> + + <h1><?php echo _t('update_system'); ?></h1> + + <p> + <?php echo _i('help'); ?> <?php echo _t('update_last', $this->last_update_time); ?> + </p> + + <?php if (!empty($this->message)) { ?> + <p class="alert <?php echo $this->message['status'] === 'bad' ? 'alert-error' : 'alert-warn'; ?>"> + <span class="alert-head"><?php echo $this->message['title']; ?></span> + <?php echo $this->message['body']; ?> + </p> + <?php } elseif ($this->check_last_hour) { ?> + <p class="alert alert-warn"> + <span class="alert-head"><?php echo _t('damn'); ?></span> + <?php echo _t('no_update'); ?> + </p> + <?php } ?> + + <?php + if (!$this->check_last_hour && + (empty($this->message) || $this->message['status'] !== 'good')) { + ?> + <p> + <a href="<?php echo _url('update', 'check'); ?>" class="btn"><?php echo _t('update_check'); ?></a> + </p> + <?php } ?> + + <?php if ($this->update_to_apply) { ?> + <a class="btn btn-important" href="<?php echo _url('update', 'apply'); ?>"><?php echo _t('update_apply'); ?></a> + <?php } ?> +</div> |
