summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGravatar Marien Fressinaud <dev@marienfressinaud.fr> 2014-07-21 18:00:32 +0200
committerGravatar Marien Fressinaud <dev@marienfressinaud.fr> 2014-07-21 18:00:32 +0200
commit8d7ac978f9af4819fc788e7e0a514dc7ca9886d9 (patch)
treefcc7e399a5f628a904518b7b522b17cef67a76e1 /app
parenta4dac0f791ae6d34b64cee5a3fae1815f6f70a2b (diff)
parent73bbdaa015ec8596603fa88bd2cb03f85548e05e (diff)
Merge branch 'dev' into beta
Diffstat (limited to 'app')
-rwxr-xr-xapp/Controllers/configureController.php290
-rwxr-xr-xapp/Controllers/entryController.php12
-rwxr-xr-xapp/Controllers/feedController.php106
-rw-r--r--app/Controllers/importExportController.php5
-rwxr-xr-xapp/Controllers/indexController.php42
-rwxr-xr-xapp/Controllers/javascriptController.php4
-rw-r--r--app/Controllers/statsController.php67
-rw-r--r--app/Controllers/usersController.php14
-rw-r--r--app/Models/Category.php2
-rw-r--r--app/Models/CategoryDAO.php16
-rw-r--r--app/Models/Configuration.php22
-rw-r--r--app/Models/Entry.php4
-rw-r--r--app/Models/EntryDAO.php570
-rw-r--r--app/Models/EntryDAOSQLite.php129
-rw-r--r--app/Models/Factory.php32
-rw-r--r--app/Models/Feed.php194
-rw-r--r--app/Models/FeedDAO.php292
-rw-r--r--app/Models/FeedDAOSQLite.php19
-rw-r--r--app/Models/StatsDAO.php88
-rw-r--r--app/Models/StatsDAOSQLite.php37
-rw-r--r--app/Models/Themes.php11
-rw-r--r--app/Models/UserDAO.php31
-rw-r--r--app/SQL/install.sql.mysql.php (renamed from app/sql.php)5
-rw-r--r--app/SQL/install.sql.sqlite.php58
-rw-r--r--app/i18n/en.php41
-rw-r--r--app/i18n/fr.php115
-rw-r--r--app/i18n/install.en.php2
-rw-r--r--app/i18n/install.fr.php2
-rw-r--r--app/install.php1124
-rw-r--r--app/layout/aside_configure.phtml3
-rw-r--r--app/layout/aside_stats.phtml9
-rw-r--r--app/layout/header.phtml101
-rw-r--r--app/layout/nav_menu.phtml57
-rw-r--r--app/views/configure/archiving.phtml21
-rw-r--r--app/views/configure/feed.phtml21
-rw-r--r--app/views/configure/queries.phtml90
-rw-r--r--app/views/configure/reading.phtml12
-rw-r--r--app/views/configure/sharing.phtml14
-rwxr-xr-xapp/views/helpers/pagination.phtml2
-rw-r--r--app/views/javascript/actualize.phtml6
-rw-r--r--app/views/stats/idle.phtml19
-rw-r--r--app/views/stats/index.phtml127
-rw-r--r--app/views/stats/main.phtml (renamed from app/views/index/stats.phtml)12
43 files changed, 2875 insertions, 953 deletions
diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php
index a117e0f9c..79f40b30b 100755
--- a/app/Controllers/configureController.php
+++ b/app/Controllers/configureController.php
@@ -1,81 +1,81 @@
<?php
class FreshRSS_configure_Controller extends Minz_ActionController {
- public function firstAction () {
+ public function firstAction() {
if (!$this->view->loginOk) {
- Minz_Error::error (
+ Minz_Error::error(
403,
- array ('error' => array (Minz_Translate::t ('access_denied')))
+ array('error' => array(Minz_Translate::t('access_denied')))
);
}
- $catDAO = new FreshRSS_CategoryDAO ();
- $catDAO->checkDefault ();
+ $catDAO = new FreshRSS_CategoryDAO();
+ $catDAO->checkDefault();
}
- public function categorizeAction () {
- $feedDAO = new FreshRSS_FeedDAO ();
- $catDAO = new FreshRSS_CategoryDAO ();
- $defaultCategory = $catDAO->getDefault ();
- $defaultId = $defaultCategory->id ();
+ public function categorizeAction() {
+ $feedDAO = FreshRSS_Factory::createFeedDao();
+ $catDAO = new FreshRSS_CategoryDAO();
+ $defaultCategory = $catDAO->getDefault();
+ $defaultId = $defaultCategory->id();
- if (Minz_Request::isPost ()) {
- $cats = Minz_Request::param ('categories', array ());
- $ids = Minz_Request::param ('ids', array ());
- $newCat = trim (Minz_Request::param ('new_category', ''));
+ if (Minz_Request::isPost()) {
+ $cats = Minz_Request::param('categories', array());
+ $ids = Minz_Request::param('ids', array());
+ $newCat = trim(Minz_Request::param('new_category', ''));
foreach ($cats as $key => $name) {
- if (strlen ($name) > 0) {
- $cat = new FreshRSS_Category ($name);
- $values = array (
- 'name' => $cat->name (),
+ if (strlen($name) > 0) {
+ $cat = new FreshRSS_Category($name);
+ $values = array(
+ 'name' => $cat->name(),
);
- $catDAO->updateCategory ($ids[$key], $values);
+ $catDAO->updateCategory($ids[$key], $values);
} elseif ($ids[$key] != $defaultId) {
- $feedDAO->changeCategory ($ids[$key], $defaultId);
- $catDAO->deleteCategory ($ids[$key]);
+ $feedDAO->changeCategory($ids[$key], $defaultId);
+ $catDAO->deleteCategory($ids[$key]);
}
}
if ($newCat != '') {
- $cat = new FreshRSS_Category ($newCat);
- $values = array (
- 'id' => $cat->id (),
- 'name' => $cat->name (),
+ $cat = new FreshRSS_Category($newCat);
+ $values = array(
+ 'id' => $cat->id(),
+ 'name' => $cat->name(),
);
- if ($catDAO->searchByName ($newCat) == null) {
- $catDAO->addCategory ($values);
+ if ($catDAO->searchByName($newCat) == null) {
+ $catDAO->addCategory($values);
}
}
invalidateHttpCache();
- $notif = array (
+ $notif = array(
'type' => 'good',
- 'content' => Minz_Translate::t ('categories_updated')
+ 'content' => Minz_Translate::t('categories_updated')
);
- Minz_Session::_param ('notification', $notif);
+ Minz_Session::_param('notification', $notif);
- Minz_Request::forward (array ('c' => 'configure', 'a' => 'categorize'), true);
+ Minz_Request::forward(array('c' => 'configure', 'a' => 'categorize'), true);
}
- $this->view->categories = $catDAO->listCategories (false);
- $this->view->defaultCategory = $catDAO->getDefault ();
- $this->view->feeds = $feedDAO->listFeeds ();
+ $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(Minz_Translate::t('categories_management') . ' · ');
}
- public function feedAction () {
- $catDAO = new FreshRSS_CategoryDAO ();
- $this->view->categories = $catDAO->listCategories (false);
+ public function feedAction() {
+ $catDAO = new FreshRSS_CategoryDAO();
+ $this->view->categories = $catDAO->listCategories(false);
- $feedDAO = new FreshRSS_FeedDAO ();
- $this->view->feeds = $feedDAO->listFeeds ();
+ $feedDAO = FreshRSS_Factory::createFeedDao();
+ $this->view->feeds = $feedDAO->listFeeds();
- $id = Minz_Request::param ('id');
- if ($id == false && !empty ($this->view->feeds)) {
- $id = current ($this->view->feeds)->id ();
+ $id = Minz_Request::param('id');
+ if ($id == false && !empty($this->view->feeds)) {
+ $id = current($this->view->feeds)->id();
}
$this->view->flux = false;
@@ -83,14 +83,14 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
$this->view->flux = $this->view->feeds[$id];
if (!$this->view->flux) {
- Minz_Error::error (
+ Minz_Error::error(
404,
- array ('error' => array (Minz_Translate::t ('page_not_found')))
+ array('error' => array(Minz_Translate::t('page_not_found')))
);
} else {
- if (Minz_Request::isPost () && $this->view->flux) {
- $user = Minz_Request::param ('http_user', '');
- $pass = Minz_Request::param ('http_pass', '');
+ if (Minz_Request::isPost() && $this->view->flux) {
+ $user = Minz_Request::param('http_user', '');
+ $pass = Minz_Request::param('http_pass', '');
$httpAuth = '';
if ($user != '' || $pass != '') {
@@ -99,45 +99,46 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
$cat = intval(Minz_Request::param('category', 0));
- $values = array (
- 'name' => Minz_Request::param ('name', ''),
+ $values = array(
+ 'name' => Minz_Request::param('name', ''),
'description' => sanitizeHTML(Minz_Request::param('description', '', true)),
'website' => Minz_Request::param('website', ''),
'url' => Minz_Request::param('url', ''),
'category' => $cat,
- 'pathEntries' => Minz_Request::param ('path_entries', ''),
- 'priority' => intval(Minz_Request::param ('priority', 0)),
+ 'pathEntries' => Minz_Request::param('path_entries', ''),
+ 'priority' => intval(Minz_Request::param('priority', 0)),
'httpAuth' => $httpAuth,
- 'keep_history' => intval(Minz_Request::param ('keep_history', -2)),
+ 'keep_history' => intval(Minz_Request::param('keep_history', -2)),
+ 'ttl' => intval(Minz_Request::param('ttl', -2)),
);
- if ($feedDAO->updateFeed ($id, $values)) {
- $this->view->flux->_category ($cat);
+ if ($feedDAO->updateFeed($id, $values)) {
+ $this->view->flux->_category($cat);
$this->view->flux->faviconPrepare();
- $notif = array (
+ $notif = array(
'type' => 'good',
- 'content' => Minz_Translate::t ('feed_updated')
+ 'content' => Minz_Translate::t('feed_updated')
);
} else {
- $notif = array (
+ $notif = array(
'type' => 'bad',
- 'content' => Minz_Translate::t ('error_occurred_update')
+ 'content' => Minz_Translate::t('error_occurred_update')
);
}
invalidateHttpCache();
- Minz_Session::_param ('notification', $notif);
- Minz_Request::forward (array ('c' => 'configure', 'a' => 'feed', 'params' => array ('id' => $id)), true);
+ Minz_Session::_param('notification', $notif);
+ 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(Minz_Translate::t('rss_feed_management') . ' — ' . $this->view->flux->name() . ' · ');
}
} else {
- Minz_View::prependTitle (Minz_Translate::t ('rss_feed_management') . ' · ');
+ Minz_View::prependTitle(Minz_Translate::t('rss_feed_management') . ' · ');
}
}
- public function displayAction () {
+ public function displayAction() {
if (Minz_Request::isPost()) {
$this->view->conf->_language(Minz_Request::param('language', 'en'));
$themeId = Minz_Request::param('theme', '');
@@ -158,36 +159,37 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
$this->view->conf->_bottomline_link(Minz_Request::param('bottomline_link', false));
$this->view->conf->save();
- Minz_Session::_param ('language', $this->view->conf->language);
- Minz_Translate::reset ();
+ Minz_Session::_param('language', $this->view->conf->language);
+ Minz_Translate::reset();
invalidateHttpCache();
- $notif = array (
+ $notif = array(
'type' => 'good',
- 'content' => Minz_Translate::t ('configuration_updated')
+ 'content' => Minz_Translate::t('configuration_updated')
);
- Minz_Session::_param ('notification', $notif);
+ Minz_Session::_param('notification', $notif);
- Minz_Request::forward (array ('c' => 'configure', 'a' => 'display'), true);
+ Minz_Request::forward(array('c' => 'configure', 'a' => 'display'), true);
}
$this->view->themes = FreshRSS_Themes::get();
- Minz_View::prependTitle (Minz_Translate::t ('display_configuration') . ' · ');
+ Minz_View::prependTitle(Minz_Translate::t('display_configuration') . ' · ');
}
- public function readingAction () {
+ public function readingAction() {
if (Minz_Request::isPost()) {
$this->view->conf->_posts_per_page(Minz_Request::param('posts_per_page', 10));
$this->view->conf->_view_mode(Minz_Request::param('view_mode', 'normal'));
- $this->view->conf->_default_view (Minz_Request::param('default_view', 'a'));
+ $this->view->conf->_default_view((int)Minz_Request::param('default_view', FreshRSS_Entry::STATE_ALL));
$this->view->conf->_auto_load_more(Minz_Request::param('auto_load_more', false));
$this->view->conf->_display_posts(Minz_Request::param('display_posts', false));
$this->view->conf->_onread_jump_next(Minz_Request::param('onread_jump_next', false));
- $this->view->conf->_lazyload (Minz_Request::param('lazyload', false));
- $this->view->conf->_sticky_post (Minz_Request::param('sticky_post', false));
+ $this->view->conf->_lazyload(Minz_Request::param('lazyload', false));
+ $this->view->conf->_sticky_post(Minz_Request::param('sticky_post', false));
+ $this->view->conf->_reading_confirm(Minz_Request::param('reading_confirm', false));
$this->view->conf->_sort_order(Minz_Request::param('sort_order', 'DESC'));
- $this->view->conf->_mark_when (array(
+ $this->view->conf->_mark_when(array(
'article' => Minz_Request::param('mark_open_article', false),
'site' => Minz_Request::param('mark_open_site', false),
'scroll' => Minz_Request::param('mark_scroll', false),
@@ -195,43 +197,43 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
));
$this->view->conf->save();
- Minz_Session::_param ('language', $this->view->conf->language);
- Minz_Translate::reset ();
+ Minz_Session::_param('language', $this->view->conf->language);
+ Minz_Translate::reset();
invalidateHttpCache();
- $notif = array (
+ $notif = array(
'type' => 'good',
- 'content' => Minz_Translate::t ('configuration_updated')
+ 'content' => Minz_Translate::t('configuration_updated')
);
- Minz_Session::_param ('notification', $notif);
+ Minz_Session::_param('notification', $notif);
- Minz_Request::forward (array ('c' => 'configure', 'a' => 'reading'), true);
+ Minz_Request::forward(array('c' => 'configure', 'a' => 'reading'), true);
}
- Minz_View::prependTitle (Minz_Translate::t ('reading_configuration') . ' · ');
+ Minz_View::prependTitle(Minz_Translate::t('reading_configuration') . ' · ');
}
- public function sharingAction () {
- if (Minz_Request::isPost ()) {
+ public function sharingAction() {
+ if (Minz_Request::isPost()) {
$params = Minz_Request::params();
- $this->view->conf->_sharing ($params['share']);
+ $this->view->conf->_sharing($params['share']);
$this->view->conf->save();
invalidateHttpCache();
- $notif = array (
+ $notif = array(
'type' => 'good',
- 'content' => Minz_Translate::t ('configuration_updated')
+ 'content' => Minz_Translate::t('configuration_updated')
);
- Minz_Session::_param ('notification', $notif);
+ Minz_Session::_param('notification', $notif);
- Minz_Request::forward (array ('c' => 'configure', 'a' => 'sharing'), true);
+ Minz_Request::forward(array('c' => 'configure', 'a' => 'sharing'), true);
}
- Minz_View::prependTitle (Minz_Translate::t ('sharing') . ' · ');
+ Minz_View::prependTitle(Minz_Translate::t('sharing') . ' · ');
}
- public function shortcutAction () {
- $list_keys = array ('a', 'b', 'backspace', 'c', 'd', 'delete', 'down', 'e', 'end', 'enter',
+ 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',
@@ -240,9 +242,9 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
'f10', 'f11', 'f12');
$this->view->list_keys = $list_keys;
- if (Minz_Request::isPost ()) {
- $shortcuts = Minz_Request::param ('shortcuts');
- $shortcuts_ok = array ();
+ if (Minz_Request::isPost()) {
+ $shortcuts = Minz_Request::param('shortcuts');
+ $shortcuts_ok = array();
foreach ($shortcuts as $key => $value) {
if (in_array($value, $list_keys)) {
@@ -250,33 +252,35 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
}
}
- $this->view->conf->_shortcuts ($shortcuts_ok);
+ $this->view->conf->_shortcuts($shortcuts_ok);
$this->view->conf->save();
invalidateHttpCache();
- $notif = array (
+ $notif = array(
'type' => 'good',
- 'content' => Minz_Translate::t ('shortcuts_updated')
+ 'content' => Minz_Translate::t('shortcuts_updated')
);
- Minz_Session::_param ('notification', $notif);
+ Minz_Session::_param('notification', $notif);
- Minz_Request::forward (array ('c' => 'configure', 'a' => 'shortcut'), true);
+ Minz_Request::forward(array('c' => 'configure', 'a' => 'shortcut'), true);
}
- Minz_View::prependTitle (Minz_Translate::t ('shortcuts') . ' · ');
+ Minz_View::prependTitle(Minz_Translate::t('shortcuts') . ' · ');
}
public function usersAction() {
- Minz_View::prependTitle(Minz_Translate::t ('users') . ' · ');
+ Minz_View::prependTitle(Minz_Translate::t('users') . ' · ');
}
- public function archivingAction () {
+ 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->save();
invalidateHttpCache();
@@ -291,7 +295,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
Minz_View::prependTitle(Minz_Translate::t('archiving_configuration') . ' · ');
- $entryDAO = new FreshRSS_EntryDAO();
+ $entryDAO = FreshRSS_Factory::createEntryDao();
$this->view->nb_total = $entryDAO->count();
$this->view->size_user = $entryDAO->size();
@@ -299,4 +303,80 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
$this->view->size_total = $entryDAO->size(true);
}
}
+
+ 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);
+ }
+ }
+ $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);
+ } else {
+ $this->view->query_get = array();
+ foreach ($this->view->conf->queries as $key => $query) {
+ if (!isset($query['get'])) {
+ continue;
+ }
+
+ switch ($query['get'][0]) {
+ case 'c':
+ $dao = new FreshRSS_CategoryDAO();
+ $category = $dao->searchById(substr($query['get'], 2));
+ $this->view->query_get[$key] = array(
+ 'type' => 'category',
+ 'name' => $category->name(),
+ );
+ break;
+ case 'f':
+ $dao = FreshRSS_Factory::createFeedDao();
+ $feed = $dao->searchById(substr($query['get'], 2));
+ $this->view->query_get[$key] = array(
+ 'type' => 'feed',
+ 'name' => $feed->name(),
+ );
+ break;
+ case 's':
+ $this->view->query_get[$key] = array(
+ 'type' => 'favorite',
+ 'name' => 'favorite',
+ );
+ break;
+ case 'a':
+ $this->view->query_get[$key] = array(
+ 'type' => 'all',
+ 'name' => 'all',
+ );
+ break;
+ }
+ }
+ }
+
+ Minz_View::prependTitle(Minz_Translate::t('queries') . ' · ');
+ }
+
+ public function addQueryAction() {
+ $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']);
+ $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);
+ }
}
diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php
index bbcb990f5..ac43587ea 100755
--- a/app/Controllers/entryController.php
+++ b/app/Controllers/entryController.php
@@ -43,7 +43,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController {
$nextGet = Minz_Request::param ('nextGet', $get);
$idMax = Minz_Request::param ('idMax', 0);
- $entryDAO = new FreshRSS_EntryDAO ();
+ $entryDAO = FreshRSS_Factory::createEntryDao();
if ($id == false) {
if (!$get) {
$entryDAO->markReadEntries ($idMax);
@@ -85,7 +85,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController {
$id = Minz_Request::param ('id');
if ($id) {
- $entryDAO = new FreshRSS_EntryDAO ();
+ $entryDAO = FreshRSS_Factory::createEntryDao();
$entryDAO->markFavorite ($id, (bool)(Minz_Request::param ('is_favorite', true)));
}
}
@@ -97,10 +97,10 @@ class FreshRSS_entry_Controller extends Minz_ActionController {
// La table des entrées a tendance à grossir énormément
// Cette action permet d'optimiser cette table permettant de grapiller un peu de place
// Cette fonctionnalité n'est à appeler qu'occasionnellement
- $entryDAO = new FreshRSS_EntryDAO();
+ $entryDAO = FreshRSS_Factory::createEntryDao();
$entryDAO->optimizeTable();
- $feedDAO = new FreshRSS_FeedDAO();
+ $feedDAO = FreshRSS_Factory::createFeedDao();
$feedDAO->updateCachedValues();
invalidateHttpCache();
@@ -124,8 +124,8 @@ class FreshRSS_entry_Controller extends Minz_ActionController {
$nb_month_old = max($this->view->conf->old_entries, 1);
$date_min = time() - (3600 * 24 * 30 * $nb_month_old);
- $feedDAO = new FreshRSS_FeedDAO();
- $feeds = $feedDAO->listFeedsOrderUpdate();
+ $feedDAO = FreshRSS_Factory::createFeedDao();
+ $feeds = $feedDAO->listFeeds();
$nbTotal = 0;
invalidateHttpCache();
diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php
index fce008399..3326b2059 100755
--- a/app/Controllers/feedController.php
+++ b/app/Controllers/feedController.php
@@ -31,7 +31,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
), true);
}
- $feedDAO = new FreshRSS_FeedDAO ();
+ $feedDAO = FreshRSS_Factory::createFeedDao();
$this->catDAO = new FreshRSS_CategoryDAO ();
$this->catDAO->checkDefault ();
@@ -102,25 +102,30 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
$is_read = $this->view->conf->mark_when['reception'] ? 1 : 0;
- $entryDAO = new FreshRSS_EntryDAO ();
+ $entryDAO = FreshRSS_Factory::createEntryDao();
$entries = array_reverse($feed->entries()); //We want chronological order and SimplePie uses reverse order
// on calcule la date des articles les plus anciens qu'on accepte
$nb_month_old = $this->view->conf->old_entries;
$date_min = time () - (3600 * 24 * 30 * $nb_month_old);
+ //MySQL: http://docs.oracle.com/cd/E17952_01/refman-5.5-en/optimizing-innodb-transaction-management.html
+ //SQLite: http://stackoverflow.com/questions/1711631/how-do-i-improve-the-performance-of-sqlite
+ $preparedStatement = $entryDAO->addEntryPrepare();
$transactionStarted = true;
- $feedDAO->beginTransaction ();
+ $feedDAO->beginTransaction();
// on ajoute les articles en masse sans vérification
foreach ($entries as $entry) {
- $values = $entry->toArray ();
- $values['id_feed'] = $feed->id ();
- $values['id'] = min(time(), $entry->date (true)) . uSecString();
+ $values = $entry->toArray();
+ $values['id_feed'] = $feed->id();
+ $values['id'] = min(time(), $entry->date(true)) . uSecString();
$values['is_read'] = $is_read;
- $entryDAO->addEntry ($values);
+ $entryDAO->addEntry($values, $preparedStatement);
+ }
+ $feedDAO->updateLastUpdate($feed->id());
+ if ($transactionStarted) {
+ $feedDAO->commit();
}
- $feedDAO->updateLastUpdate ($feed->id ());
- $feedDAO->commit ();
$transactionStarted = false;
// ok, ajout terminé
@@ -162,45 +167,46 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
}
Minz_Request::forward (array ('c' => 'configure', 'a' => 'feed', 'params' => $params), true);
- }
-
- // GET request so we must ask confirmation to user
- Minz_View::prependTitle(Minz_Translate::t('add_rss_feed') . ' · ');
- $this->view->categories = $this->catDAO->listCategories();
- $this->view->feed = new FreshRSS_Feed($url);
- try {
- // We try to get some more information about the feed
- $this->view->feed->load(true);
- $this->view->load_ok = true;
- } catch (Exception $e) {
- $this->view->load_ok = false;
- }
+ } else {
- $feed = $feedDAO->searchByUrl($this->view->feed->url());
- if ($feed) {
- // Already subscribe so we redirect to the feed configuration page
- $notif = array(
- 'type' => 'bad',
- 'content' => Minz_Translate::t(
- 'already_subscribed', $feed->name()
- )
- );
- Minz_Session::_param('notification', $notif);
+ // GET request so we must ask confirmation to user
+ Minz_View::prependTitle(Minz_Translate::t('add_rss_feed') . ' · ');
+ $this->view->categories = $this->catDAO->listCategories();
+ $this->view->feed = new FreshRSS_Feed($url);
+ try {
+ // We try to get some more information about the feed
+ $this->view->feed->load(true);
+ $this->view->load_ok = true;
+ } catch (Exception $e) {
+ $this->view->load_ok = false;
+ }
- Minz_Request::forward(array(
- 'c' => 'configure',
- 'a' => 'feed',
- 'params' => array(
- 'id' => $feed->id()
- )
- ), true);
+ $feed = $feedDAO->searchByUrl($this->view->feed->url());
+ if ($feed) {
+ // Already subscribe so we redirect to the feed configuration page
+ $notif = array(
+ 'type' => 'bad',
+ 'content' => Minz_Translate::t(
+ 'already_subscribed', $feed->name()
+ )
+ );
+ Minz_Session::_param('notification', $notif);
+
+ Minz_Request::forward(array(
+ 'c' => 'configure',
+ 'a' => 'feed',
+ 'params' => array(
+ 'id' => $feed->id()
+ )
+ ), true);
+ }
}
}
public function truncateAction () {
if (Minz_Request::isPost ()) {
$id = Minz_Request::param ('id');
- $feedDAO = new FreshRSS_FeedDAO ();
+ $feedDAO = FreshRSS_Factory::createFeedDao();
$n = $feedDAO->truncate($id);
$notif = array(
'type' => $n === false ? 'bad' : 'good',
@@ -215,8 +221,8 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
public function actualizeAction () {
@set_time_limit(300);
- $feedDAO = new FreshRSS_FeedDAO ();
- $entryDAO = new FreshRSS_EntryDAO ();
+ $feedDAO = FreshRSS_Factory::createFeedDao();
+ $entryDAO = FreshRSS_Factory::createEntryDao();
Minz_Session::_param('actualize_feeds', false);
$id = Minz_Request::param ('id');
@@ -232,7 +238,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
$feeds = array ($feed);
}
} else {
- $feeds = $feedDAO->listFeedsOrderUpdate ();
+ $feeds = $feedDAO->listFeedsOrderUpdate($this->view->conf->ttl_default);
}
// on calcule la date des articles les plus anciens qu'on accepte
@@ -264,22 +270,23 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
$feedHistory = $this->view->conf->keep_history_default;
}
+ $preparedStatement = $entryDAO->addEntryPrepare();
$hasTransaction = true;
$feedDAO->beginTransaction();
// On ne vérifie pas strictement que l'article n'est pas déjà en BDD
// La BDD refusera l'ajout car (id_feed, guid) doit être unique
foreach ($entries as $entry) {
- $eDate = $entry->date (true);
- if ((!isset ($existingGuids[$entry->guid ()])) &&
+ $eDate = $entry->date(true);
+ if ((!isset($existingGuids[$entry->guid()])) &&
(($feedHistory != 0) || ($eDate >= $date_min))) {
- $values = $entry->toArray ();
+ $values = $entry->toArray();
//Use declared date at first import, otherwise use discovery date
$values['id'] = ($useDeclaredDate || $eDate < $date_min) ?
min(time(), $eDate) . uSecString() :
uTimeString();
$values['is_read'] = $is_read;
- $entryDAO->addEntry ($values);
+ $entryDAO->addEntry($values, $preparedStatement);
}
}
}
@@ -300,7 +307,8 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
$feedDAO->commit();
}
$flux_update++;
- if ($feed->url() !== $url) { //URL has changed (auto-discovery)
+ if (($feed->url() !== $url)) { //HTTP 301 Moved Permanently
+ Minz_Log::record('Feed ' . $url . ' moved permanently to ' . $feed->url(), Minz_Log::NOTICE);
$feedDAO->updateFeed($feed->id(), array('url' => $feed->url()));
}
} catch (FreshRSS_Feed_Exception $e) {
@@ -373,7 +381,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
$type = Minz_Request::param ('type', 'feed');
$id = Minz_Request::param ('id');
- $feedDAO = new FreshRSS_FeedDAO ();
+ $feedDAO = FreshRSS_Factory::createFeedDao();
if ($type == 'category') {
if ($feedDAO->deleteFeedByCategory ($id)) {
$notif = array (
diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php
index 3cd791781..ba172cc6d 100644
--- a/app/Controllers/importExportController.php
+++ b/app/Controllers/importExportController.php
@@ -12,8 +12,8 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
require_once(LIB_PATH . '/lib_opml.php');
$this->catDAO = new FreshRSS_CategoryDAO();
- $this->entryDAO = new FreshRSS_EntryDAO();
- $this->feedDAO = new FreshRSS_FeedDAO();
+ $this->entryDAO = FreshRSS_Factory::createEntryDao();
+ $this->feedDAO = FreshRSS_Factory::createFeedDao();
}
public function indexAction() {
@@ -266,6 +266,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
);
$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()
);
diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php
index c843748c3..9a46bde6c 100755
--- a/app/Controllers/indexController.php
+++ b/app/Controllers/indexController.php
@@ -45,7 +45,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
}
$catDAO = new FreshRSS_CategoryDAO();
- $entryDAO = new FreshRSS_EntryDAO();
+ $entryDAO = FreshRSS_Factory::createEntryDao();
$this->view->cat_aside = $catDAO->listCategories ();
$this->view->nb_favorites = $entryDAO->countUnreadReadFavorites ();
@@ -70,11 +70,11 @@ class FreshRSS_index_Controller extends Minz_ActionController {
// mise à jour des titres
$this->view->rss_title = $this->view->currentName . ' | ' . Minz_View::title();
if ($this->view->nb_not_read > 0) {
- Minz_View::appendTitle (' (' . formatNumber($this->view->nb_not_read) . ')');
+ Minz_View::prependTitle('(' . formatNumber($this->view->nb_not_read) . ') ');
}
- Minz_View::prependTitle (
+ Minz_View::prependTitle(
+ ($this->nb_not_read_cat > 0 ? '(' . formatNumber($this->nb_not_read_cat) . ') ' : '') .
$this->view->currentName .
- ($this->nb_not_read_cat > 0 ? ' (' . formatNumber($this->nb_not_read_cat) . ')' : '') .
' · '
);
@@ -82,9 +82,6 @@ class FreshRSS_index_Controller extends Minz_ActionController {
$this->view->state = $state = Minz_Request::param ('state', $this->view->conf->default_view);
$state_param = Minz_Request::param ('state', null);
$filter = Minz_Request::param ('search', '');
- if (!empty($filter)) {
- $state = FreshRSS_Entry::STATE_ALL; //Search always in read and unread articles
- }
$this->view->order = $order = Minz_Request::param ('order', $this->view->conf->sort_order);
$nb = Minz_Request::param ('nb', $this->view->conf->posts_per_page);
$first = Minz_Request::param ('next', '');
@@ -127,8 +124,14 @@ class FreshRSS_index_Controller extends Minz_ActionController {
// Si on a récupéré aucun article "non lus"
// on essaye de récupérer tous les articles
- if ($state === FreshRSS_Entry::STATE_NOT_READ && empty($entries) && ($state_param === null)) {
- Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG);
+ if ($state === FreshRSS_Entry::STATE_NOT_READ && empty($entries) && ($state_param === null) && ($filter == '')) {
+ Minz_Log::record('Conflicting information about nbNotRead!', Minz_Log::DEBUG);
+ $feedDAO = FreshRSS_Factory::createFeedDao();
+ try {
+ $feedDAO->updateCachedValues();
+ } catch (Exception $ex) {
+ Minz_Log::record('Failed to automatically correct nbNotRead! ' + $ex->getMessage(), Minz_Log::NOTICE);
+ }
$this->view->state = FreshRSS_Entry::STATE_ALL;
$entries = $entryDAO->listWhere($getType, $getId, $this->view->state, $order, $nb, $first, $filter, $date_min, true, $keepHistoryDefault);
}
@@ -184,7 +187,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
case 'f':
$feed = FreshRSS_CategoryDAO::findFeed($this->view->cat_aside, $getId);
if (empty($feed)) {
- $feedDAO = new FreshRSS_FeedDAO();
+ $feedDAO = FreshRSS_Factory::createFeedDao();
$feed = $feedDAO->searchById($getId);
}
if ($feed) {
@@ -201,25 +204,6 @@ class FreshRSS_index_Controller extends Minz_ActionController {
}
}
- public function statsAction () {
- if (!$this->view->loginOk) {
- Minz_Error::error (
- 403,
- array ('error' => array (Minz_Translate::t ('access_denied')))
- );
- }
-
- Minz_View::prependTitle (Minz_Translate::t ('stats') . ' · ');
-
- $statsDAO = new FreshRSS_StatsDAO ();
- Minz_View::appendScript (Minz_Url::display ('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js')));
- $this->view->repartition = $statsDAO->calculateEntryRepartition();
- $this->view->count = ($statsDAO->calculateEntryCount());
- $this->view->feedByCategory = $statsDAO->calculateFeedByCategory();
- $this->view->entryByCategory = $statsDAO->calculateEntryByCategory();
- $this->view->topFeed = $statsDAO->calculateTopFeed();
- }
-
public function aboutAction () {
Minz_View::prependTitle (Minz_Translate::t ('about') . ' · ');
}
diff --git a/app/Controllers/javascriptController.php b/app/Controllers/javascriptController.php
index 3d741e298..67148350f 100755
--- a/app/Controllers/javascriptController.php
+++ b/app/Controllers/javascriptController.php
@@ -7,8 +7,8 @@ class FreshRSS_javascript_Controller extends Minz_ActionController {
public function actualizeAction () {
header('Content-Type: text/javascript; charset=UTF-8');
- $feedDAO = new FreshRSS_FeedDAO ();
- $this->view->feeds = $feedDAO->listFeedsOrderUpdate();
+ $feedDAO = FreshRSS_Factory::createFeedDao();
+ $this->view->feeds = $feedDAO->listFeedsOrderUpdate($this->view->conf->ttl_default);
}
public function nbUnreadsPerFeedAction() {
diff --git a/app/Controllers/statsController.php b/app/Controllers/statsController.php
new file mode 100644
index 000000000..9009468bc
--- /dev/null
+++ b/app/Controllers/statsController.php
@@ -0,0 +1,67 @@
+<?php
+
+class FreshRSS_stats_Controller extends Minz_ActionController {
+
+ public function indexAction() {
+ $statsDAO = FreshRSS_Factory::createStatsDAO();
+ Minz_View::appendScript (Minz_Url::display ('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js')));
+ $this->view->repartition = $statsDAO->calculateEntryRepartition();
+ $this->view->count = ($statsDAO->calculateEntryCount());
+ $this->view->feedByCategory = $statsDAO->calculateFeedByCategory();
+ $this->view->entryByCategory = $statsDAO->calculateEntryByCategory();
+ $this->view->topFeed = $statsDAO->calculateTopFeed();
+ }
+
+ public function idleAction() {
+ $statsDAO = FreshRSS_Factory::createStatsDAO();
+ $feeds = $statsDAO->calculateFeedLastDate();
+ $idleFeeds = array();
+ $now = new \DateTime();
+ $feedDate = clone $now;
+ $lastWeek = clone $now;
+ $lastWeek->modify('-1 week');
+ $lastMonth = clone $now;
+ $lastMonth->modify('-1 month');
+ $last3Month = clone $now;
+ $last3Month->modify('-3 month');
+ $last6Month = clone $now;
+ $last6Month->modify('-6 month');
+ $lastYear = clone $now;
+ $lastYear->modify('-1 year');
+
+ foreach ($feeds as $feed) {
+ $feedDate->setTimestamp($feed['last_date']);
+ if ($feedDate >= $lastWeek) {
+ continue;
+ }
+ if ($feedDate < $lastWeek) {
+ $idleFeeds['last_week'][] = $feed['name'];
+ }
+ if ($feedDate < $lastMonth) {
+ $idleFeeds['last_month'][] = $feed['name'];
+ }
+ if ($feedDate < $last3Month) {
+ $idleFeeds['last_3_month'][] = $feed['name'];
+ }
+ if ($feedDate < $last6Month) {
+ $idleFeeds['last_6_month'][] = $feed['name'];
+ }
+ if ($feedDate < $lastYear) {
+ $idleFeeds['last_year'][] = $feed['name'];
+ }
+ }
+
+ $this->view->idleFeeds = array_reverse($idleFeeds);
+ }
+
+ public function firstAction() {
+ if (!$this->view->loginOk) {
+ Minz_Error::error(
+ 403, array('error' => array(Minz_Translate::t('access_denied')))
+ );
+ }
+
+ Minz_View::prependTitle(Minz_Translate::t('stats') . ' · ');
+ }
+
+}
diff --git a/app/Controllers/usersController.php b/app/Controllers/usersController.php
index fa967cedc..35fa3675f 100644
--- a/app/Controllers/usersController.php
+++ b/app/Controllers/usersController.php
@@ -17,7 +17,7 @@ class FreshRSS_users_Controller extends Minz_ActionController {
if (Minz_Request::isPost()) {
$ok = true;
- $passwordPlain = Minz_Request::param('passwordPlain', false);
+ $passwordPlain = Minz_Request::param('passwordPlain', '', true);
if ($passwordPlain != '') {
Minz_Request::_param('passwordPlain'); //Discard plain-text password ASAP
$_POST['passwordPlain'] = '';
@@ -32,7 +32,7 @@ class FreshRSS_users_Controller extends Minz_ActionController {
}
Minz_Session::_param('passwordHash', $this->view->conf->passwordHash);
- $passwordPlain = Minz_Request::param('apiPasswordPlain', false);
+ $passwordPlain = Minz_Request::param('apiPasswordPlain', '', true);
if ($passwordPlain != '') {
if (!function_exists('password_hash')) {
include_once(LIB_PATH . '/password_compat.php');
@@ -45,7 +45,7 @@ class FreshRSS_users_Controller extends Minz_ActionController {
}
if (Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) {
- $this->view->conf->_mail_login(Minz_Request::param('mail_login', false));
+ $this->view->conf->_mail_login(Minz_Request::param('mail_login', '', true));
}
$email = $this->view->conf->mail_login;
Minz_Session::_param('mail', $email);
@@ -99,7 +99,8 @@ class FreshRSS_users_Controller extends Minz_ActionController {
public function createAction() {
if (Minz_Request::isPost() && Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) {
- require_once(APP_PATH . '/sql.php');
+ $db = Minz_Configuration::dataBase();
+ require_once(APP_PATH . '/SQL/sql.' . $db['type'] . '.php');
$new_user_language = Minz_Request::param('new_user_language', $this->view->conf->language);
if (!in_array($new_user_language, $this->view->conf->availableLanguages())) {
@@ -119,7 +120,7 @@ class FreshRSS_users_Controller extends Minz_ActionController {
}
if ($ok) {
- $passwordPlain = Minz_Request::param('new_user_passwordPlain', false);
+ $passwordPlain = Minz_Request::param('new_user_passwordPlain', '', true);
$passwordHash = '';
if ($passwordPlain != '') {
Minz_Request::_param('new_user_passwordPlain'); //Discard plain-text password ASAP
@@ -170,7 +171,8 @@ class FreshRSS_users_Controller extends Minz_ActionController {
public function deleteAction() {
if (Minz_Request::isPost() && Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) {
- require_once(APP_PATH . '/sql.php');
+ $db = Minz_Configuration::dataBase();
+ require_once(APP_PATH . '/SQL/sql.' . $db['type'] . '.php');
$username = Minz_Request::param('username');
$ok = ctype_alnum($username);
diff --git a/app/Models/Category.php b/app/Models/Category.php
index 328bae799..0a0dbd3ca 100644
--- a/app/Models/Category.php
+++ b/app/Models/Category.php
@@ -44,7 +44,7 @@ class FreshRSS_Category extends Minz_Model {
}
public function feeds () {
if ($this->feeds === null) {
- $feedDAO = new FreshRSS_FeedDAO ();
+ $feedDAO = FreshRSS_Factory::createFeedDao();
$this->feeds = $feedDAO->listByCategory ($this->id ());
$this->nbFeed = 0;
$this->nbNotRead = 0;
diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php
index 6a9b839b9..f11f87f47 100644
--- a/app/Models/CategoryDAO.php
+++ b/app/Models/CategoryDAO.php
@@ -12,8 +12,8 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo {
if ($stm && $stm->execute ($values)) {
return $this->bd->lastInsertId();
} else {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error addCategory: ' . $info[2], Minz_Log::ERROR);
return false;
}
}
@@ -43,8 +43,8 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo {
if ($stm && $stm->execute ($values)) {
return $stm->rowCount();
} else {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error updateCategory: ' . $info[2], Minz_Log::ERROR);
return false;
}
}
@@ -58,8 +58,8 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo {
if ($stm && $stm->execute ($values)) {
return $stm->rowCount();
} else {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error deleteCategory: ' . $info[2], Minz_Log::ERROR);
return false;
}
}
@@ -102,7 +102,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo {
$sql = 'SELECT c.id AS c_id, c.name AS c_name, '
. ($details ? 'f.* ' : 'f.id, f.name, f.url, f.website, f.priority, f.error, f.cache_nbEntries, f.cache_nbUnreads ')
. 'FROM `' . $this->prefix . 'category` c '
- . 'LEFT OUTER JOIN `' . $this->prefix . 'feed` f ON f.category = c.id '
+ . 'LEFT OUTER JOIN `' . $this->prefix . 'feed` f ON f.category=c.id '
. 'GROUP BY f.id '
. 'ORDER BY c.name, f.name';
$stm = $this->bd->prepare ($sql);
@@ -166,7 +166,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo {
}
public function countNotRead ($id) {
- $sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id WHERE category=? AND e.is_read=0';
+ $sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE category=? AND e.is_read=0';
$stm = $this->bd->prepare ($sql);
$values = array ($id);
$stm->execute ($values);
diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php
index 0d6666297..7596c54cd 100644
--- a/app/Models/Configuration.php
+++ b/app/Models/Configuration.php
@@ -7,6 +7,7 @@ class FreshRSS_Configuration {
'language' => 'en',
'old_entries' => 3,
'keep_history_default' => 0,
+ 'ttl_default' => 3600,
'mail_login' => '',
'token' => '',
'passwordHash' => '', //CRYPT_BLOWFISH
@@ -19,6 +20,7 @@ class FreshRSS_Configuration {
'onread_jump_next' => true,
'lazyload' => true,
'sticky_post' => true,
+ 'reading_confirm' => false,
'sort_order' => 'DESC',
'anon_access' => false,
'mark_when' => array(
@@ -53,6 +55,7 @@ class FreshRSS_Configuration {
'bottomline_date' => true,
'bottomline_link' => true,
'sharing' => array(),
+ 'queries' => array(),
);
private $available_languages = array(
@@ -147,6 +150,9 @@ class FreshRSS_Configuration {
public function _sticky_post($value) {
$this->data['sticky_post'] = ((bool)$value) && $value !== 'no';
}
+ public function _reading_confirm($value) {
+ $this->data['reading_confirm'] = ((bool)$value) && $value !== 'no';
+ }
public function _sort_order ($value) {
$this->data['sort_order'] = $value === 'ASC' ? 'ASC' : 'DESC';
}
@@ -158,6 +164,10 @@ class FreshRSS_Configuration {
$value = intval($value);
$this->data['keep_history_default'] = $value >= -1 ? $value : 0;
}
+ public function _ttl_default($value) {
+ $value = intval($value);
+ $this->data['ttl_default'] = $value >= -1 ? $value : 3600;
+ }
public function _shortcuts ($values) {
foreach ($values as $key => $value) {
if (isset($this->data['shortcuts'][$key])) {
@@ -219,6 +229,18 @@ class FreshRSS_Configuration {
$this->data['sharing'][] = $value;
}
}
+ public function _queries ($values) {
+ $this->data['queries'] = array();
+ foreach ($values as $value) {
+ $value = array_filter($value);
+ $params = $value;
+ unset($params['name']);
+ unset($params['url']);
+ $value['url'] = Minz_Url::display(array('params' => $params));
+
+ $this->data['queries'][] = $value;
+ }
+ }
public function _theme($value) {
$this->data['theme'] = $value;
}
diff --git a/app/Models/Entry.php b/app/Models/Entry.php
index fa9066d5b..0bf1f2616 100644
--- a/app/Models/Entry.php
+++ b/app/Models/Entry.php
@@ -74,7 +74,7 @@ class FreshRSS_Entry extends Minz_Model {
}
public function feed ($object = false) {
if ($object) {
- $feedDAO = new FreshRSS_FeedDAO ();
+ $feedDAO = FreshRSS_Factory::createFeedDao();
return $feedDAO->searchById ($this->feed);
} else {
return $this->feed;
@@ -154,7 +154,7 @@ class FreshRSS_Entry extends Minz_Model {
// Gestion du contenu
// On cherche à récupérer les articles en entier... même si le flux ne le propose pas
if ($pathEntries) {
- $entryDAO = new FreshRSS_EntryDAO();
+ $entryDAO = FreshRSS_Factory::createEntryDao();
$entry = $entryDAO->searchByGuid($this->feed, $this->guid);
if($entry) {
diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php
index 4e24541dc..8c001e73b 100644
--- a/app/Models/EntryDAO.php
+++ b/app/Models/EntryDAO.php
@@ -1,12 +1,25 @@
<?php
class FreshRSS_EntryDAO extends Minz_ModelPdo {
- public function addEntry ($valuesTmp) {
- $sql = 'INSERT INTO `' . $this->prefix . 'entry`(id, guid, title, author, content_bin, link, date, is_read, is_favorite, id_feed, tags) '
- . 'VALUES(?, ?, ?, ?, COMPRESS(?), ?, ?, ?, ?, ?, ?)';
- $stm = $this->bd->prepare ($sql);
- $values = array (
+ public function isCompressed() {
+ return parent::$sharedDbType !== 'sqlite';
+ }
+
+ public function addEntryPrepare() {
+ $sql = 'INSERT INTO `' . $this->prefix . 'entry`(id, guid, title, author, '
+ . ($this->isCompressed() ? 'content_bin' : 'content')
+ . ', link, date, is_read, is_favorite, id_feed, tags) '
+ . 'VALUES(?, ?, ?, ?, '
+ . ($this->isCompressed() ? 'COMPRESS(?)' : '?')
+ . ', ?, ?, ?, ?, ?, ?)';
+ return $this->bd->prepare($sql);
+ }
+
+ public function addEntry($valuesTmp, $preparedStatement = null) {
+ $stm = $preparedStatement === null ? addEntryPrepare() : $preparedStatement;
+
+ $values = array(
$valuesTmp['id'],
substr($valuesTmp['guid'], 0, 760),
substr($valuesTmp['title'], 0, 255),
@@ -20,12 +33,12 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
substr($valuesTmp['tags'], 0, 1023),
);
- if ($stm && $stm->execute ($values)) {
+ if ($stm && $stm->execute($values)) {
return $this->bd->lastInsertId();
} else {
- $info = $stm->errorInfo();
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
if ((int)($info[0] / 1000) !== 23) { //Filter out "SQLSTATE Class code 23: Constraint Violation" because of expected duplicate entries
- Minz_Log::record ('SQL error ' . $info[0] . ': ' . $info[1] . ' ' . $info[2]
+ Minz_Log::record('SQL error addEntry: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2]
. ' while adding entry in feed ' . $valuesTmp['id_feed'] . ' with title: ' . $valuesTmp['title'], Minz_Log::ERROR);
} /*else {
Minz_Log::record ('SQL error ' . $info[0] . ': ' . $info[1] . ' ' . $info[2]
@@ -69,23 +82,53 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
if (!is_array($ids)) {
$ids = array($ids);
}
- $sql = 'UPDATE `' . $this->prefix . 'entry` e '
- . 'SET e.is_favorite = ? '
- . 'WHERE e.id IN (' . str_repeat('?,', count($ids) - 1). '?)';
- $values = array ($is_favorite ? 1 : 0);
+ $sql = 'UPDATE `' . $this->prefix . 'entry` '
+ . 'SET is_favorite=? '
+ . 'WHERE id IN (' . str_repeat('?,', count($ids) - 1). '?)';
+ $values = array($is_favorite ? 1 : 0);
$values = array_merge($values, $ids);
- $stm = $this->bd->prepare ($sql);
- if ($stm && $stm->execute ($values)) {
+ $stm = $this->bd->prepare($sql);
+ if ($stm && $stm->execute($values)) {
return $stm->rowCount();
} else {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error markFavorite: ' . $info[2], Minz_Log::ERROR);
+ return false;
+ }
+ }
+
+ protected function updateCacheUnreads($catId = false, $feedId = false) {
+ $sql = 'UPDATE `' . $this->prefix . 'feed` f '
+ . 'LEFT OUTER JOIN ('
+ . 'SELECT e.id_feed, '
+ . 'COUNT(*) AS nbUnreads '
+ . 'FROM `' . $this->prefix . 'entry` e '
+ . 'WHERE e.is_read=0 '
+ . 'GROUP BY e.id_feed'
+ . ') x ON x.id_feed=f.id '
+ . 'SET f.cache_nbUnreads=COALESCE(x.nbUnreads, 0) '
+ . 'WHERE 1';
+ $values = array();
+ if ($feedId !== false) {
+ $sql .= ' AND f.id=?';
+ $values[] = $id;
+ }
+ if ($catId !== false) {
+ $sql .= ' AND f.category=?';
+ $values[] = $catId;
+ }
+ $stm = $this->bd->prepare($sql);
+ if ($stm && $stm->execute($values)) {
+ return true;
+ } else {
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error updateCacheUnreads: ' . $info[2], Minz_Log::ERROR);
return false;
}
}
public function markRead($ids, $is_read = true) {
- if (is_array($ids)) {
+ if (is_array($ids)) { //Many IDs at once (used by API)
if (count($ids) < 6) { //Speed heuristics
$affected = 0;
foreach ($ids as $id) {
@@ -94,316 +137,164 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
return $affected;
}
- $this->bd->beginTransaction();
- $sql = 'UPDATE `' . $this->prefix . 'entry` e '
- . 'SET e.is_read = ? '
- . 'WHERE e.id IN (' . str_repeat('?,', count($ids) - 1). '?)';
+ $sql = 'UPDATE `' . $this->prefix . 'entry` '
+ . 'SET is_read=? '
+ . 'WHERE id IN (' . str_repeat('?,', count($ids) - 1). '?)';
$values = array($is_read ? 1 : 0);
$values = array_merge($values, $ids);
$stm = $this->bd->prepare($sql);
if (!($stm && $stm->execute($values))) {
- $info = $stm->errorInfo();
- Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR);
- $this->bd->rollBack();
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR);
return false;
}
$affected = $stm->rowCount();
-
- if ($affected > 0) {
- $sql = 'UPDATE `' . $this->prefix . 'feed` 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 `' . $this->prefix . 'entry` e '
- . 'GROUP BY e.id_feed'
- . ') x ON x.id_feed=f.id '
- . 'SET f.cache_nbEntries=x.nbEntries, f.cache_nbUnreads=x.nbUnreads';
- $stm = $this->bd->prepare($sql);
- if (!($stm && $stm->execute())) {
- $info = $stm->errorInfo();
- Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR);
- $this->bd->rollBack();
- return false;
- }
+ if (($affected > 0) && (!$this->updateCacheUnreads(false, false))) {
+ return false;
}
-
- $this->bd->commit();
return $affected;
} else {
- $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
- . 'SET e.is_read = ?,'
+ $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id '
+ . 'SET e.is_read=?,'
. 'f.cache_nbUnreads=f.cache_nbUnreads' . ($is_read ? '-' : '+') . '1 '
- . 'WHERE e.id=?';
- $values = array($is_read ? 1 : 0, $ids);
+ . 'WHERE e.id=? AND e.is_read=?';
+ $values = array($is_read ? 1 : 0, $ids, $is_read ? 0 : 1);
$stm = $this->bd->prepare($sql);
if ($stm && $stm->execute($values)) {
return $stm->rowCount();
} else {
- $info = $stm->errorInfo();
- Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR);
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR);
return false;
}
}
}
- public function markReadEntries ($idMax = 0, $onlyFavorites = false, $priorityMin = 0) {
+ public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0) {
if ($idMax == 0) {
- $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
- . 'SET e.is_read = 1, f.cache_nbUnreads=0 '
- . 'WHERE e.is_read = 0';
- if ($onlyFavorites) {
- $sql .= ' AND e.is_favorite = 1';
- } elseif ($priorityMin >= 0) {
- $sql .= ' AND f.priority > ' . intval($priorityMin);
- }
- $stm = $this->bd->prepare ($sql);
- if ($stm && $stm->execute ()) {
- return $stm->rowCount();
- } else {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
- return false;
- }
- } else {
- $this->bd->beginTransaction ();
-
- $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
- . 'SET e.is_read = 1 '
- . 'WHERE e.is_read = 0 AND e.id <= ?';
- if ($onlyFavorites) {
- $sql .= ' AND e.is_favorite = 1';
- } elseif ($priorityMin >= 0) {
- $sql .= ' AND f.priority > ' . intval($priorityMin);
- }
- $values = array ($idMax);
- $stm = $this->bd->prepare ($sql);
- if (!($stm && $stm->execute ($values))) {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
- $this->bd->rollBack ();
- return false;
- }
- $affected = $stm->rowCount();
-
- if ($affected > 0) {
- $sql = 'UPDATE `' . $this->prefix . 'feed` f '
- . 'LEFT OUTER JOIN ('
- . 'SELECT e.id_feed, '
- . 'COUNT(*) AS nbUnreads '
- . 'FROM `' . $this->prefix . 'entry` e '
- . 'WHERE e.is_read = 0 '
- . 'GROUP BY e.id_feed'
- . ') x ON x.id_feed=f.id '
- . 'SET f.cache_nbUnreads=COALESCE(x.nbUnreads, 0)';
- $stm = $this->bd->prepare ($sql);
- if (!($stm && $stm->execute ())) {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
- $this->bd->rollBack ();
- return false;
- }
- }
+ $idMax = time() . '000000';
+ Minz_Log::record($nb . 'Calling markReadEntries(0) is deprecated!', Minz_Log::DEBUG);
+ }
- $this->bd->commit ();
- return $affected;
+ $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id '
+ . 'SET e.is_read=1 '
+ . 'WHERE e.is_read=0 AND e.id <= ?';
+ if ($onlyFavorites) {
+ $sql .= ' AND e.is_favorite=1';
+ } elseif ($priorityMin >= 0) {
+ $sql .= ' AND f.priority > ' . intval($priorityMin);
}
+ $values = array($idMax);
+ $stm = $this->bd->prepare($sql);
+ if (!($stm && $stm->execute($values))) {
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error markReadEntries: ' . $info[2], Minz_Log::ERROR);
+ return false;
+ }
+ $affected = $stm->rowCount();
+ if (($affected > 0) && (!$this->updateCacheUnreads(false, false))) {
+ return false;
+ }
+ return $affected;
}
- public function markReadCat ($id, $idMax = 0) {
+ public function markReadCat($id, $idMax = 0) {
if ($idMax == 0) {
- $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
- . 'SET e.is_read = 1, f.cache_nbUnreads=0 '
- . 'WHERE f.category = ? AND e.is_read = 0';
- $values = array ($id);
- $stm = $this->bd->prepare ($sql);
- if ($stm && $stm->execute ($values)) {
- return $stm->rowCount();
- } else {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
- return false;
- }
- } else {
- $this->bd->beginTransaction ();
-
- $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
- . 'SET e.is_read = 1 '
- . 'WHERE f.category = ? AND e.is_read = 0 AND e.id <= ?';
- $values = array ($id, $idMax);
- $stm = $this->bd->prepare ($sql);
- if (!($stm && $stm->execute ($values))) {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
- $this->bd->rollBack ();
- return false;
- }
- $affected = $stm->rowCount();
-
- if ($affected > 0) {
- $sql = 'UPDATE `' . $this->prefix . 'feed` f '
- . 'LEFT OUTER JOIN ('
- . 'SELECT e.id_feed, '
- . 'COUNT(*) AS nbUnreads '
- . 'FROM `' . $this->prefix . 'entry` e '
- . 'WHERE e.is_read = 0 '
- . 'GROUP BY e.id_feed'
- . ') x ON x.id_feed=f.id '
- . 'SET f.cache_nbUnreads=COALESCE(x.nbUnreads, 0) '
- . 'WHERE f.category = ?';
- $values = array ($id);
- $stm = $this->bd->prepare ($sql);
- if (!($stm && $stm->execute ($values))) {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
- $this->bd->rollBack ();
- return false;
- }
- }
+ $idMax = time() . '000000';
+ Minz_Log::record($nb . 'Calling markReadCat(0) is deprecated!', Minz_Log::DEBUG);
+ }
- $this->bd->commit ();
- return $affected;
+ $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id '
+ . 'SET e.is_read=1 '
+ . 'WHERE f.category=? AND e.is_read=0 AND e.id <= ?';
+ $values = array($id, $idMax);
+ $stm = $this->bd->prepare($sql);
+ if (!($stm && $stm->execute($values))) {
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error markReadCat: ' . $info[2], Minz_Log::ERROR);
+ return false;
+ }
+ $affected = $stm->rowCount();
+ if (($affected > 0) && (!$this->updateCacheUnreads($id, false))) {
+ return false;
}
+ return $affected;
}
- public function markReadCatName($name, $idMax = 0) {
+ public function markReadFeed($id, $idMax = 0) {
if ($idMax == 0) {
- $sql = 'UPDATE `' . $this->prefix . 'entry` e '
- . 'INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
- . 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category '
- . 'SET e.is_read = 1, f.cache_nbUnreads=0 '
- . 'WHERE c.name = ?';
- $values = array($name);
- $stm = $this->bd->prepare($sql);
- if ($stm && $stm->execute($values)) {
- return $stm->rowCount();
- } else {
- $info = $stm->errorInfo();
- Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR);
- return false;
- }
- } else {
- $this->bd->beginTransaction();
-
- $sql = 'UPDATE `' . $this->prefix . 'entry` e '
- . 'INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
- . 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category '
- . 'SET e.is_read = 1 '
- . 'WHERE c.name = ? AND e.id <= ?';
- $values = array($name, $idMax);
+ $idMax = time() . '000000';
+ Minz_Log::record($nb . 'Calling markReadFeed(0) is deprecated!', Minz_Log::DEBUG);
+ }
+ $this->bd->beginTransaction();
+
+ $sql = 'UPDATE `' . $this->prefix . 'entry` '
+ . 'SET is_read=1 '
+ . 'WHERE id_feed=? AND is_read=0 AND id <= ?';
+ $values = array($id, $idMax);
+ $stm = $this->bd->prepare($sql);
+ if (!($stm && $stm->execute($values))) {
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error markReadFeed: ' . $info[2], Minz_Log::ERROR);
+ $this->bd->rollBack();
+ return false;
+ }
+ $affected = $stm->rowCount();
+
+ if ($affected > 0) {
+ $sql = 'UPDATE `' . $this->prefix . 'feed` '
+ . 'SET cache_nbUnreads=cache_nbUnreads-' . $affected
+ . ' WHERE id=?';
+ $values = array($id);
$stm = $this->bd->prepare($sql);
if (!($stm && $stm->execute($values))) {
- $info = $stm->errorInfo();
- Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR);
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error markReadFeed: ' . $info[2], Minz_Log::ERROR);
$this->bd->rollBack();
return false;
}
- $affected = $stm->rowCount();
-
- if ($affected > 0) {
- $sql = 'UPDATE `' . $this->prefix . 'feed` f '
- . 'LEFT OUTER JOIN ('
- . 'SELECT e.id_feed, '
- . 'COUNT(*) AS nbUnreads '
- . 'FROM `' . $this->prefix . 'entry` e '
- . 'WHERE e.is_read = 0 '
- . 'GROUP BY e.id_feed'
- . ') x ON x.id_feed=f.id '
- . 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category '
- . 'SET f.cache_nbUnreads=COALESCE(x.nbUnreads, 0) '
- . 'WHERE c.name = ?';
- $values = array($name);
- $stm = $this->bd->prepare($sql);
- if (!($stm && $stm->execute($values))) {
- $info = $stm->errorInfo();
- Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR);
- $this->bd->rollBack();
- return false;
- }
- }
-
- $this->bd->commit();
- return $affected;
}
- }
-
- public function markReadFeed ($id, $idMax = 0) {
- if ($idMax == 0) {
- $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
- . 'SET e.is_read = 1, f.cache_nbUnreads=0 '
- . 'WHERE f.id=? AND e.is_read = 0';
- $values = array ($id);
- $stm = $this->bd->prepare ($sql);
- if ($stm && $stm->execute ($values)) {
- return $stm->rowCount();
- } else {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
- return false;
- }
- } else {
- $this->bd->beginTransaction ();
-
- $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
- . 'SET e.is_read = 1 '
- . 'WHERE f.id=? AND e.is_read = 0 AND e.id <= ?';
- $values = array ($id, $idMax);
- $stm = $this->bd->prepare ($sql);
- if (!($stm && $stm->execute ($values))) {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
- $this->bd->rollBack ();
- return false;
- }
- $affected = $stm->rowCount();
-
- if ($affected > 0) {
- $sql = 'UPDATE `' . $this->prefix . 'feed` f '
- . 'SET f.cache_nbUnreads=f.cache_nbUnreads-' . $affected
- . ' WHERE f.id=?';
- $values = array ($id);
- $stm = $this->bd->prepare ($sql);
- if (!($stm && $stm->execute ($values))) {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
- $this->bd->rollBack ();
- return false;
- }
- }
- $this->bd->commit ();
- return $affected;
- }
+ $this->bd->commit();
+ return $affected;
}
- public function searchByGuid ($feed_id, $id) {
+ public function searchByGuid($feed_id, $id) {
// un guid est unique pour un flux donné
- $sql = 'SELECT id, guid, title, author, UNCOMPRESS(content_bin) AS content, link, date, is_read, is_favorite, id_feed, tags '
+ $sql = 'SELECT id, guid, title, author, '
+ . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
+ . ', link, date, is_read, is_favorite, id_feed, tags '
. 'FROM `' . $this->prefix . 'entry` WHERE id_feed=? AND guid=?';
- $stm = $this->bd->prepare ($sql);
+ $stm = $this->bd->prepare($sql);
- $values = array (
+ $values = array(
$feed_id,
$id
);
- $stm->execute ($values);
- $res = $stm->fetchAll (PDO::FETCH_ASSOC);
- $entries = self::daoToEntry ($res);
- return isset ($entries[0]) ? $entries[0] : null;
+ $stm->execute($values);
+ $res = $stm->fetchAll(PDO::FETCH_ASSOC);
+ $entries = self::daoToEntry($res);
+ return isset($entries[0]) ? $entries[0] : null;
}
- public function searchById ($id) {
- $sql = 'SELECT id, guid, title, author, UNCOMPRESS(content_bin) AS content, link, date, is_read, is_favorite, id_feed, tags '
+ public function searchById($id) {
+ $sql = 'SELECT id, guid, title, author, '
+ . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
+ . ', link, date, is_read, is_favorite, id_feed, tags '
. 'FROM `' . $this->prefix . 'entry` WHERE id=?';
- $stm = $this->bd->prepare ($sql);
+ $stm = $this->bd->prepare($sql);
+
+ $values = array($id);
- $values = array ($id);
+ $stm->execute($values);
+ $res = $stm->fetchAll(PDO::FETCH_ASSOC);
+ $entries = self::daoToEntry($res);
+ return isset($entries[0]) ? $entries[0] : null;
+ }
- $stm->execute ($values);
- $res = $stm->fetchAll (PDO::FETCH_ASSOC);
- $entries = self::daoToEntry ($res);
- return isset ($entries[0]) ? $entries[0] : null;
+ protected function sqlConcat($s1, $s2) {
+ return 'CONCAT(' . $s1 . ',' . $s2 . ')'; //MySQL
}
private function sqlListWhere($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) {
@@ -419,39 +310,39 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
$joinFeed = true;
break;
case 's': //Deprecated: use $state instead
- $where .= 'e1.is_favorite = 1 ';
+ $where .= 'e1.is_favorite=1 ';
break;
case 'c':
- $where .= 'f.category = ? ';
+ $where .= 'f.category=? ';
$values[] = intval($id);
$joinFeed = true;
break;
case 'f':
- $where .= 'e1.id_feed = ? ';
+ $where .= 'e1.id_feed=? ';
$values[] = intval($id);
break;
case 'A':
$where .= '1 ';
break;
default:
- throw new FreshRSS_EntriesGetter_Exception ('Bad type in Entry->listByType: [' . $type . ']!');
+ throw new FreshRSS_EntriesGetter_Exception('Bad type in Entry->listByType: [' . $type . ']!');
}
if ($state & FreshRSS_Entry::STATE_NOT_READ) {
if (!($state & FreshRSS_Entry::STATE_READ)) {
- $where .= 'AND e1.is_read = 0 ';
+ $where .= 'AND e1.is_read=0 ';
}
}
elseif ($state & FreshRSS_Entry::STATE_READ) {
- $where .= 'AND e1.is_read = 1 ';
+ $where .= 'AND e1.is_read=1 ';
}
if ($state & FreshRSS_Entry::STATE_FAVORITE) {
if (!($state & FreshRSS_Entry::STATE_NOT_FAVORITE)) {
- $where .= 'AND e1.is_favorite = 1 ';
+ $where .= 'AND e1.is_favorite=1 ';
}
}
elseif ($state & FreshRSS_Entry::STATE_NOT_FAVORITE) {
- $where .= 'AND e1.is_favorite = 0 ';
+ $where .= 'AND e1.is_favorite=0 ';
}
switch ($order) {
@@ -459,7 +350,10 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
case 'ASC':
break;
default:
- throw new FreshRSS_EntriesGetter_Exception ('Bad order in Entry->listByType: [' . $order . ']!');
+ throw new FreshRSS_EntriesGetter_Exception('Bad order in Entry->listByType: [' . $order . ']!');
+ }
+ if ($firstId === '' && parent::$sharedDbType === 'mysql') {
+ $firstId = $order === 'DESC' ? '9000000000'. '000000' : '0'; //MySQL optimization. Tested on MySQL 5.5 with 150k articles
}
if ($firstId !== '') {
$where .= 'AND e1.id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' ';
@@ -467,7 +361,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
if (($date_min > 0) && ($type !== 's')) {
$where .= 'AND (e1.id >= ' . $date_min . '000000';
if ($showOlderUnreadsorFavorites) { //Lax date constraint
- $where .= ' OR e1.is_read = 0 OR e1.is_favorite = 1 OR (f.keep_history <> 0';
+ $where .= ' OR e1.is_read=0 OR e1.is_favorite=1 OR (f.keep_history <> 0';
if (intval($keepHistoryDefault) === 0) {
$where .= ' AND f.keep_history <> -2'; //default
}
@@ -520,7 +414,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
$search .= 'AND e1.tags LIKE ? ';
$values[] = '%' . $word .'%';
} else {
- $search .= 'AND CONCAT(e1.title, UNCOMPRESS(e1.content_bin)) LIKE ? ';
+ $search .= 'AND ' . $this->sqlconcat('e1.title', $this->isCompressed() ? 'UNCOMPRESS(content_bin)' : 'content') . ' LIKE ? ';
$values[] = '%' . $word .'%';
}
}
@@ -529,7 +423,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
return array($values,
'SELECT e1.id FROM `' . $this->prefix . 'entry` e1 '
- . ($joinFeed ? 'INNER JOIN `' . $this->prefix . 'feed` f ON e1.id_feed = f.id ' : '')
+ . ($joinFeed ? 'INNER JOIN `' . $this->prefix . 'feed` f ON e1.id_feed=f.id ' : '')
. 'WHERE ' . $where
. $search
. 'ORDER BY e1.id ' . $order
@@ -539,17 +433,19 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
public function listWhere($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) {
list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filter, $date_min, $showOlderUnreadsorFavorites, $keepHistoryDefault);
- $sql = 'SELECT e.id, e.guid, e.title, e.author, UNCOMPRESS(e.content_bin) AS content, e.link, e.date, e.is_read, e.is_favorite, e.id_feed, e.tags '
+ $sql = 'SELECT e.id, e.guid, e.title, e.author, '
+ . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
+ . ', e.link, e.date, e.is_read, e.is_favorite, e.id_feed, e.tags '
. 'FROM `' . $this->prefix . 'entry` e '
. 'INNER JOIN ('
. $sql
- . ') e2 ON e2.id = e.id '
+ . ') e2 ON e2.id=e.id '
. 'ORDER BY e.id ' . $order;
- $stm = $this->bd->prepare ($sql);
- $stm->execute ($values);
+ $stm = $this->bd->prepare($sql);
+ $stm->execute($values);
- return self::daoToEntry ($stm->fetchAll (PDO::FETCH_ASSOC));
+ return self::daoToEntry($stm->fetchAll(PDO::FETCH_ASSOC));
}
public function listIdsWhere($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) { //For API
@@ -563,69 +459,85 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
public function listLastGuidsByFeed($id, $n) {
$sql = 'SELECT guid FROM `' . $this->prefix . 'entry` WHERE id_feed=? ORDER BY id DESC LIMIT ' . intval($n);
- $stm = $this->bd->prepare ($sql);
- $values = array ($id);
- $stm->execute ($values);
- return $stm->fetchAll (PDO::FETCH_COLUMN, 0);
+ $stm = $this->bd->prepare($sql);
+ $values = array($id);
+ $stm->execute($values);
+ return $stm->fetchAll(PDO::FETCH_COLUMN, 0);
}
- public function countUnreadRead () {
- $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id WHERE priority > 0'
- . ' UNION SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id WHERE priority > 0 AND is_read = 0';
- $stm = $this->bd->prepare ($sql);
- $stm->execute ();
- $res = $stm->fetchAll (PDO::FETCH_COLUMN, 0);
+ public function countUnreadRead() {
+ $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE priority > 0'
+ . ' UNION SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE priority > 0 AND is_read=0';
+ $stm = $this->bd->prepare($sql);
+ $stm->execute();
+ $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
$all = empty($res[0]) ? 0 : $res[0];
$unread = empty($res[1]) ? 0 : $res[1];
return array('all' => $all, 'unread' => $unread, 'read' => $all - $unread);
}
- public function count ($minPriority = null) {
- $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id';
+ public function count($minPriority = null) {
+ $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id';
if ($minPriority !== null) {
$sql = ' WHERE priority > ' . intval($minPriority);
}
- $stm = $this->bd->prepare ($sql);
- $stm->execute ();
- $res = $stm->fetchAll (PDO::FETCH_COLUMN, 0);
+ $stm = $this->bd->prepare($sql);
+ $stm->execute();
+ $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
return $res[0];
}
- public function countNotRead ($minPriority = null) {
- $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id WHERE is_read = 0';
+ public function countNotRead($minPriority = null) {
+ $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE is_read=0';
if ($minPriority !== null) {
$sql = ' AND priority > ' . intval($minPriority);
}
- $stm = $this->bd->prepare ($sql);
- $stm->execute ();
- $res = $stm->fetchAll (PDO::FETCH_COLUMN, 0);
+ $stm = $this->bd->prepare($sql);
+ $stm->execute();
+ $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
return $res[0];
}
- public function countUnreadReadFavorites () {
- $sql = 'SELECT COUNT(id) FROM `' . $this->prefix . 'entry` WHERE is_favorite=1'
- . ' UNION SELECT COUNT(id) FROM `' . $this->prefix . 'entry` WHERE is_favorite=1 AND is_read = 0';
- $stm = $this->bd->prepare ($sql);
- $stm->execute ();
- $res = $stm->fetchAll (PDO::FETCH_COLUMN, 0);
+ public function countUnreadReadFavorites() {
+ $sql = 'SELECT c FROM ('
+ . 'SELECT COUNT(id) AS c, 1 as o FROM `' . $this->prefix . 'entry` WHERE is_favorite=1 '
+ . 'UNION SELECT COUNT(id) AS c, 2 AS o FROM `' . $this->prefix . 'entry` WHERE is_favorite=1 AND is_read=0'
+ . ') u ORDER BY o';
+ $stm = $this->bd->prepare($sql);
+ $stm->execute();
+ $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
$all = empty($res[0]) ? 0 : $res[0];
$unread = empty($res[1]) ? 0 : $res[1];
return array('all' => $all, 'unread' => $unread, 'read' => $all - $unread);
}
public function optimizeTable() {
- $sql = 'OPTIMIZE TABLE `' . $this->prefix . 'entry`';
- $stm = $this->bd->prepare ($sql);
- $stm->execute ();
+ $sql = 'OPTIMIZE TABLE `' . $this->prefix . 'entry`'; //MySQL
+ $stm = $this->bd->prepare($sql);
+ $stm->execute();
+ }
+
+ public function size($all = false) {
+ $db = Minz_Configuration::dataBase();
+ $sql = 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema=?'; //MySQL
+ $values = array($db['base']);
+ if (!$all) {
+ $sql .= ' AND table_name LIKE ?';
+ $values[] = $this->prefix . '%';
+ }
+ $stm = $this->bd->prepare($sql);
+ $stm->execute($values);
+ $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
+ return $res[0];
}
- public static function daoToEntry ($listDAO) {
- $list = array ();
+ public static function daoToEntry($listDAO) {
+ $list = array();
- if (!is_array ($listDAO)) {
- $listDAO = array ($listDAO);
+ if (!is_array($listDAO)) {
+ $listDAO = array($listDAO);
}
foreach ($listDAO as $key => $dao) {
- $entry = new FreshRSS_Entry (
+ $entry = new FreshRSS_Entry(
$dao['id_feed'],
$dao['guid'],
$dao['title'],
@@ -637,13 +549,13 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
$dao['is_favorite'],
$dao['tags']
);
- if (isset ($dao['id'])) {
- $entry->_id ($dao['id']);
+ if (isset($dao['id'])) {
+ $entry->_id($dao['id']);
}
$list[] = $entry;
}
- unset ($listDAO);
+ unset($listDAO);
return $list;
}
diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php
new file mode 100644
index 000000000..3dabce4b2
--- /dev/null
+++ b/app/Models/EntryDAOSQLite.php
@@ -0,0 +1,129 @@
+<?php
+
+class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO {
+
+ protected function sqlConcat($s1, $s2) {
+ return $s1 . '||' . $s2;
+ }
+
+ protected function updateCacheUnreads($catId = false, $feedId = false) {
+ $sql = 'UPDATE `' . $this->prefix . 'feed` '
+ . 'SET cache_nbUnreads=('
+ . 'SELECT COUNT(*) AS nbUnreads FROM `' . $this->prefix . 'entry` e '
+ . 'WHERE e.id_feed=`' . $this->prefix . 'feed`.id AND e.is_read=0) '
+ . 'WHERE 1';
+ $values = array();
+ if ($feedId !== false) {
+ $sql .= ' AND id=?';
+ $values[] = $feedId;
+ }
+ if ($catId !== false) {
+ $sql .= ' AND category=?';
+ $values[] = $catId;
+ }
+ $stm = $this->bd->prepare($sql);
+ if ($stm && $stm->execute($values)) {
+ return true;
+ } else {
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error updateCacheUnreads: ' . $info[2], Minz_Log::ERROR);
+ return false;
+ }
+ }
+
+ public function markRead($ids, $is_read = true) {
+ if (is_array($ids)) { //Many IDs at once (used by API)
+ if (true) { //Speed heuristics //TODO: Not implemented yet for SQLite (so always call IDs one by one)
+ $affected = 0;
+ foreach ($ids as $id) {
+ $affected += $this->markRead($id, $is_read);
+ }
+ return $affected;
+ }
+ } else {
+ $this->bd->beginTransaction();
+ $sql = 'UPDATE `' . $this->prefix . 'entry` SET is_read=? WHERE id=? AND is_read=?';
+ $values = array($is_read ? 1 : 0, $ids, $is_read ? 0 : 1);
+ $stm = $this->bd->prepare($sql);
+ if (!($stm && $stm->execute($values))) {
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error markRead 1: ' . $info[2], Minz_Log::ERROR);
+ $this->bd->rollBack();
+ return false;
+ }
+ $affected = $stm->rowCount();
+ if ($affected > 0) {
+ $sql = 'UPDATE `' . $this->prefix . 'feed` SET cache_nbUnreads=cache_nbUnreads' . ($is_read ? '-' : '+') . '1 '
+ . 'WHERE id=(SELECT e.id_feed FROM `' . $this->prefix . 'entry` e WHERE e.id=?)';
+ $values = array($ids);
+ $stm = $this->bd->prepare($sql);
+ if (!($stm && $stm->execute($values))) {
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error markRead 2: ' . $info[2], Minz_Log::ERROR);
+ $this->bd->rollBack();
+ return false;
+ }
+ }
+ $this->bd->commit();
+ return $affected;
+ }
+ }
+
+ public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0) {
+ if ($idMax == 0) {
+ $idMax = time() . '000000';
+ Minz_Log::record($nb . 'Calling markReadEntries(0) is deprecated!', Minz_Log::DEBUG);
+ }
+
+ $sql = 'UPDATE `' . $this->prefix . 'entry` SET is_read=1 WHERE is_read=0 AND id <= ?';
+ if ($onlyFavorites) {
+ $sql .= ' AND is_favorite=1';
+ } elseif ($priorityMin >= 0) {
+ $sql .= ' AND id_feed IN (SELECT f.id FROM `' . $this->prefix . 'feed` f WHERE f.priority > ' . intval($priorityMin) . ')';
+ }
+ $values = array($idMax);
+ $stm = $this->bd->prepare($sql);
+ if (!($stm && $stm->execute($values))) {
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error markReadEntries: ' . $info[2], Minz_Log::ERROR);
+ return false;
+ }
+ $affected = $stm->rowCount();
+ if (($affected > 0) && (!$this->updateCacheUnreads(false, false))) {
+ return false;
+ }
+ return $affected;
+ }
+
+ public function markReadCat($id, $idMax = 0) {
+ if ($idMax == 0) {
+ $idMax = time() . '000000';
+ Minz_Log::record($nb . 'Calling markReadCat(0) is deprecated!', Minz_Log::DEBUG);
+ }
+
+ $sql = 'UPDATE `' . $this->prefix . 'entry` '
+ . 'SET is_read=1 '
+ . 'WHERE is_read=0 AND id <= ? AND '
+ . 'id_feed IN (SELECT f.id FROM `' . $this->prefix . 'feed` f WHERE f.category=?)';
+ $values = array($idMax, $id);
+ $stm = $this->bd->prepare($sql);
+ if (!($stm && $stm->execute($values))) {
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error markReadCat: ' . $info[2], Minz_Log::ERROR);
+ return false;
+ }
+ $affected = $stm->rowCount();
+ if (($affected > 0) && (!$this->updateCacheUnreads($id, false))) {
+ return false;
+ }
+ return $affected;
+ }
+
+ public function optimizeTable() {
+ //TODO: Search for an equivalent in SQLite
+ }
+
+ public function size($all = false) {
+ return @filesize(DATA_PATH . '/' . Minz_Session::param('currentUser', '_') . '.sqlite');
+ }
+}
diff --git a/app/Models/Factory.php b/app/Models/Factory.php
new file mode 100644
index 000000000..08569b2e2
--- /dev/null
+++ b/app/Models/Factory.php
@@ -0,0 +1,32 @@
+<?php
+
+class FreshRSS_Factory {
+
+ public static function createFeedDao() {
+ $db = Minz_Configuration::dataBase();
+ if ($db['type'] === 'sqlite') {
+ return new FreshRSS_FeedDAOSQLite();
+ } else {
+ return new FreshRSS_FeedDAO();
+ }
+ }
+
+ public static function createEntryDao() {
+ $db = Minz_Configuration::dataBase();
+ if ($db['type'] === 'sqlite') {
+ return new FreshRSS_EntryDAOSQLite();
+ } else {
+ return new FreshRSS_EntryDAO();
+ }
+ }
+
+ public static function createStatsDAO() {
+ $db = Minz_Configuration::dataBase();
+ if ($db['type'] === 'sqlite') {
+ return new FreshRSS_StatsDAOSQLite();
+ } else {
+ return new FreshRSS_StatsDAO();
+ }
+ }
+
+}
diff --git a/app/Models/Feed.php b/app/Models/Feed.php
index 757eacd59..576f37760 100644
--- a/app/Models/Feed.php
+++ b/app/Models/Feed.php
@@ -16,17 +16,19 @@ class FreshRSS_Feed extends Minz_Model {
private $httpAuth = '';
private $error = false;
private $keep_history = -2;
+ private $ttl = -2;
private $hash = null;
+ private $lockPath = '';
- public function __construct ($url, $validate=true) {
+ public function __construct($url, $validate=true) {
if ($validate) {
- $this->_url ($url);
+ $this->_url($url);
} else {
$this->url = $url;
}
}
- public function id () {
+ public function id() {
return $this->id;
}
@@ -37,74 +39,77 @@ class FreshRSS_Feed extends Minz_Model {
return $this->hash;
}
- public function url () {
+ public function url() {
return $this->url;
}
- public function category () {
+ public function category() {
return $this->category;
}
- public function entries () {
+ public function entries() {
return $this->entries === null ? array() : $this->entries;
}
- public function name () {
+ public function name() {
return $this->name;
}
- public function website () {
+ public function website() {
return $this->website;
}
- public function description () {
+ public function description() {
return $this->description;
}
- public function lastUpdate () {
+ public function lastUpdate() {
return $this->lastUpdate;
}
- public function priority () {
+ public function priority() {
return $this->priority;
}
- public function pathEntries () {
+ public function pathEntries() {
return $this->pathEntries;
}
- public function httpAuth ($raw = true) {
+ public function httpAuth($raw = true) {
if ($raw) {
return $this->httpAuth;
} else {
- $pos_colon = strpos ($this->httpAuth, ':');
- $user = substr ($this->httpAuth, 0, $pos_colon);
- $pass = substr ($this->httpAuth, $pos_colon + 1);
+ $pos_colon = strpos($this->httpAuth, ':');
+ $user = substr($this->httpAuth, 0, $pos_colon);
+ $pass = substr($this->httpAuth, $pos_colon + 1);
- return array (
+ return array(
'username' => $user,
'password' => $pass
);
}
}
- public function inError () {
+ public function inError() {
return $this->error;
}
- public function keepHistory () {
+ public function keepHistory() {
return $this->keep_history;
}
- public function nbEntries () {
+ public function ttl() {
+ return $this->ttl;
+ }
+ public function nbEntries() {
if ($this->nbEntries < 0) {
- $feedDAO = new FreshRSS_FeedDAO ();
- $this->nbEntries = $feedDAO->countEntries ($this->id ());
+ $feedDAO = FreshRSS_Factory::createFeedDao();
+ $this->nbEntries = $feedDAO->countEntries($this->id());
}
return $this->nbEntries;
}
- public function nbNotRead () {
+ public function nbNotRead() {
if ($this->nbNotRead < 0) {
- $feedDAO = new FreshRSS_FeedDAO ();
- $this->nbNotRead = $feedDAO->countNotRead ($this->id ());
+ $feedDAO = FreshRSS_Factory::createFeedDao();
+ $this->nbNotRead = $feedDAO->countNotRead($this->id());
}
return $this->nbNotRead;
}
public function faviconPrepare() {
$file = DATA_PATH . '/favicons/' . $this->hash() . '.txt';
- if (!file_exists ($file)) {
+ if (!file_exists($file)) {
$t = $this->website;
- if (empty($t)) {
+ if ($t == '') {
$t = $this->url;
}
file_put_contents($file, $t);
@@ -115,109 +120,123 @@ class FreshRSS_Feed extends Minz_Model {
@unlink($path . '.ico');
@unlink($path . '.txt');
}
- public function favicon () {
- return Minz_Url::display ('/f.php?' . $this->hash());
+ public function favicon() {
+ return Minz_Url::display('/f.php?' . $this->hash());
}
- public function _id ($value) {
+ public function _id($value) {
$this->id = $value;
}
- public function _url ($value, $validate=true) {
+ public function _url($value, $validate=true) {
+ $this->hash = null;
if ($validate) {
$value = checkUrl($value);
}
- if (empty ($value)) {
- throw new FreshRSS_BadUrl_Exception ($value);
+ if (empty($value)) {
+ throw new FreshRSS_BadUrl_Exception($value);
}
$this->url = $value;
}
- public function _category ($value) {
+ public function _category($value) {
$value = intval($value);
$this->category = $value >= 0 ? $value : 0;
}
- public function _name ($value) {
+ public function _name($value) {
$this->name = $value === null ? '' : $value;
}
- public function _website ($value, $validate=true) {
+ public function _website($value, $validate=true) {
if ($validate) {
$value = checkUrl($value);
}
- if (empty ($value)) {
+ if (empty($value)) {
$value = '';
}
$this->website = $value;
}
- public function _description ($value) {
+ public function _description($value) {
$this->description = $value === null ? '' : $value;
}
- public function _lastUpdate ($value) {
+ public function _lastUpdate($value) {
$this->lastUpdate = $value;
}
- public function _priority ($value) {
+ public function _priority($value) {
$value = intval($value);
$this->priority = $value >= 0 ? $value : 10;
}
- public function _pathEntries ($value) {
+ public function _pathEntries($value) {
$this->pathEntries = $value;
}
- public function _httpAuth ($value) {
+ public function _httpAuth($value) {
$this->httpAuth = $value;
}
- public function _error ($value) {
+ public function _error($value) {
$this->error = (bool)$value;
}
- public function _keepHistory ($value) {
+ public function _keepHistory($value) {
$value = intval($value);
$value = min($value, 1000000);
$value = max($value, -2);
$this->keep_history = $value;
}
- public function _nbNotRead ($value) {
+ public function _ttl($value) {
+ $value = intval($value);
+ $value = min($value, 100000000);
+ $value = max($value, -2);
+ $this->ttl = $value;
+ }
+ public function _nbNotRead($value) {
$this->nbNotRead = intval($value);
}
- public function _nbEntries ($value) {
+ public function _nbEntries($value) {
$this->nbEntries = intval($value);
}
- public function load ($loadDetails = false) {
+ public function load($loadDetails = false) {
if ($this->url !== null) {
if (CACHE_PATH === false) {
- throw new Minz_FileNotExistException (
+ throw new Minz_FileNotExistException(
'CACHE_PATH',
Minz_Exception::ERROR
);
} else {
- $url = htmlspecialchars_decode ($this->url, ENT_QUOTES);
+ $url = htmlspecialchars_decode($this->url, ENT_QUOTES);
if ($this->httpAuth != '') {
- $url = preg_replace ('#((.+)://)(.+)#', '${1}' . $this->httpAuth . '@${3}', $url);
+ $url = preg_replace('#((.+)://)(.+)#', '${1}' . $this->httpAuth . '@${3}', $url);
}
$feed = customSimplePie();
- $feed->set_feed_url ($url);
+ $feed->set_feed_url($url);
+ if (!$loadDetails) { //Only activates auto-discovery when adding a new feed
+ $feed->set_autodiscovery_level(SIMPLEPIE_LOCATOR_NONE);
+ }
$mtime = $feed->init();
if ((!$mtime) || $feed->error()) {
- throw new FreshRSS_Feed_Exception ($feed->error() . ' [' . $url . ']');
- }
-
- // si on a utilisé l'auto-discover, notre url va avoir changé
- $subscribe_url = $feed->subscribe_url ();
- if ($subscribe_url !== null && $subscribe_url !== $this->url) {
- if ($this->httpAuth != '') {
- // on enlève les id si authentification HTTP
- $subscribe_url = preg_replace ('#((.+)://)((.+)@)(.+)#', '${1}${5}', $subscribe_url);
- }
- $this->_url ($subscribe_url);
+ throw new FreshRSS_Feed_Exception($feed->error() . ' [' . $url . ']');
}
if ($loadDetails) {
+ // si on a utilisé l'auto-discover, notre url va avoir changé
+ $subscribe_url = $feed->subscribe_url(false);
+
$title = strtr(html_only_entity_decode($feed->get_title()), array('<' => '&lt;', '>' => '&gt;', '"' => '&quot;')); //HTML to HTML-PRE //ENT_COMPAT except &
- $this->_name ($title == '' ? $this->url : $title);
+ $this->_name($title == '' ? $this->url : $title);
$this->_website(html_only_entity_decode($feed->get_link()));
$this->_description(html_only_entity_decode($feed->get_description()));
+ } else {
+ //The case of HTTP 301 Moved Permanently
+ $subscribe_url = $feed->subscribe_url(true);
+ }
+
+ if ($subscribe_url !== null && $subscribe_url !== $this->url) {
+ if ($this->httpAuth != '') {
+ // on enlève les id si authentification HTTP
+ $subscribe_url = preg_replace('#((.+)://)((.+)@)(.+)#', '${1}${5}', $subscribe_url);
+ }
+ $this->_url($subscribe_url);
}
- if (($mtime === true) || ($mtime > $this->lastUpdate)) {
+ if (($mtime === true) ||($mtime > $this->lastUpdate)) {
syslog(LOG_DEBUG, 'FreshRSS no cache ' . $mtime . ' > ' . $this->lastUpdate . ' for ' . $subscribe_url);
$this->loadEntries($feed); // et on charge les articles du flux
} else {
@@ -231,25 +250,25 @@ class FreshRSS_Feed extends Minz_Model {
}
}
- private function loadEntries ($feed) {
- $entries = array ();
+ private function loadEntries($feed) {
+ $entries = array();
- foreach ($feed->get_items () as $item) {
- $title = html_only_entity_decode (strip_tags ($item->get_title ()));
- $author = $item->get_author ();
- $link = $item->get_permalink ();
- $date = @strtotime ($item->get_date ());
+ foreach ($feed->get_items() as $item) {
+ $title = html_only_entity_decode(strip_tags($item->get_title()));
+ $author = $item->get_author();
+ $link = $item->get_permalink();
+ $date = @strtotime($item->get_date());
// gestion des tags (catégorie == tag)
- $tags_tmp = $item->get_categories ();
- $tags = array ();
+ $tags_tmp = $item->get_categories();
+ $tags = array();
if ($tags_tmp !== null) {
foreach ($tags_tmp as $tag) {
- $tags[] = html_only_entity_decode ($tag->get_label ());
+ $tags[] = html_only_entity_decode($tag->get_label());
}
}
- $content = html_only_entity_decode ($item->get_content ());
+ $content = html_only_entity_decode($item->get_content());
$elinks = array();
foreach ($item->get_enclosures() as $enclosure) {
@@ -267,16 +286,16 @@ class FreshRSS_Feed extends Minz_Model {
}
}
- $entry = new FreshRSS_Entry (
- $this->id (),
- $item->get_id (),
+ $entry = new FreshRSS_Entry(
+ $this->id(),
+ $item->get_id(),
$title === null ? '' : $title,
- $author === null ? '' : html_only_entity_decode ($author->name),
+ $author === null ? '' : html_only_entity_decode($author->name),
$content === null ? '' : $content,
$link === null ? '' : $link,
- $date ? $date : time ()
+ $date ? $date : time()
);
- $entry->_tags ($tags);
+ $entry->_tags($tags);
// permet de récupérer le contenu des flux tronqués
$entry->loadCompleteContent($this->pathEntries());
@@ -288,20 +307,19 @@ class FreshRSS_Feed extends Minz_Model {
}
function lock() {
- $lock = TMP_PATH . '/' . md5(Minz_Configuration::salt() . $this->url) . '.freshrss.lock';
- if (file_exists($lock) && ((time() - @filemtime($lock)) > 3600)) {
- @unlink($lock);
+ $this->lockPath = TMP_PATH . '/' . $this->hash() . '.freshrss.lock';
+ if (file_exists($this->lockPath) && ((time() - @filemtime($this->lockPath)) > 3600)) {
+ @unlink($this->lockPath);
}
- if (($handle = @fopen($lock, 'x')) === false) {
+ if (($handle = @fopen($this->lockPath, 'x')) === false) {
return false;
}
- //register_shutdown_function('unlink', $lock);
+ //register_shutdown_function('unlink', $this->lockPath);
@fclose($handle);
return true;
}
function unlock() {
- $lock = TMP_PATH . '/' . md5(Minz_Configuration::salt() . $this->url) . '.freshrss.lock';
- @unlink($lock);
+ @unlink($this->lockPath);
}
}
diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php
index b65ff4af0..756b1f008 100644
--- a/app/Models/FeedDAO.php
+++ b/app/Models/FeedDAO.php
@@ -1,25 +1,25 @@
<?php
class FreshRSS_FeedDAO extends Minz_ModelPdo {
- public function addFeed ($valuesTmp) {
- $sql = 'INSERT INTO `' . $this->prefix . 'feed` (url, category, name, website, description, lastUpdate, priority, httpAuth, error, keep_history) VALUES(?, ?, ?, ?, ?, ?, 10, ?, 0, -2)';
- $stm = $this->bd->prepare ($sql);
+ public function addFeed($valuesTmp) {
+ $sql = 'INSERT INTO `' . $this->prefix . 'feed` (url, category, name, website, description, lastUpdate, priority, httpAuth, error, keep_history, ttl) VALUES(?, ?, ?, ?, ?, ?, 10, ?, 0, -2, -2)';
+ $stm = $this->bd->prepare($sql);
- $values = array (
+ $values = array(
substr($valuesTmp['url'], 0, 511),
$valuesTmp['category'],
substr($valuesTmp['name'], 0, 255),
substr($valuesTmp['website'], 0, 255),
substr($valuesTmp['description'], 0, 1023),
$valuesTmp['lastUpdate'],
- base64_encode ($valuesTmp['httpAuth']),
+ base64_encode($valuesTmp['httpAuth']),
);
- if ($stm && $stm->execute ($values)) {
+ if ($stm && $stm->execute($values)) {
return $this->bd->lastInsertId();
} else {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error addFeed: ' . $info[2], Minz_Log::ERROR);
return false;
}
}
@@ -54,185 +54,163 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
return $feed_search->id();
}
- public function updateFeed ($id, $valuesTmp) {
+ public function updateFeed($id, $valuesTmp) {
$set = '';
foreach ($valuesTmp as $key => $v) {
$set .= $key . '=?, ';
if ($key == 'httpAuth') {
- $valuesTmp[$key] = base64_encode ($v);
+ $valuesTmp[$key] = base64_encode($v);
}
}
- $set = substr ($set, 0, -2);
+ $set = substr($set, 0, -2);
$sql = 'UPDATE `' . $this->prefix . 'feed` SET ' . $set . ' WHERE id=?';
- $stm = $this->bd->prepare ($sql);
+ $stm = $this->bd->prepare($sql);
foreach ($valuesTmp as $v) {
$values[] = $v;
}
$values[] = $id;
- if ($stm && $stm->execute ($values)) {
+ if ($stm && $stm->execute($values)) {
return $stm->rowCount();
} else {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error updateFeed: ' . $info[2], Minz_Log::ERROR);
return false;
}
}
- public function updateLastUpdate ($id, $inError = 0, $updateCache = true) {
+ public function updateLastUpdate($id, $inError = 0, $updateCache = true) {
if ($updateCache) {
- $sql = 'UPDATE `' . $this->prefix . 'feed` f ' //2 sub-requests with FOREIGN KEY(e.id_feed), INDEX(e.is_read) faster than 1 request with GROUP BY or CASE
- . 'SET f.cache_nbEntries=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=f.id),'
- . 'f.cache_nbUnreads=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=f.id AND e2.is_read=0),'
+ $sql = 'UPDATE `' . $this->prefix . 'feed` ' //2 sub-requests with FOREIGN KEY(e.id_feed), INDEX(e.is_read) faster than 1 request with GROUP BY or CASE
+ . 'SET cache_nbEntries=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=`' . $this->prefix . 'feed`.id),'
+ . 'cache_nbUnreads=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=`' . $this->prefix . 'feed`.id AND e2.is_read=0),'
. 'lastUpdate=?, error=? '
- . 'WHERE f.id=?';
+ . 'WHERE id=?';
} else {
- $sql = 'UPDATE `' . $this->prefix . 'feed` f '
+ $sql = 'UPDATE `' . $this->prefix . 'feed` '
. 'SET lastUpdate=?, error=? '
- . 'WHERE f.id=?';
+ . 'WHERE id=?';
}
- $values = array (
+ $values = array(
time(),
$inError,
$id,
);
- $stm = $this->bd->prepare ($sql);
+ $stm = $this->bd->prepare($sql);
- if ($stm && $stm->execute ($values)) {
+ if ($stm && $stm->execute($values)) {
return $stm->rowCount();
} else {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error updateLastUpdate: ' . $info[2], Minz_Log::ERROR);
return false;
}
}
- public function changeCategory ($idOldCat, $idNewCat) {
- $catDAO = new FreshRSS_CategoryDAO ();
- $newCat = $catDAO->searchById ($idNewCat);
+ public function changeCategory($idOldCat, $idNewCat) {
+ $catDAO = new FreshRSS_CategoryDAO();
+ $newCat = $catDAO->searchById($idNewCat);
if (!$newCat) {
- $newCat = $catDAO->getDefault ();
+ $newCat = $catDAO->getDefault();
}
$sql = 'UPDATE `' . $this->prefix . 'feed` SET category=? WHERE category=?';
- $stm = $this->bd->prepare ($sql);
+ $stm = $this->bd->prepare($sql);
- $values = array (
- $newCat->id (),
+ $values = array(
+ $newCat->id(),
$idOldCat
);
- if ($stm && $stm->execute ($values)) {
+ if ($stm && $stm->execute($values)) {
return $stm->rowCount();
} else {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error changeCategory: ' . $info[2], Minz_Log::ERROR);
return false;
}
}
- public function deleteFeed ($id) {
- /*//For MYISAM (MySQL 5.5-) without FOREIGN KEY
- $sql = 'DELETE FROM `' . $this->prefix . 'entry` WHERE id_feed=?';
- $stm = $this->bd->prepare ($sql);
- $values = array ($id);
- if (!($stm && $stm->execute ($values))) {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
- return false;
- }*/
-
+ public function deleteFeed($id) {
$sql = 'DELETE FROM `' . $this->prefix . 'feed` WHERE id=?';
- $stm = $this->bd->prepare ($sql);
+ $stm = $this->bd->prepare($sql);
- $values = array ($id);
+ $values = array($id);
- if ($stm && $stm->execute ($values)) {
+ if ($stm && $stm->execute($values)) {
return $stm->rowCount();
} else {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error deleteFeed: ' . $info[2], Minz_Log::ERROR);
return false;
}
}
- public function deleteFeedByCategory ($id) {
- /*//For MYISAM (MySQL 5.5-) without FOREIGN KEY
- $sql = 'DELETE FROM `' . $this->prefix . 'entry` e '
- . 'INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
- . 'WHERE f.category=?';
- $stm = $this->bd->prepare ($sql);
- $values = array ($id);
- if (!($stm && $stm->execute ($values))) {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
- return false;
- }*/
-
+ public function deleteFeedByCategory($id) {
$sql = 'DELETE FROM `' . $this->prefix . 'feed` WHERE category=?';
- $stm = $this->bd->prepare ($sql);
+ $stm = $this->bd->prepare($sql);
- $values = array ($id);
+ $values = array($id);
- if ($stm && $stm->execute ($values)) {
+ if ($stm && $stm->execute($values)) {
return $stm->rowCount();
} else {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error deleteFeedByCategory: ' . $info[2], Minz_Log::ERROR);
return false;
}
}
- public function searchById ($id) {
+ public function searchById($id) {
$sql = 'SELECT * FROM `' . $this->prefix . 'feed` WHERE id=?';
- $stm = $this->bd->prepare ($sql);
+ $stm = $this->bd->prepare($sql);
- $values = array ($id);
+ $values = array($id);
- $stm->execute ($values);
- $res = $stm->fetchAll (PDO::FETCH_ASSOC);
- $feed = self::daoToFeed ($res);
+ $stm->execute($values);
+ $res = $stm->fetchAll(PDO::FETCH_ASSOC);
+ $feed = self::daoToFeed($res);
- if (isset ($feed[$id])) {
+ if (isset($feed[$id])) {
return $feed[$id];
} else {
return null;
}
}
- public function searchByUrl ($url) {
+ public function searchByUrl($url) {
$sql = 'SELECT * FROM `' . $this->prefix . 'feed` WHERE url=?';
- $stm = $this->bd->prepare ($sql);
+ $stm = $this->bd->prepare($sql);
- $values = array ($url);
+ $values = array($url);
- $stm->execute ($values);
- $res = $stm->fetchAll (PDO::FETCH_ASSOC);
- $feed = current (self::daoToFeed ($res));
+ $stm->execute($values);
+ $res = $stm->fetchAll(PDO::FETCH_ASSOC);
+ $feed = current(self::daoToFeed($res));
- if (isset ($feed)) {
+ if (isset($feed)) {
return $feed;
} else {
return null;
}
}
- public function listFeeds () {
+ public function listFeeds() {
$sql = 'SELECT * FROM `' . $this->prefix . 'feed` ORDER BY name';
- $stm = $this->bd->prepare ($sql);
- $stm->execute ();
+ $stm = $this->bd->prepare($sql);
+ $stm->execute();
- return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC));
+ return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC));
}
public function arrayFeedCategoryNames() { //For API
$sql = 'SELECT f.id, f.name, c.name as c_name FROM `' . $this->prefix . 'feed` f '
. 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category';
- $stm = $this->bd->prepare ($sql);
- $stm->execute ();
+ $stm = $this->bd->prepare($sql);
+ $stm->execute();
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
$feedCategoryNames = array();
foreach ($res as $line) {
@@ -244,49 +222,58 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
return $feedCategoryNames;
}
- public function listFeedsOrderUpdate ($cacheDuration = 1500) {
- $sql = 'SELECT id, name, url, lastUpdate, pathEntries, httpAuth, keep_history '
+ public function listFeedsOrderUpdate($defaultCacheDuration = 3600) {
+ if ($defaultCacheDuration < 0) {
+ $defaultCacheDuration = 2147483647;
+ }
+ $sql = 'SELECT id, url, name, website, lastUpdate, pathEntries, httpAuth, keep_history, ttl '
. 'FROM `' . $this->prefix . 'feed` '
- . 'WHERE lastUpdate < ' . (time() - intval($cacheDuration))
- . ' ORDER BY lastUpdate';
- $stm = $this->bd->prepare ($sql);
- $stm->execute ();
+ . 'WHERE ttl <> -1 AND lastUpdate < (' . (time() + 60) . '-(CASE WHEN ttl=-2 THEN ' . intval($defaultCacheDuration) . ' ELSE ttl END)) '
+ . 'ORDER BY lastUpdate';
+ $stm = $this->bd->prepare($sql);
+ if (!($stm && $stm->execute())) {
+ $sql2 = 'ALTER TABLE `' . $this->prefix . 'feed` ADD COLUMN ttl INT NOT NULL DEFAULT -2'; //v0.7.3
+ $stm = $this->bd->prepare($sql2);
+ $stm->execute();
+ $stm = $this->bd->prepare($sql);
+ $stm->execute();
+ }
- return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC));
+ return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC));
}
- public function listByCategory ($cat) {
+ public function listByCategory($cat) {
$sql = 'SELECT * FROM `' . $this->prefix . 'feed` WHERE category=? ORDER BY name';
- $stm = $this->bd->prepare ($sql);
+ $stm = $this->bd->prepare($sql);
- $values = array ($cat);
+ $values = array($cat);
- $stm->execute ($values);
+ $stm->execute($values);
- return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC));
+ return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC));
}
- public function countEntries ($id) {
+ public function countEntries($id) {
$sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'entry` WHERE id_feed=?';
- $stm = $this->bd->prepare ($sql);
- $values = array ($id);
- $stm->execute ($values);
- $res = $stm->fetchAll (PDO::FETCH_ASSOC);
+ $stm = $this->bd->prepare($sql);
+ $values = array($id);
+ $stm->execute($values);
+ $res = $stm->fetchAll(PDO::FETCH_ASSOC);
return $res[0]['count'];
}
- public function countNotRead ($id) {
+ public function countNotRead($id) {
$sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'entry` WHERE id_feed=? AND is_read=0';
- $stm = $this->bd->prepare ($sql);
- $values = array ($id);
- $stm->execute ($values);
- $res = $stm->fetchAll (PDO::FETCH_ASSOC);
+ $stm = $this->bd->prepare($sql);
+ $values = array($id);
+ $stm->execute($values);
+ $res = $stm->fetchAll(PDO::FETCH_ASSOC);
return $res[0]['count'];
}
- public function updateCachedValues () { //For one single feed, call updateLastUpdate($id)
+ public function updateCachedValues() { //For one single feed, call updateLastUpdate($id)
$sql = 'UPDATE `' . $this->prefix . 'feed` f '
. 'INNER JOIN ('
. 'SELECT e.id_feed, '
@@ -296,50 +283,50 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
. 'GROUP BY e.id_feed'
. ') x ON x.id_feed=f.id '
. 'SET f.cache_nbEntries=x.nbEntries, f.cache_nbUnreads=x.nbUnreads';
- $stm = $this->bd->prepare ($sql);
+ $stm = $this->bd->prepare($sql);
if ($stm && $stm->execute()) {
return $stm->rowCount();
} else {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error updateCachedValues: ' . $info[2], Minz_Log::ERROR);
return false;
}
}
- public function truncate ($id) {
- $sql = 'DELETE e.* FROM `' . $this->prefix . 'entry` e WHERE e.id_feed=?';
+ public function truncate($id) {
+ $sql = 'DELETE FROM `' . $this->prefix . 'entry` WHERE id_feed=?';
$stm = $this->bd->prepare($sql);
$values = array($id);
- $this->bd->beginTransaction ();
- if (!($stm && $stm->execute ($values))) {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
- $this->bd->rollBack ();
- return false;
- }
+ $this->bd->beginTransaction();
+ if (!($stm && $stm->execute($values))) {
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error truncate: ' . $info[2], Minz_Log::ERROR);
+ $this->bd->rollBack();
+ return false;
+ }
$affected = $stm->rowCount();
- $sql = 'UPDATE `' . $this->prefix . 'feed` f '
- . 'SET f.cache_nbEntries=0, f.cache_nbUnreads=0 WHERE f.id=?';
- $values = array ($id);
- $stm = $this->bd->prepare ($sql);
- if (!($stm && $stm->execute ($values))) {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
- $this->bd->rollBack ();
+ $sql = 'UPDATE `' . $this->prefix . 'feed` '
+ . 'SET cache_nbEntries=0, cache_nbUnreads=0 WHERE id=?';
+ $values = array($id);
+ $stm = $this->bd->prepare($sql);
+ if (!($stm && $stm->execute($values))) {
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error truncate: ' . $info[2], Minz_Log::ERROR);
+ $this->bd->rollBack();
return false;
}
- $this->bd->commit ();
+ $this->bd->commit();
return $affected;
}
- public function cleanOldEntries ($id, $date_min, $keep = 15) { //Remember to call updateLastUpdate($id) just after
- $sql = 'DELETE e.* FROM `' . $this->prefix . 'entry` e '
- . 'WHERE e.id_feed = :id_feed AND e.id <= :id_max AND e.is_favorite = 0 AND e.id NOT IN '
- . '(SELECT id FROM (SELECT e2.id FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed = :id_feed ORDER BY id DESC LIMIT :keep) keep)'; //Double select because of: MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
- $stm = $this->bd->prepare ($sql);
+ public function cleanOldEntries($id, $date_min, $keep = 15) { //Remember to call updateLastUpdate($id) just after
+ $sql = 'DELETE FROM `' . $this->prefix . 'entry` '
+ . 'WHERE id_feed = :id_feed AND id <= :id_max AND is_favorite=0 AND id NOT IN '
+ . '(SELECT id FROM (SELECT e2.id FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed = :id_feed ORDER BY id DESC LIMIT :keep) keep)'; //Double select MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
+ $stm = $this->bd->prepare($sql);
$id_max = intval($date_min) . '000000';
@@ -347,27 +334,27 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
$stm->bindParam(':id_max', $id_max, PDO::PARAM_INT);
$stm->bindParam(':keep', $keep, PDO::PARAM_INT);
- if ($stm && $stm->execute ()) {
+ if ($stm && $stm->execute()) {
return $stm->rowCount();
} else {
- $info = $stm->errorInfo();
- Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error cleanOldEntries: ' . $info[2], Minz_Log::ERROR);
return false;
}
}
- public static function daoToFeed ($listDAO, $catID = null) {
- $list = array ();
+ public static function daoToFeed($listDAO, $catID = null) {
+ $list = array();
- if (!is_array ($listDAO)) {
- $listDAO = array ($listDAO);
+ if (!is_array($listDAO)) {
+ $listDAO = array($listDAO);
}
foreach ($listDAO as $key => $dao) {
- if (!isset ($dao['name'])) {
+ if (!isset($dao['name'])) {
continue;
}
- if (isset ($dao['id'])) {
+ if (isset($dao['id'])) {
$key = $dao['id'];
}
if ($catID === null) {
@@ -384,13 +371,14 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
$myFeed->_lastUpdate(isset($dao['lastUpdate']) ? $dao['lastUpdate'] : 0);
$myFeed->_priority(isset($dao['priority']) ? $dao['priority'] : 10);
$myFeed->_pathEntries(isset($dao['pathEntries']) ? $dao['pathEntries'] : '');
- $myFeed->_httpAuth(isset($dao['httpAuth']) ? base64_decode ($dao['httpAuth']) : '');
+ $myFeed->_httpAuth(isset($dao['httpAuth']) ? base64_decode($dao['httpAuth']) : '');
$myFeed->_error(isset($dao['error']) ? $dao['error'] : 0);
$myFeed->_keepHistory(isset($dao['keep_history']) ? $dao['keep_history'] : -2);
+ $myFeed->_ttl(isset($dao['ttl']) ? $dao['ttl'] : -2);
$myFeed->_nbNotRead(isset($dao['cache_nbUnreads']) ? $dao['cache_nbUnreads'] : 0);
$myFeed->_nbEntries(isset($dao['cache_nbEntries']) ? $dao['cache_nbEntries'] : 0);
- if (isset ($dao['id'])) {
- $myFeed->_id ($dao['id']);
+ if (isset($dao['id'])) {
+ $myFeed->_id($dao['id']);
}
$list[$key] = $myFeed;
}
diff --git a/app/Models/FeedDAOSQLite.php b/app/Models/FeedDAOSQLite.php
new file mode 100644
index 000000000..0d1872389
--- /dev/null
+++ b/app/Models/FeedDAOSQLite.php
@@ -0,0 +1,19 @@
+<?php
+
+class FreshRSS_FeedDAOSQLite extends FreshRSS_FeedDAO {
+
+ public function updateCachedValues() { //For one single feed, call updateLastUpdate($id)
+ $sql = 'UPDATE `' . $this->prefix . 'feed` '
+ . 'SET cache_nbEntries=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=`' . $this->prefix . 'feed`.id),'
+ . 'cache_nbUnreads=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=`' . $this->prefix . 'feed`.id AND e2.is_read=0)';
+ $stm = $this->bd->prepare($sql);
+ if ($stm && $stm->execute()) {
+ return $stm->rowCount();
+ } else {
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+ Minz_Log::record('SQL error updateCachedValues: ' . $info[2], Minz_Log::ERROR);
+ return false;
+ }
+ }
+
+}
diff --git a/app/Models/StatsDAO.php b/app/Models/StatsDAO.php
index 60cec7847..66f5104b3 100644
--- a/app/Models/StatsDAO.php
+++ b/app/Models/StatsDAO.php
@@ -2,6 +2,8 @@
class FreshRSS_StatsDAO extends Minz_ModelPdo {
+ const ENTRY_COUNT_PERIOD = 30;
+
/**
* Calculates entry repartition for all feeds and for main stream.
* The repartition includes:
@@ -9,7 +11,7 @@ class FreshRSS_StatsDAO extends Minz_ModelPdo {
* - read entries
* - unread entries
* - favorite entries
- *
+ *
* @return type
*/
public function calculateEntryRepartition() {
@@ -50,50 +52,19 @@ SQL;
/**
* Calculates entry count per day on a 30 days period.
* Returns the result as a JSON string.
- *
+ *
* @return string
*/
public function calculateEntryCount() {
- $count = array();
+ $count = $this->initEntryCountArray();
+ $period = self::ENTRY_COUNT_PERIOD;
- // Generates a list of 30 last day to be sure we always have 30 days.
- // If we do not do that kind of thing, we'll end up with holes in the
- // days if the user do not have a lot of feeds.
- $sql = <<<SQL
-SELECT - (tens.val + units.val + 1) AS day
-FROM (
- SELECT 0 AS val
- UNION ALL SELECT 1
- UNION ALL SELECT 2
- UNION ALL SELECT 3
- UNION ALL SELECT 4
- UNION ALL SELECT 5
- UNION ALL SELECT 6
- UNION ALL SELECT 7
- UNION ALL SELECT 8
- UNION ALL SELECT 9
-) AS units
-CROSS JOIN (
- SELECT 0 AS val
- UNION ALL SELECT 10
- UNION ALL SELECT 20
-) AS tens
-ORDER BY day ASC
-SQL;
- $stm = $this->bd->prepare($sql);
- $stm->execute();
- $res = $stm->fetchAll(PDO::FETCH_ASSOC);
- foreach ($res as $value) {
- $count[$value['day']] = 0;
- }
-
- // Get stats per day for the last 30 days and applies the result on
- // the array created with the last query.
+ // Get stats per day for the last 30 days
$sql = <<<SQL
SELECT DATEDIFF(FROM_UNIXTIME(e.date), NOW()) AS day,
COUNT(1) AS count
FROM {$this->prefix}entry AS e
-WHERE FROM_UNIXTIME(e.date, '%Y%m%d') BETWEEN DATE_FORMAT(DATE_ADD(NOW(), INTERVAL -30 DAY), '%Y%m%d') AND DATE_FORMAT(DATE_ADD(NOW(), INTERVAL -1 DAY), '%Y%m%d')
+WHERE FROM_UNIXTIME(e.date, '%Y%m%d') BETWEEN DATE_FORMAT(DATE_ADD(NOW(), INTERVAL -{$period} DAY), '%Y%m%d') AND DATE_FORMAT(DATE_ADD(NOW(), INTERVAL -1 DAY), '%Y%m%d')
GROUP BY day
ORDER BY day ASC
SQL;
@@ -109,9 +80,20 @@ SQL;
}
/**
+ * Initialize an array for the entry count.
+ *
+ * @return array
+ */
+ protected function initEntryCountArray() {
+ return array_map(function () {
+ return 0;
+ }, array_flip(range(-self::ENTRY_COUNT_PERIOD, -1)));
+ }
+
+ /**
* Calculates feed count per category.
* Returns the result as a JSON string.
- *
+ *
* @return string
*/
public function calculateFeedByCategory() {
@@ -134,7 +116,7 @@ SQL;
/**
* Calculates entry count per category.
* Returns the result as a JSON string.
- *
+ *
* @return string
*/
public function calculateEntryByCategory() {
@@ -158,7 +140,7 @@ SQL;
/**
* Calculates the 10 top feeds based on their number of entries
- *
+ *
* @return array
*/
public function calculateTopFeed() {
@@ -172,7 +154,7 @@ FROM {$this->prefix}category AS c,
{$this->prefix}entry AS e
WHERE c.id = f.category
AND f.id = e.id_feed
-GROUP BY id
+GROUP BY f.id
ORDER BY count DESC
LIMIT 10
SQL;
@@ -181,7 +163,27 @@ SQL;
return $stm->fetchAll(PDO::FETCH_ASSOC);
}
- private function convertToSerie($data) {
+ /**
+ * Calculates the last publication date for each feed
+ *
+ * @return array
+ */
+ public function calculateFeedLastDate() {
+ $sql = <<<SQL
+SELECT MAX(f.name) AS name
+, MAX(date) AS last_date
+FROM {$this->prefix}feed AS f,
+{$this->prefix}entry AS e
+WHERE f.id = e.id_feed
+GROUP BY f.id
+ORDER BY name
+SQL;
+ $stm = $this->bd->prepare($sql);
+ $stm->execute();
+ return $stm->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ protected function convertToSerie($data) {
$serie = array();
foreach ($data as $key => $value) {
@@ -191,7 +193,7 @@ SQL;
return json_encode($serie);
}
- private function convertToPieSerie($data) {
+ protected function convertToPieSerie($data) {
$serie = array();
foreach ($data as $value) {
diff --git a/app/Models/StatsDAOSQLite.php b/app/Models/StatsDAOSQLite.php
new file mode 100644
index 000000000..dea590c92
--- /dev/null
+++ b/app/Models/StatsDAOSQLite.php
@@ -0,0 +1,37 @@
+<?php
+
+class FreshRSS_StatsDAOSQLite extends FreshRSS_StatsDAO {
+
+ /**
+ * Calculates entry count per day on a 30 days period.
+ * Returns the result as a JSON string.
+ *
+ * @return string
+ */
+ public function calculateEntryCount() {
+ $count = $this->initEntryCountArray();
+ $period = parent::ENTRY_COUNT_PERIOD;
+
+ // Get stats per day for the last 30 days
+ $sql = <<<SQL
+SELECT round(julianday(e.date, 'unixepoch') - julianday('now')) AS day,
+COUNT(1) AS count
+FROM {$this->prefix}entry AS e
+WHERE strftime('%Y%m%d', e.date, 'unixepoch')
+ BETWEEN strftime('%Y%m%d', 'now', '-{$period} days')
+ AND strftime('%Y%m%d', 'now', '-1 day')
+GROUP BY day
+ORDER BY day ASC
+SQL;
+ $stm = $this->bd->prepare($sql);
+ $stm->execute();
+ $res = $stm->fetchAll(PDO::FETCH_ASSOC);
+
+ foreach ($res as $value) {
+ $count[(int)$value['day']] = (int) $value['count'];
+ }
+
+ return $this->convertToSerie($count);
+ }
+
+}
diff --git a/app/Models/Themes.php b/app/Models/Themes.php
index 620149934..538eb6554 100644
--- a/app/Models/Themes.php
+++ b/app/Models/Themes.php
@@ -31,7 +31,10 @@ class FreshRSS_Themes extends Minz_Model {
if (file_exists($json_filename)) {
$content = file_get_contents($json_filename);
$res = json_decode($content, true);
- if ($res && isset($res['files']) && is_array($res['files'])) {
+ if ($res &&
+ !empty($res['name']) &&
+ isset($res['files']) &&
+ is_array($res['files'])) {
$res['id'] = $theme_id;
return $res;
}
@@ -70,6 +73,7 @@ class FreshRSS_Themes extends Minz_Model {
'add' => '✚',
'all' => '☰',
'bookmark' => '★',
+ 'bookmark-add' => '✚',
'category' => '☷',
'category-white' => '☷',
'close' => '❌',
@@ -77,6 +81,7 @@ class FreshRSS_Themes extends Minz_Model {
'down' => '▽',
'favorite' => '★',
'help' => 'ⓘ',
+ 'icon' => '⊚',
'key' => '⚿',
'link' => '↗',
'login' => '🔒',
@@ -109,3 +114,7 @@ class FreshRSS_Themes extends Minz_Model {
'<img class="icon" src="' . Minz_Url::display($url) . '" alt="' . $alts[$name] . '" />';
}
}
+
+function _i($icon, $url_only = false) {
+ return FreshRSS_Themes::icon($icon, $url_only);
+}
diff --git a/app/Models/UserDAO.php b/app/Models/UserDAO.php
index a25b57f89..dcf847a62 100644
--- a/app/Models/UserDAO.php
+++ b/app/Models/UserDAO.php
@@ -2,33 +2,44 @@
class FreshRSS_UserDAO extends Minz_ModelPdo {
public function createUser($username) {
- require_once(APP_PATH . '/sql.php');
$db = Minz_Configuration::dataBase();
+ require_once(APP_PATH . '/SQL/sql.' . $db['type'] . '.php');
+
+ if (defined('SQL_CREATE_TABLES')) {
+ $sql = sprintf(SQL_CREATE_TABLES, $db['prefix'] . $username . '_', Minz_Translate::t('default_category'));
+ $stm = $c->prepare($sql);
+ $ok = $stm && $stm->execute();
+ } else {
+ global $SQL_CREATE_TABLES;
+ if (is_array($SQL_CREATE_TABLES)) {
+ $ok = true;
+ foreach ($SQL_CREATE_TABLES as $instruction) {
+ $sql = sprintf($instruction, '', Minz_Translate::t('default_category'));
+ $stm = $c->prepare($sql);
+ $ok &= ($stm && $stm->execute());
+ }
+ }
+ }
- $sql = sprintf(SQL_CREATE_TABLES, $db['prefix'] . $username . '_');
- $stm = $this->bd->prepare($sql, array(PDO::ATTR_EMULATE_PREPARES => true));
- $values = array(
- 'catName' => Minz_Translate::t('default_category'),
- );
- if ($stm && $stm->execute($values)) {
+ if ($ok) {
return true;
} else {
- $info = $stm->errorInfo();
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
return false;
}
}
public function deleteUser($username) {
- require_once(APP_PATH . '/sql.php');
$db = Minz_Configuration::dataBase();
+ require_once(APP_PATH . '/SQL/sql.' . $db['type'] . '.php');
$sql = sprintf(SQL_DROP_TABLES, $db['prefix'] . $username . '_');
$stm = $this->bd->prepare($sql);
if ($stm && $stm->execute()) {
return true;
} else {
- $info = $stm->errorInfo();
+ $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
return false;
}
diff --git a/app/sql.php b/app/SQL/install.sql.mysql.php
index 5cd7c52ed..16cb3a3b8 100644
--- a/app/sql.php
+++ b/app/SQL/install.sql.mysql.php
@@ -21,6 +21,7 @@ CREATE TABLE IF NOT EXISTS `%1$sfeed` (
`httpAuth` varchar(511) DEFAULT NULL,
`error` boolean DEFAULT 0,
`keep_history` MEDIUMINT NOT NULL DEFAULT -2, -- v0.7
+ `ttl` INT NOT NULL DEFAULT -2, -- v0.7.3
`cache_nbEntries` int DEFAULT 0, -- v0.7
`cache_nbUnreads` int DEFAULT 0, -- v0.7
PRIMARY KEY (`id`),
@@ -52,7 +53,9 @@ CREATE TABLE IF NOT EXISTS `%1$sentry` (
) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci
ENGINE = INNODB;
-INSERT IGNORE INTO `%1$scategory` (id, name) VALUES(1, :catName);
+INSERT IGNORE INTO `%1$scategory` (id, name) VALUES(1, "%2$s");
');
define('SQL_DROP_TABLES', 'DROP TABLES %1$sentry, %1$sfeed, %1$scategory');
+
+define('SQL_SHOW_TABLES', 'SHOW tables;');
diff --git a/app/SQL/install.sql.sqlite.php b/app/SQL/install.sql.sqlite.php
new file mode 100644
index 000000000..b90a5ef5e
--- /dev/null
+++ b/app/SQL/install.sql.sqlite.php
@@ -0,0 +1,58 @@
+<?php
+$SQL_CREATE_TABLES = array(
+'CREATE TABLE IF NOT EXISTS `%1$scategory` (
+ `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ `name` varchar(255) NOT NULL,
+ UNIQUE (`name`)
+);',
+
+'CREATE TABLE IF NOT EXISTS `%1$sfeed` (
+ `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ `url` varchar(511) NOT NULL,
+ `%1$scategory` SMALLINT DEFAULT 0,
+ `name` varchar(255) NOT NULL,
+ `website` varchar(255),
+ `description` text,
+ `lastUpdate` int(11) DEFAULT 0,
+ `priority` tinyint(2) NOT NULL DEFAULT 10,
+ `pathEntries` varchar(511) DEFAULT NULL,
+ `httpAuth` varchar(511) DEFAULT NULL,
+ `error` boolean DEFAULT 0,
+ `keep_history` MEDIUMINT NOT NULL DEFAULT -2,
+ `ttl` INT NOT NULL DEFAULT -2,
+ `cache_nbEntries` int DEFAULT 0,
+ `cache_nbUnreads` int DEFAULT 0,
+ FOREIGN KEY (`%1$scategory`) REFERENCES `%1$scategory`(`id`) ON DELETE SET NULL ON UPDATE CASCADE,
+ UNIQUE (`url`)
+);',
+
+'CREATE INDEX IF NOT EXISTS feed_name_index ON `%1$sfeed`(`name`);',
+'CREATE INDEX IF NOT EXISTS feed_priority_index ON `%1$sfeed`(`priority`);',
+'CREATE INDEX IF NOT EXISTS feed_keep_history_index ON `%1$sfeed`(`keep_history`);',
+
+'CREATE TABLE IF NOT EXISTS `%1$sentry` (
+ `id` bigint NOT NULL,
+ `guid` varchar(760) NOT NULL,
+ `title` varchar(255) NOT NULL,
+ `author` varchar(255),
+ `content` text,
+ `link` varchar(1023) NOT NULL,
+ `date` int(11),
+ `is_read` boolean NOT NULL DEFAULT 0,
+ `is_favorite` boolean NOT NULL DEFAULT 0,
+ `id_feed` SMALLINT,
+ `tags` varchar(1023),
+ PRIMARY KEY (`id`),
+ FOREIGN KEY (`id_feed`) REFERENCES `%1$sfeed`(`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ UNIQUE (`id_feed`,`guid`)
+);',
+
+'CREATE INDEX IF NOT EXISTS entry_is_favorite_index ON `%1$sentry`(`is_favorite`);',
+'CREATE INDEX IF NOT EXISTS entry_is_read_index ON `%1$sentry`(`is_read`);',
+
+'INSERT OR IGNORE INTO `%1$scategory` (id, name) VALUES(1, "%2$s");',
+);
+
+define('SQL_DROP_TABLES', 'DROP TABLES %1$sentry, %1$sfeed, %1$scategory');
+
+define('SQL_SHOW_TABLES', 'SELECT name FROM sqlite_master WHERE type="table"');
diff --git a/app/i18n/en.php b/app/i18n/en.php
index a3c1dfeb7..8634f99b5 100644
--- a/app/i18n/en.php
+++ b/app/i18n/en.php
@@ -15,8 +15,45 @@ return array (
'feed' => 'Feed',
'feeds' => 'Feeds',
'shortcuts' => 'Shortcuts',
+ 'queries' => 'User queries',
+ 'query_search' => 'Search for "%s"',
+ 'query_order_asc' => 'Display oldest articles first',
+ 'query_order_desc' => 'Display newest articles first',
+ 'query_get_category' => 'Display "%s" category',
+ 'query_get_feed' => 'Display "%s" feed',
+ 'query_get_all' => 'Display all articles',
+ 'query_get_favorite' => 'Display favorite articles',
+ 'query_state_0' => 'Display all articles',
+ 'query_state_1' => 'Display read articles',
+ 'query_state_2' => 'Display unread articles',
+ 'query_state_3' => 'Display all articles',
+ 'query_state_4' => 'Display favorite articles',
+ 'query_state_5' => 'Display read favorite articles',
+ 'query_state_6' => 'Display unread favorite articles',
+ 'query_state_7' => 'Display favorite articles',
+ 'query_state_8' => 'Display not favorite articles',
+ 'query_state_9' => 'Display read not favorite articles',
+ 'query_state_10' => 'Display unread not favorite articles',
+ 'query_state_11' => 'Display not favorite articles',
+ 'query_state_12' => 'Display all articles',
+ 'query_state_13' => 'Display read articles',
+ 'query_state_14' => 'Display unread articles',
+ 'query_state_15' => 'Display all articles',
+ 'query_number' => 'Query n°%d',
+ 'add_query' => 'Add a query',
+ 'no_query' => 'You haven’t created any user query yet.',
+ 'query_filter' => 'Filter applied:',
+ 'no_query_filter' => 'No filter',
'about' => 'About',
'stats' => 'Statistics',
+ 'stats_idle' => 'Idle feeds',
+ 'stats_main' => 'Main statistics',
+
+ 'last_week' => 'Last week',
+ 'last_month' => 'Last month',
+ 'last_3_month' => 'Last three months',
+ 'last_6_month' => 'Last six months',
+ 'last_year' => 'Last year',
'your_rss_feeds' => 'Your RSS feeds',
'add_rss_feed' => 'Add a RSS feed',
@@ -160,6 +197,7 @@ return array (
'by_feed' => 'by feed',
'by_default' => 'By default',
'keep_history' => 'Minimum number of articles to keep',
+ 'ttl' => 'Do not automatically refresh more often than',
'categorize' => 'Store in a category',
'truncate' => 'Delete all articles',
'advanced' => 'Advanced',
@@ -226,8 +264,9 @@ return array (
'bottom_line' => 'Bottom line',
'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_selected' => 'when article is selected',
+ 'article_viewed' => 'when article is viewed',
'article_open_on_website' => 'when article is opened on its original website',
'scroll' => 'during page scrolls',
'upon_reception' => 'upon reception of the article',
diff --git a/app/i18n/fr.php b/app/i18n/fr.php
index 4acf5b397..e04078dba 100644
--- a/app/i18n/fr.php
+++ b/app/i18n/fr.php
@@ -15,8 +15,45 @@ return array (
'feed' => 'Flux',
'feeds' => 'Flux',
'shortcuts' => 'Raccourcis',
+ 'queries' => 'Filtres utilisateurs',
+ 'query_search' => 'Recherche de "%s"',
+ 'query_order_asc' => 'Afficher les articles les plus anciens en premier',
+ 'query_order_desc' => 'Afficher les articles les plus récents en premier',
+ 'query_get_category' => 'Afficher la catégorie "%s"',
+ 'query_get_feed' => 'Afficher le flux "%s"',
+ 'query_get_all' => 'Afficher tous les articles',
+ 'query_get_favorite' => 'Afficher les articles favoris',
+ 'query_state_0' => 'Afficher tous les articles',
+ 'query_state_1' => 'Afficher les articles lus',
+ 'query_state_2' => 'Afficher les articles non lus',
+ 'query_state_3' => 'Afficher tous les articles',
+ 'query_state_4' => 'Afficher les articles favoris',
+ 'query_state_5' => 'Afficher les articles lus et favoris',
+ 'query_state_6' => 'Afficher les articles non lus et favoris',
+ 'query_state_7' => 'Afficher les articles favoris',
+ 'query_state_8' => 'Afficher les articles non favoris',
+ 'query_state_9' => 'Afficher les articles lus et non favoris',
+ 'query_state_10' => 'Afficher les articles non lus et non favoris',
+ 'query_state_11' => 'Afficher les articles non favoris',
+ 'query_state_12' => 'Afficher tous les articles',
+ 'query_state_13' => 'Afficher les articles lus',
+ 'query_state_14' => 'Afficher les articles non lus',
+ 'query_state_15' => 'Afficher tous les articles',
+ 'query_number' => 'Filtre n°%d',
+ 'add_query' => 'Créer un filtre',
+ 'no_query' => 'Vous n’avez pas encore créé de filtre.',
+ 'query_filter' => 'Filtres appliqués :',
+ 'no_query_filter' => 'Aucun filtre appliqué',
'about' => 'À propos',
'stats' => 'Statistiques',
+ 'stats_idle' => 'Flux inactifs',
+ 'stats_main' => 'Statistiques principales',
+
+ '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',
'your_rss_feeds' => 'Vos flux RSS',
'add_rss_feed' => 'Ajouter un flux RSS',
@@ -33,7 +70,7 @@ return array (
'filter' => 'Filtrer',
'see_website' => 'Voir le site',
- 'administration' => 'Gestion',
+ 'administration' => 'Gérer',
'actualize' => 'Actualiser',
'mark_read' => 'Marquer comme lu',
@@ -66,49 +103,49 @@ return array (
'article_published_on' => 'Article publié initialement sur <a href="%s">%s</a>',
'article_published_on_author' => 'Article publié initialement sur <a href="%s">%s</a> par %s',
- 'access_denied' => 'Vous n’avez pas le droit d’accéder à cette page',
- 'page_not_found' => 'La page que vous cherchez n’existe pas',
- 'error_occurred' => 'Une erreur est survenue',
- 'error_occurred_update' => 'Rien n’a été modifié',
+ 'access_denied' => 'Vous n’avez pas le droit d’accéder à cette page !',
+ 'page_not_found' => 'La page que vous cherchez n’existe pas !',
+ 'error_occurred' => 'Une erreur est survenue !',
+ 'error_occurred_update' => 'Rien n’a été modifié !',
'default_category' => 'Sans catégorie',
- 'categories_updated' => 'Les catégories ont été mises à jour',
+ 'categories_updated' => 'Les catégories ont été mises à jour.',
'categories_management' => 'Gestion des catégories',
- 'feed_updated' => 'Le flux a été mis à jour',
+ 'feed_updated' => 'Le flux a été mis à jour.',
'rss_feed_management' => 'Gestion des flux RSS',
- 'configuration_updated' => 'La configuration a été mise à jour',
+ 'configuration_updated' => 'La configuration a été mise à jour.',
'sharing_management' => 'Gestion des options de partage',
- 'bad_opml_file' => 'Votre fichier OPML n’est pas valide',
- 'shortcuts_updated' => 'Les raccourcis ont été mis à jour',
+ 'bad_opml_file' => 'Votre fichier OPML n’est pas valide.',
+ 'shortcuts_updated' => 'Les raccourcis ont été mis à jour.',
'shortcuts_navigation' => 'Navigation',
'shortcuts_navigation_help' => 'Avec le modificateur "Shift", les raccourcis de navigation s’appliquent aux flux.<br/>Avec le modificateur "Alt", les raccourcis de navigation s’appliquent aux catégories.',
'shortcuts_article_action' => 'Actions associées à l’article courant',
'shortcuts_other_action' => 'Autres actions',
- 'feeds_marked_read' => 'Les flux ont été marqués comme lus',
- 'updated' => 'Modifications enregistrées',
+ 'feeds_marked_read' => 'Les flux ont été marqués comme lus.',
+ 'updated' => 'Modifications enregistrées.',
'already_subscribed' => 'Vous êtes déjà abonné à <em>%s</em>',
- 'feed_added' => 'Le flux <em>%s</em> a bien été ajouté',
- 'feed_not_added' => '<em>%s</em> n’a pas pu être ajouté',
+ 'feed_added' => 'Le flux <em>%s</em> a bien été ajouté.',
+ 'feed_not_added' => '<em>%s</em> n’a pas pu être ajouté.',
'internal_problem_feed' => 'Le flux ne peut pas être ajouté. <a href="%s">Consulter les logs de FreshRSS</a> pour plus de détails.',
- 'invalid_url' => 'L’url <em>%s</em> est invalide',
- 'feed_actualized' => '<em>%s</em> a été mis à jour',
- 'n_feeds_actualized' => '%d flux ont été mis à jour',
- 'feeds_actualized' => 'Les flux ont été mis à jour',
- 'no_feed_actualized' => 'Aucun flux n’a pu être mis à jour',
- 'n_entries_deleted' => '%d articles ont été supprimés',
- 'feeds_imported_with_errors' => 'Vos flux ont été importés mais des erreurs sont survenues',
- 'feeds_imported' => 'Vos flux ont été importés et vont maintenant être actualisés',
- 'category_emptied' => 'La catégorie a été vidée',
- 'feed_deleted' => 'Le flux a été supprimé',
+ 'invalid_url' => 'L’url <em>%s</em> est invalide.',
+ 'feed_actualized' => '<em>%s</em> a été mis à jour.',
+ 'n_feeds_actualized' => '%d flux ont été mis à jour.',
+ 'feeds_actualized' => 'Les flux ont été mis à jour.',
+ 'no_feed_actualized' => 'Aucun flux n’a pu être mis à jour.',
+ 'n_entries_deleted' => '%d articles ont été supprimés.',
+ 'feeds_imported_with_errors' => 'Vos flux ont été importés mais des erreurs sont survenues.',
+ 'feeds_imported' => 'Vos flux ont été importés et vont maintenant être actualisés.',
+ 'category_emptied' => 'La catégorie a été vidée.',
+ 'feed_deleted' => 'Le flux a été supprimé.',
'feed_validator' => 'Vérifier la valididé du flux',
- 'optimization_complete' => 'Optimisation terminée',
+ 'optimization_complete' => 'Optimisation terminée.',
'your_rss_feeds' => 'Vos flux RSS',
'your_favorites' => 'Vos favoris',
'public' => 'Public',
- 'invalid_login' => 'L’identifiant est invalide',
+ 'invalid_login' => 'L’identifiant est invalide !',
// VIEWS
'save' => 'Enregistrer',
@@ -120,12 +157,12 @@ return array (
'category_number' => 'Catégorie n°%d',
'ask_empty' => 'Vider ?',
'number_feeds' => '%d flux',
- 'can_not_be_deleted' => 'Ne peut pas être supprimée',
+ 'can_not_be_deleted' => 'Ne peut pas être supprimée.',
'add_category' => 'Ajouter une catégorie',
'new_category' => 'Nouvelle catégorie',
- 'javascript_for_shortcuts' => 'Le JavaScript doit être activé pour pouvoir profiter des raccourcis',
- 'javascript_should_be_activated'=> 'Le JavaScript doit être activé',
+ 'javascript_for_shortcuts' => 'Le JavaScript doit être activé pour pouvoir profiter des raccourcis.',
+ 'javascript_should_be_activated'=> 'Le JavaScript doit être activé.',
'shift_for_all_read' => '+ <code>shift</code> pour marquer tous les articles comme lus',
'see_on_website' => 'Voir sur le site d’origine',
'next_article' => 'Passer à l’article suivant',
@@ -136,7 +173,7 @@ return array (
'previous_page' => 'Passer à la page précédente',
'collapse_article' => 'Refermer',
'auto_share' => 'Partager',
- 'auto_share_help' => 'Si il n’y a qu’un mode de partage, celui ci est utilisé automatiquement. Sinon ils sont accessibles par leur numéro.',
+ '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',
'file_to_import' => 'Fichier à importer<br />(OPML, Json ou Zip)',
@@ -160,6 +197,7 @@ return array (
'by_feed' => 'par flux',
'by_default' => 'Par défaut',
'keep_history' => 'Nombre minimum d’articles à conserver',
+ 'ttl' => 'Ne pas automatiquement rafraîchir plus souvent que',
'categorize' => 'Ranger dans une catégorie',
'truncate' => 'Supprimer tous les articles',
'advanced' => 'Avancé',
@@ -175,7 +213,7 @@ return array (
'share_name' => 'Nom du partage à afficher',
'share_url' => 'URL du partage à utiliser',
'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',
+ '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&amp;a=feed">Vous pouvez ajouter des flux</a>.',
@@ -202,16 +240,16 @@ return array (
'username' => 'Nom d’utilisateur',
'password' => 'Mot de passe',
'create' => 'Créer',
- 'user_created' => 'L’utilisateur %s a été créé',
- 'user_deleted' => 'L’utilisateur %s a été supprimé',
+ 'user_created' => 'L’utilisateur %s a été créé.',
+ 'user_deleted' => 'L’utilisateur %s a été supprimé.',
'language' => 'Langue',
'month' => 'mois',
'archiving_configuration' => 'Archivage',
- 'delete_articles_every' => 'Supprimer les articles après',
+ 'delete_articles_every' => 'Supprimer les articles après',
'purge_now' => 'Purger maintenant',
- 'purge_completed' => 'Purge effectuée (%d articles supprimés)',
- 'archiving_configuration_help' => 'D’autres options sont disponibles dans la configuration individuelle des flux',
+ 'purge_completed' => 'Purge effectuée (%d articles supprimés).',
+ 'archiving_configuration_help' => 'D’autres options sont disponibles dans la configuration individuelle des flux.',
'reading_configuration' => 'Lecture',
'display_configuration' => 'Affichage',
'articles_per_page' => 'Nombre d’articles par page',
@@ -226,8 +264,9 @@ return array (
'bottom_line' => 'Ligne du bas',
'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”',
'auto_read_when' => 'Marquer un article comme lu…',
- 'article_selected' => 'lorsque l’article est sélectionné',
+ 'article_viewed' => 'lorsque l’article est affiché',
'article_open_on_website' => 'lorsque l’article est ouvert sur le site d’origine',
'scroll' => 'au défilement de la page',
'upon_reception' => 'dès la réception du nouvel article',
@@ -293,7 +332,7 @@ return array (
'version' => 'Version',
'logs' => 'Logs',
- 'logs_empty' => 'Les logs sont vides',
+ 'logs_empty' => 'Les logs sont vides.',
'clear_logs' => 'Effacer les logs',
'forbidden_access' => 'L’accès vous est interdit !',
diff --git a/app/i18n/install.en.php b/app/i18n/install.en.php
index 0311ee9a4..553a79921 100644
--- a/app/i18n/install.en.php
+++ b/app/i18n/install.en.php
@@ -61,7 +61,7 @@ return array (
'update_end' => 'Update process is completed, now you can go to the final step.',
- 'installation_is_ok' => 'The installation process was successful.<br />The final step will now attempt to delete the <kbd>./p/i/install.php</kbd> file and any database backup created during the update process.<br />You may choose to skip this step and delete <kbd>./p/i/install.php</kbd> manually.',
+ 'installation_is_ok' => 'The installation process was successful.<br />The final step will now attempt to delete any file and database backup created during the update process.<br />You may choose to skip this step by deleting <kbd>./data/do-install.txt</kbd> manually.',
'finish_installation' => 'Complete installation',
'install_not_deleted' => 'Something went wrong; you must delete the file <em>%s</em> manually.',
);
diff --git a/app/i18n/install.fr.php b/app/i18n/install.fr.php
index bb183642f..470d83e1a 100644
--- a/app/i18n/install.fr.php
+++ b/app/i18n/install.fr.php
@@ -60,7 +60,7 @@ return array (
'update_long' => 'Ce processus peut prendre longtemps, selon la taille de votre base de données. Vous aurez peut-être à attendre que cette page dépasse son temps maximum d’exécution (~5 minutes) puis à la recharger.',
'update_end' => 'La mise à jour est terminée, vous pouvez maintenant passer à l’étape finale.',
- 'installation_is_ok' => 'L’installation s’est bien passée.<br />La dernière étape va maintenant tenter de supprimer le fichier <kbd>./p/i/install.php</kbd>, ainsi que d’éventuelles copies de base de données créées durant le processus de mise à jour.<br />Vous pouvez choisir de sauter cette étape et de supprimer <kbd>./p/i/install.php</kbd> manuellement.',
+ 'installation_is_ok' => 'L’installation s’est bien passée.<br />La dernière étape va maintenant tenter de supprimer les fichiers ainsi que d’éventuelles copies de base de données créés durant le processus de mise à jour.<br />Vous pouvez choisir de sauter cette étape en supprimant <kbd>./data/do-install.txt</kbd> manuellement.',
'finish_installation' => 'Terminer l’installation',
'install_not_deleted' => 'Quelque chose s’est mal passé, vous devez supprimer le fichier <em>%s</em> à la main.',
);
diff --git a/app/install.php b/app/install.php
new file mode 100644
index 000000000..3767e3d91
--- /dev/null
+++ b/app/install.php
@@ -0,0 +1,1124 @@
+<?php
+if (function_exists('opcache_reset')) {
+ opcache_reset();
+}
+
+define('BCRYPT_COST', 9);
+
+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']);
+} else {
+ define ('STEP', 1);
+}
+
+define('SQL_CREATE_DB', 'CREATE DATABASE IF NOT EXISTS %1$s DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;');
+
+if (STEP === 3 && isset($_POST['type'])) {
+ $_SESSION['bd_type'] = $_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;
+ }
+}
+
+//<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;');
+
+define('SQL_GET_FEEDS', 'SELECT id, url, website FROM `%1$sfeed`;');
+//</updates>
+
+// gestion internationalisation
+$translates = array ();
+$actual = 'en';
+function initTranslate () {
+ global $translates;
+ global $actual;
+
+ $actual = isset($_SESSION['language']) ? $_SESSION['language'] : getBetterLanguage('en');
+
+ $file = APP_PATH . '/i18n/' . $actual . '.php';
+ if (file_exists($file)) {
+ $translates = array_merge($translates, include($file));
+ }
+
+ $file = APP_PATH . '/i18n/install.' . $actual . '.php';
+ if (file_exists($file)) {
+ $translates = array_merge($translates, include($file));
+ }
+}
+
+function getBetterLanguage ($fallback) {
+ $available = availableLanguages ();
+ $accept = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
+ $language = strtolower (substr ($accept, 0, 2));
+
+ if (isset ($available[$language])) {
+ return $language;
+ } else {
+ return $fallback;
+ }
+}
+function availableLanguages () {
+ return array (
+ 'en' => 'English',
+ 'fr' => 'Français'
+ );
+}
+function _t ($key) {
+ global $translates;
+ $translate = $key;
+ if (isset ($translates[$key])) {
+ $translate = $translates[$key];
+ }
+
+ $args = func_get_args ();
+ unset($args[0]);
+
+ return vsprintf ($translate, $args);
+}
+
+/*** SAUVEGARDES ***/
+function saveLanguage () {
+ if (!empty ($_POST)) {
+ if (!isset ($_POST['language'])) {
+ return false;
+ }
+
+ $_SESSION['language'] = $_POST['language'];
+
+ 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'])) {
+ if (!function_exists('password_hash')) {
+ include_once(LIB_PATH . '/password_compat.php');
+ }
+ $passwordHash = password_hash($_POST['passwordPlain'], PASSWORD_BCRYPT, array('cost' => BCRYPT_COST));
+ $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js
+ $_SESSION['passwordHash'] = $passwordHash;
+ }
+
+ $token = '';
+ if ($_SESSION['mail_login']) {
+ $token = sha1($_SESSION['salt'] . $_SESSION['mail_login']);
+ }
+
+ $config_array = array (
+ 'language' => $_SESSION['language'],
+ 'theme' => $_SESSION['theme'],
+ 'old_entries' => $_SESSION['old_entries'],
+ 'mail_login' => $_SESSION['mail_login'],
+ 'passwordHash' => $_SESSION['passwordHash'],
+ 'token' => $token,
+ );
+
+ $configPath = DATA_PATH . '/' . $_SESSION['default_user'] . '_user.php';
+ @unlink($configPath); //To avoid access-rights problems
+ file_put_contents($configPath, "<?php\n return " . var_export($config_array, true) . ';');
+
+ if ($_SESSION['mail_login'] != '') {
+ $personaFile = DATA_PATH . '/persona/' . $_SESSION['mail_login'] . '.txt';
+ @unlink($personaFile);
+ file_put_contents($personaFile, $_SESSION['default_user']);
+ }
+
+ header ('Location: index.php?step=3');
+ }
+}
+
+function saveStep3 () {
+ if (!empty ($_POST)) {
+ if ($_SESSION['bd_type'] === 'sqlite') {
+ $_SESSION['bd_base'] = $_SESSION['default_user'];
+ $_SESSION['bd_host'] = '';
+ $_SESSION['bd_user'] = '';
+ $_SESSION['bd_password'] = '';
+ $_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'])) {
+ $_SESSION['bd_error'] = 'Missing parameters!';
+ }
+ $_SESSION['bd_base'] = substr($_POST['base'], 0, 64);
+ $_SESSION['bd_host'] = $_POST['host'];
+ $_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'] . '_'));
+ }
+
+ $ini_array = array(
+ 'general' => array(
+ 'environment' => empty($_SESSION['environment']) ? 'production' : $_SESSION['environment'],
+ 'salt' => $_SESSION['salt'],
+ 'base_url' => '',
+ 'title' => $_SESSION['title'],
+ 'default_user' => $_SESSION['default_user'],
+ 'auth_type' => $_SESSION['auth_type'],
+ 'allow_anonymous' => isset($_SESSION['allow_anonymous']) ? $_SESSION['allow_anonymous'] : false,
+ 'allow_anonymous_refresh' => false,
+ 'unsafe_autologin_enabled' => false,
+ 'api_enabled' => false,
+ ),
+ 'db' => array(
+ 'type' => $_SESSION['bd_type'],
+ 'host' => $_SESSION['bd_host'],
+ 'user' => $_SESSION['bd_user'],
+ 'password' => $_SESSION['bd_password'],
+ 'base' => $_SESSION['bd_base'],
+ 'prefix' => $_SESSION['bd_prefix'],
+ ),
+ );
+
+ @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 ();
+
+ if ($res) {
+ $_SESSION['bd_error'] = '';
+ header ('Location: index.php?step=4');
+ } elseif (empty($_SESSION['bd_error'])) {
+ $_SESSION['bd_error'] = 'Unknown error!';
+ }
+ }
+ 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;
+ }
+ 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();
+
+ $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) {
+ 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;
+}
+
+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 ();
+ if (STEP > 0 && $s0['all'] != 'ok') {
+ header ('Location: index.php?step=0');
+ } elseif (STEP > 1 && $s1['all'] != 'ok') {
+ header ('Location: index.php?step=1');
+ } elseif (STEP > 2 && $s2['all'] != 'ok') {
+ header ('Location: index.php?step=2');
+ } elseif (STEP > 3 && $s3['all'] != 'ok') {
+ 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', 'auth_type');
+ 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];
+ }
+ }
+ }
+ }
+
+ 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 (
+ '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 = extension_loaded ('pdo_mysql');
+ $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 (
+ 'php' => $php ? 'ok' : 'ko',
+ 'minz' => $minz ? 'ok' : 'ko',
+ 'curl' => $curl ? 'ok' : 'ko',
+ 'pdo-mysql' => $pdo ? 'ok' : 'ko',
+ 'pcre' => $pcre ? 'ok' : 'ko',
+ 'ctype' => $ctype ? 'ok' : 'ko',
+ 'dom' => $dom ? 'ok' : 'ko',
+ 'data' => $data ? 'ok' : 'ko',
+ 'cache' => $cache ? 'ok' : 'ko',
+ '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'
+ );
+}
+
+function checkStep2 () {
+ $conf = !empty($_SESSION['salt']) &&
+ !empty($_SESSION['title']) &&
+ !empty($_SESSION['old_entries']) &&
+ isset($_SESSION['mail_login']) &&
+ !empty($_SESSION['default_user']);
+ $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 (
+ 'conf' => $conf ? 'ok' : 'ko',
+ 'data' => $data ? 'ok' : 'ko',
+ 'all' => $conf && $data ? 'ok' : 'ko'
+ );
+}
+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']);
+ $conn = empty($_SESSION['bd_error']);
+
+ return array (
+ 'bd' => $bd ? 'ok' : 'ko',
+ 'conn' => $conn ? 'ok' : 'ko',
+ 'conf' => $conf ? 'ok' : 'ko',
+ 'all' => $bd && $conn && $conf ? 'ok' : 'ko'
+ );
+}
+
+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);
+
+ 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
+ }
+ }
+
+ if (defined('SQL_CREATE_TABLES')) {
+ $sql = sprintf(SQL_CREATE_TABLES, $_SESSION['bd_prefix_user'], _t('default_category'));
+ $stm = $c->prepare($sql);
+ $ok = $stm->execute();
+ } else {
+ global $SQL_CREATE_TABLES;
+ if (is_array($SQL_CREATE_TABLES)) {
+ $ok = true;
+ foreach ($SQL_CREATE_TABLES as $instruction) {
+ $sql = sprintf($instruction, $_SESSION['bd_prefix_user'], _t('default_category'));
+ $stm = $c->prepare($sql);
+ $ok &= $stm->execute();
+ }
+ }
+ }
+ } catch (PDOException $e) {
+ $ok = false;
+ $_SESSION['bd_error'] = $e->getMessage();
+ }
+
+ if (!$ok) {
+ @unlink(DATA_PATH . '/config.php');
+ }
+
+ return $ok;
+}
+
+/*** AFFICHAGE ***/
+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 } ?>
+
+ <form action="index.php?step=0" method="post">
+ <legend><?php echo _t ('choose_language'); ?></legend>
+ <div class="form-group">
+ <label class="group-name" for="language"><?php echo _t ('language'); ?></label>
+ <div class="group-controls">
+ <select name="language" id="language">
+ <?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 } ?>
+ </select>
+ </div>
+ </div>
+
+ <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>
+ <?php if ($s0['all'] == 'ok') { ?>
+ <a class="btn btn-important next-step" href="?step=1"><?php echo _t ('next_step'); ?></a>
+ <?php } ?>
+ </div>
+ </div>
+ </form>
+<?php
+}
+
+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>
+
+ <?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>
+ <?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>
+ <?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>
+ <?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>
+ <?php } ?>
+
+ <?php if ($res['pdo-mysql'] == 'ok') { ?>
+ <p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('pdomysql_is_ok'); ?></p>
+ <?php } else { ?>
+ <p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('pdomysql_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>
+ <?php } else { ?>
+ <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>
+ <?php } else { ?>
+ <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>
+ <?php } else { ?>
+ <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>
+ <?php } else { ?>
+ <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>
+ <?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>
+ <?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>
+ <?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>
+ <?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>
+ <?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>
+ <?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>
+ <?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>
+ <?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>
+ <?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>
+ <?php } ?>
+
+ <?php if ($res['all'] == 'ok') { ?>
+ <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>
+ <?php } ?>
+<?php
+}
+
+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 } ?>
+
+ <form action="index.php?step=2" method="post">
+ <legend><?php echo _t ('general_configuration'); ?></legend>
+
+ <div class="form-group">
+ <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'); ?>" />
+ </div>
+ </div>
+
+ <div class="form-group">
+ <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'); ?>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <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(); ?>" />
+ </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>
+ </div>
+ </div>
+
+ <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" />
+ <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>
+ <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>
+ </div>
+ </div>
+
+ <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>
+ <?php if ($s2['all'] == 'ok') { ?>
+ <a class="btn btn-important next-step" href="?step=3"><?php echo _t ('next_step'); ?></a>
+ <?php } ?>
+ </div>
+ </div>
+ </form>
+<?php
+}
+
+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 } 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>
+ <?php } ?>
+
+ <form action="index.php?step=3" method="post">
+ <legend><?php echo _t ('bdd_configuration'); ?></legend>
+ <div class="form-group">
+ <label class="group-name" for="type"><?php echo _t ('bdd_type'); ?></label>
+ <div class="group-controls">
+ <select name="type" id="type" onchange="mySqlShowHide()">
+ <option value="mysql"
+ <?php echo (isset($_SESSION['bd_type']) && $_SESSION['bd_type'] === 'mysql') ? 'selected="selected"' : ''; ?>>
+ MySQL
+ </option>
+ <option value="sqlite"
+ <?php echo (isset($_SESSION['bd_type']) && $_SESSION['bd_type'] === 'sqlite') ? 'selected="selected"' : ''; ?>>
+ SQLite
+ </option>
+ </select>
+ </div>
+ </div>
+
+ <div id="mysql">
+ <div class="form-group">
+ <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'; ?>" />
+ </div>
+ </div>
+
+ <div class="form-group">
+ <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'] : ''; ?>" />
+ </div>
+ </div>
+
+ <div class="form-group">
+ <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'] : ''; ?>" />
+ </div>
+ </div>
+
+ <div class="form-group">
+ <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" />
+ </div>
+ </div>
+
+ <div class="form-group">
+ <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_'; ?>" />
+ </div>
+ </div>
+ </div>
+ <script>
+ function mySqlShowHide() {
+ document.getElementById('mysql').style.display = document.getElementById('type').value === 'mysql' ? 'block' : 'none';
+ }
+ mySqlShowHide();
+ </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>
+ <?php if ($s3['all'] == 'ok') { ?>
+ <a class="btn btn-important next-step" href="?step=4"><?php echo _t ('next_step'); ?></a>
+ <?php } ?>
+ </div>
+ </div>
+ </form>
+<?php
+}
+
+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>
+<?php
+}
+
+function printStep6 () {
+?>
+ <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 ();
+
+initTranslate ();
+
+switch (STEP) {
+case 0:
+default:
+ saveLanguage ();
+ break;
+case 1:
+ break;
+case 2:
+ saveStep2 ();
+ break;
+case 3:
+ saveStep3 ();
+ break;
+case 4:
+ if (!empty($_POST['updateDatabase'])) {
+ updateDatabase(true);
+ }
+ break;
+case 5:
+ postUpdate();
+ break;
+case 6:
+ deleteInstall ();
+ break;
+}
+?>
+<!DOCTYPE html>
+<html lang="fr">
+ <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" />
+ <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>
+ </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>
+ </ul>
+
+ <div class="post">
+ <?php
+ switch (STEP) {
+ case 0:
+ default:
+ printStep0 ();
+ break;
+ case 1:
+ printStep1 ();
+ break;
+ case 2:
+ printStep2 ();
+ break;
+ case 3:
+ printStep3 ();
+ break;
+ case 4:
+ printStep4 ();
+ break;
+ case 5:
+ printStep5 ();
+ break;
+ case 6:
+ printStep6 ();
+ break;
+ }
+ ?>
+ </div>
+</div>
+ </body>
+</html>
diff --git a/app/layout/aside_configure.phtml b/app/layout/aside_configure.phtml
index 43adeb3c6..e66f2f64c 100644
--- a/app/layout/aside_configure.phtml
+++ b/app/layout/aside_configure.phtml
@@ -15,6 +15,9 @@
<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>
+ <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>
<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>
diff --git a/app/layout/aside_stats.phtml b/app/layout/aside_stats.phtml
new file mode 100644
index 000000000..32a3f5dee
--- /dev/null
+++ b/app/layout/aside_stats.phtml
@@ -0,0 +1,9 @@
+<ul class="nav nav-list aside">
+ <li class="nav-header"><?php echo Minz_Translate::t ('stats'); ?></li>
+ <li class="item<?php echo Minz_Request::actionName () == 'index' ? ' active' : ''; ?>">
+ <a href="<?php echo _url ('stats', 'index'); ?>"><?php echo Minz_Translate::t ('stats_main'); ?></a>
+ </li>
+ <li class="item<?php echo Minz_Request::actionName () == 'idle' ? ' active' : ''; ?>">
+ <a href="<?php echo _url ('stats', 'idle'); ?>"><?php echo Minz_Translate::t ('stats_idle'); ?></a>
+ </li>
+</ul>
diff --git a/app/layout/header.phtml b/app/layout/header.phtml
index 08aa7715d..2e42bfdea 100644
--- a/app/layout/header.phtml
+++ b/app/layout/header.phtml
@@ -2,20 +2,20 @@
if (Minz_Configuration::canLogIn()) {
?><ul class="nav nav-head nav-login"><?php
switch (Minz_Configuration::authType()) {
- case 'form':
- if ($this->loginOk) {
- ?><li class="item"><?php echo FreshRSS_Themes::icon('logout'); ?> <a class="signout" href="<?php echo _url ('index', 'formLogout'); ?>"><?php echo Minz_Translate::t ('logout'); ?></a></li><?php
- } else {
- ?><li class="item"><?php echo FreshRSS_Themes::icon('login'); ?> <a class="signin" href="<?php echo _url ('index', 'formLogin'); ?>"><?php echo Minz_Translate::t ('login'); ?></a></li><?php
- }
- break;
- case 'persona':
- if ($this->loginOk) {
- ?><li class="item"><?php echo FreshRSS_Themes::icon('logout'); ?> <a class="signout" href="#"><?php echo Minz_Translate::t ('logout'); ?></a></li><?php
- } else {
- ?><li class="item"><?php echo FreshRSS_Themes::icon('login'); ?> <a class="signin" href="#"><?php echo Minz_Translate::t ('login'); ?></a></li><?php
- }
- break;
+ case 'form':
+ if ($this->loginOk) {
+ ?><li class="item"><?php echo _i('logout'); ?> <a class="signout" href="<?php echo _url('index', 'formLogout'); ?>"><?php echo _t('logout'); ?></a></li><?php
+ } else {
+ ?><li class="item"><?php echo _i('login'); ?> <a class="signin" href="<?php echo _url('index', 'formLogin'); ?>"><?php echo _t('login'); ?></a></li><?php
+ }
+ break;
+ case 'persona':
+ if ($this->loginOk) {
+ ?><li class="item"><?php echo _i('logout'); ?> <a class="signout" href="#"><?php echo _t('logout'); ?></a></li><?php
+ } else {
+ ?><li class="item"><?php echo _i('login'); ?> <a class="signin" href="#"><?php echo _t('login'); ?></a></li><?php
+ }
+ break;
}
?></ul><?php
}
@@ -24,36 +24,36 @@ if (Minz_Configuration::canLogIn()) {
<div class="header">
<div class="item title">
<h1>
- <a href="<?php echo _url ('index', 'index'); ?>">
- <img class="logo" src="<?php echo Minz_Url::display ('/themes/icons/icon.svg'); ?>" alt="⊚" />
- <?php echo Minz_Configuration::title (); ?>
+ <a href="<?php echo _url('index', 'index'); ?>">
+ <img class="logo" src="<?php echo _i('icon', true); ?>" alt="⊚" />
+ <?php echo Minz_Configuration::title(); ?>
</a>
</h1>
</div>
<div class="item search">
<?php if ($this->loginOk || Minz_Configuration::allowAnonymous()) { ?>
- <form action="<?php echo _url ('index', 'index'); ?>" method="get">
+ <form action="<?php echo _url('index', 'index'); ?>" method="get">
<div class="stick">
- <?php $search = Minz_Request::param ('search', ''); ?>
- <input type="search" name="search" id="search" class="extend" value="<?php echo $search; ?>" placeholder="<?php echo Minz_Translate::t ('search'); ?>" />
+ <?php $search = Minz_Request::param('search', ''); ?>
+ <input type="search" name="search" id="search" class="extend" value="<?php echo $search; ?>" placeholder="<?php echo _t('search'); ?>" />
- <?php $get = Minz_Request::param ('get', ''); ?>
- <?php if($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 if($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 if($state != '') { ?>
+ <?php $state = Minz_Request::param('state', ''); ?>
+ <?php if ($state != '') { ?>
<input type="hidden" name="state" value="<?php echo $state; ?>" />
<?php } ?>
- <button class="btn" type="submit"><?php echo FreshRSS_Themes::icon('search'); ?></button>
+ <button class="btn" type="submit"><?php echo _i('search'); ?></button>
</div>
</form>
<?php } ?>
@@ -63,31 +63,32 @@ if (Minz_Configuration::canLogIn()) {
<div class="item configure">
<div class="dropdown">
<div id="dropdown-configure" class="dropdown-target"></div>
- <a class="btn dropdown-toggle" href="#dropdown-configure"><?php echo FreshRSS_Themes::icon('configure'); ?></a>
+ <a class="btn dropdown-toggle" href="#dropdown-configure"><?php echo _i('configure'); ?></a>
<ul class="dropdown-menu">
<li class="dropdown-close"><a href="#close">❌</a></li>
- <li class="dropdown-header"><?php echo Minz_Translate::t ('configuration'); ?></li>
- <li class="item"><a href="<?php echo _url ('configure', 'display'); ?>"><?php echo Minz_Translate::t ('display_configuration'); ?></a></li>
- <li class="item"><a href="<?php echo _url ('configure', 'reading'); ?>"><?php echo Minz_Translate::t ('reading_configuration'); ?></a></li>
- <li class="item"><a href="<?php echo _url ('configure', 'archiving'); ?>"><?php echo Minz_Translate::t ('archiving_configuration'); ?></a></li>
- <li class="item"><a href="<?php echo _url ('configure', 'sharing'); ?>"><?php echo Minz_Translate::t ('sharing'); ?></a></li>
- <li class="item"><a href="<?php echo _url ('configure', 'shortcut'); ?>"><?php echo Minz_Translate::t ('shortcuts'); ?></a></li>
+ <li class="dropdown-header"><?php echo _t('configuration'); ?></li>
+ <li class="item"><a href="<?php echo _url('configure', 'display'); ?>"><?php echo _t('display_configuration'); ?></a></li>
+ <li class="item"><a href="<?php echo _url('configure', 'reading'); ?>"><?php echo _t('reading_configuration'); ?></a></li>
+ <li class="item"><a href="<?php echo _url('configure', 'archiving'); ?>"><?php echo _t('archiving_configuration'); ?></a></li>
+ <li class="item"><a href="<?php echo _url('configure', 'sharing'); ?>"><?php echo _t('sharing'); ?></a></li>
+ <li class="item"><a href="<?php echo _url('configure', 'shortcut'); ?>"><?php echo _t('shortcuts'); ?></a></li>
+ <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 Minz_Translate::t ('users'); ?></a></li>
+ <li class="item"><a href="<?php echo _url('configure', 'users'); ?>"><?php echo _t('users'); ?></a></li>
<li class="separator"></li>
- <li class="item"><a href="<?php echo _url ('index', 'stats'); ?>"><?php echo Minz_Translate::t ('stats'); ?></a></li>
- <li class="item"><a href="<?php echo _url ('index', 'logs'); ?>"><?php echo Minz_Translate::t ('logs'); ?></a></li>
- <li class="item"><a href="<?php echo _url ('index', 'about'); ?>"><?php echo Minz_Translate::t ('about'); ?></a></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>
+ <li class="item"><a href="<?php echo _url('index', 'about'); ?>"><?php echo _t('about'); ?></a></li>
<?php
if (Minz_Configuration::canLogIn()) {
?><li class="separator"></li><?php
switch (Minz_Configuration::authType()) {
- case 'form':
- ?><li class="item"><a class="signout" href="<?php echo _url ('index', 'formLogout'); ?>"><?php echo FreshRSS_Themes::icon('logout'), ' ', Minz_Translate::t ('logout'); ?></a></li><?php
- break;
- case 'persona':
- ?><li class="item"><a class="signout" href="#"><?php echo FreshRSS_Themes::icon('logout'), ' ', Minz_Translate::t ('logout'); ?></a></li><?php
- break;
+ case 'form':
+ ?><li class="item"><a class="signout" href="<?php echo _url('index', 'formLogout'); ?>"><?php echo _i('logout'), ' ', _t('logout'); ?></a></li><?php
+ break;
+ case 'persona':
+ ?><li class="item"><a class="signout" href="#"><?php echo _i('logout'), ' ', _t('logout'); ?></a></li><?php
+ break;
}
} ?>
</ul>
@@ -96,12 +97,12 @@ if (Minz_Configuration::canLogIn()) {
<?php } elseif (Minz_Configuration::canLogIn()) {
?><div class="item configure"><?php
switch (Minz_Configuration::authType()) {
- case 'form':
- echo FreshRSS_Themes::icon('login'); ?><a class="signin" href="<?php echo _url ('index', 'formLogin'); ?>"><?php echo Minz_Translate::t ('login'); ?></a></li><?php
- break;
- case 'persona':
- echo FreshRSS_Themes::icon('login'); ?><a class="signin" href="#"><?php echo Minz_Translate::t ('login'); ?></a></li><?php
- break;
+ case 'form':
+ echo _i('login'); ?><a class="signin" href="<?php echo _url('index', 'formLogin'); ?>"><?php echo _t('login'); ?></a></li><?php
+ break;
+ case 'persona':
+ echo _i('login'); ?><a class="signin" href="#"><?php echo _t('login'); ?></a></li><?php
+ break;
}
?></div><?php
} ?>
diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml
index b42f816b4..29ea9032c 100644
--- a/app/layout/nav_menu.phtml
+++ b/app/layout/nav_menu.phtml
@@ -7,18 +7,20 @@
<?php } ?>
<?php if ($this->loginOk) { ?>
- <?php $url_state = $this->url;
- if ($this->state & FreshRSS_Entry::STATE_READ) {
- $url_state['params']['state'] = $this->state & ~FreshRSS_Entry::STATE_READ;
- $checked = 'true';
- $class = 'active';
- } else {
- $url_state['params']['state'] = $this->state | FreshRSS_Entry::STATE_READ;
- $checked = 'false';
- $class = '';
- }
- ?>
<div class="stick">
+ <?php
+ $url_state = $this->url;
+
+ if ($this->state & FreshRSS_Entry::STATE_READ) {
+ $url_state['params']['state'] = $this->state & ~FreshRSS_Entry::STATE_READ;
+ $checked = 'true';
+ $class = 'active';
+ } else {
+ $url_state['params']['state'] = $this->state | FreshRSS_Entry::STATE_READ;
+ $checked = 'false';
+ $class = '';
+ }
+ ?>
<a id="toggle-read"
class="btn <?php echo $class; ?>"
aria-checked="<?php echo $checked; ?>"
@@ -26,6 +28,7 @@
title="<?php echo Minz_Translate::t ('show_read'); ?>">
<?php echo FreshRSS_Themes::icon('read'); ?>
</a>
+
<?php
if ($this->state & FreshRSS_Entry::STATE_NOT_READ) {
$url_state['params']['state'] = $this->state & ~FreshRSS_Entry::STATE_NOT_READ;
@@ -44,6 +47,7 @@
title="<?php echo Minz_Translate::t ('show_not_reads'); ?>">
<?php echo FreshRSS_Themes::icon('unread'); ?>
</a>
+
<?php
if ($this->state & FreshRSS_Entry::STATE_FAVORITE) {
$url_state['params']['state'] = $this->state & ~FreshRSS_Entry::STATE_FAVORITE;
@@ -62,6 +66,7 @@
title="<?php echo Minz_Translate::t ('show_favorite'); ?>">
<?php echo FreshRSS_Themes::icon('starred'); ?>
</a>
+
<?php
if ($this->state & FreshRSS_Entry::STATE_NOT_FAVORITE) {
$url_state['params']['state'] = $this->state & ~FreshRSS_Entry::STATE_NOT_FAVORITE;
@@ -80,6 +85,34 @@
title="<?php echo Minz_Translate::t ('show_not_favorite'); ?>">
<?php echo FreshRSS_Themes::icon('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>
+ <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>
+
+ <?php foreach ($this->conf->queries as $query) { ?>
+ <li class="item">
+ <a href="<?php echo $query['url']; ?>"><?php echo $query['name']; ?></a>
+ </li>
+ <?php } ?>
+
+ <?php if (count($this->conf->queries) > 0) { ?>
+ <li class="separator no-mobile"></li>
+ <?php } ?>
+
+ <?php
+ $url_query = $this->url;
+ $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>
+ </ul>
+ </div>
</div>
<?php
$get = false;
@@ -148,7 +181,7 @@
?>
<div class="stick" id="nav_menu_read_all">
- <a class="read_all btn" href="<?php echo $markReadUrl; ?>"><?php echo Minz_Translate::t ('mark_read'); ?></a>
+ <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>
<div class="dropdown">
<div id="dropdown-read" class="dropdown-target"></div>
diff --git a/app/views/configure/archiving.phtml b/app/views/configure/archiving.phtml
index e144d0f45..c9cc7fe02 100644
--- a/app/views/configure/archiving.phtml
+++ b/app/views/configure/archiving.phtml
@@ -24,6 +24,27 @@
?></select> (<?php echo Minz_Translate::t('by_default'); ?>)
</div>
</div>
+ <div class="form-group">
+ <label class="group-name" for="ttl_default"><?php echo Minz_Translate::t('ttl'); ?></label>
+ <div class="group-controls">
+ <select class="number" name="ttl_default" id="ttl_default" required="required"><?php
+ $found = false;
+ foreach (array(1200 => '20min', 1500 => '25min', 1800 => '30min', 2700 => '45min',
+ 3600 => '1h', 5400 => '1.5h', 7200 => '2h', 10800 => '3h', 14400 => '4h', 18800 => '5h', 21600 => '6h', 25200 => '7h', 28800 => '8h',
+ 36000 => '10h', 43200 => '12h', 64800 => '18h',
+ 86400 => '1d', 129600 => '1.5d', 172800 => '2d', 259200 => '3d', 345600 => '4d', 432000 => '5d', 518400 => '6d',
+ 604800 => '1wk', -1 => '∞') as $v => $t) {
+ echo '<option value="' . $v . ($this->conf->ttl_default == $v ? '" selected="selected' : '') . '">' . $t . '</option>';
+ if ($this->conf->ttl_default == $v) {
+ $found = true;
+ }
+ }
+ if (!$found) {
+ echo '<option value="' . intval($this->conf->ttl_default) . '" selected="selected">' . intval($this->conf->ttl_default) . 's</option>';
+ }
+ ?></select> (<?php echo Minz_Translate::t('by_default'); ?>)
+ </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 27b0990ff..a8dd9a8cb 100644
--- a/app/views/configure/feed.phtml
+++ b/app/views/configure/feed.phtml
@@ -103,6 +103,27 @@
?></select>
</div>
</div>
+ <div class="form-group">
+ <label class="group-name" for="ttl"><?php echo Minz_Translate::t('ttl'); ?></label>
+ <div class="group-controls">
+ <select class="number" name="ttl" id="ttl" required="required"><?php
+ $found = false;
+ foreach (array(-2 => Minz_Translate::t('by_default'), 900 => '15min', 1200 => '20min', 1500 => '25min', 1800 => '30min', 2700 => '45min',
+ 3600 => '1h', 5400 => '1.5h', 7200 => '2h', 10800 => '3h', 14400 => '4h', 18800 => '5h', 21600 => '6h', 25200 => '7h', 28800 => '8h',
+ 36000 => '10h', 43200 => '12h', 64800 => '18h',
+ 86400 => '1d', 129600 => '1.5d', 172800 => '2d', 259200 => '3d', 345600 => '4d', 432000 => '5d', 518400 => '6d',
+ 604800 => '1wk', 1209600 => '2wk', 1814400 => '3wk', 2419200 => '4wk', 2629744 => '1mo', -1 => '∞') as $v => $t) {
+ echo '<option value="' . $v . ($this->flux->ttl() === $v ? '" selected="selected' : '') . '">' . $t . '</option>';
+ if ($this->flux->ttl() == $v) {
+ $found = true;
+ }
+ }
+ if (!$found) {
+ echo '<option value="' . intval($this->flux->ttl()) . '" selected="selected">' . intval($this->flux->ttl()) . 's</option>';
+ }
+ ?></select>
+ </div>
+ </div>
<div class="form-group form-actions">
<div class="group-controls">
<button class="btn btn-important"><?php echo Minz_Translate::t ('save'); ?></button>
diff --git a/app/views/configure/queries.phtml b/app/views/configure/queries.phtml
new file mode 100644
index 000000000..2895f584c
--- /dev/null
+++ b/app/views/configure/queries.phtml
@@ -0,0 +1,90 @@
+<?php $this->partial('aside_configure'); ?>
+
+<div class="post">
+ <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('back_to_rss_feeds'); ?></a>
+
+ <form method="post" action="<?php echo _url('configure', 'queries'); ?>">
+ <legend><?php echo _t('queries'); ?></legend>
+
+ <?php foreach ($this->conf->queries as $key => $query) { ?>
+ <div class="form-group" id="query-group-<?php echo $key; ?>">
+ <label class="group-name" for="queries_<?php echo $key; ?>_name">
+ <?php echo _t('query_number', $key + 1); ?>
+ </label>
+
+ <div class="group-controls">
+ <input type="hidden" id="queries_<?php echo $key; ?>_search" name="queries[<?php echo $key; ?>][search]" value="<?php echo isset($query['search']) ? $query['search'] : ""; ?>"/>
+ <input type="hidden" id="queries_<?php echo $key; ?>_state" name="queries[<?php echo $key; ?>][state]" value="<?php echo isset($query['state']) ? $query['state'] : ""; ?>"/>
+ <input type="hidden" id="queries_<?php echo $key; ?>_order" name="queries[<?php echo $key; ?>][order]" value="<?php echo isset($query['order']) ? $query['order'] : ""; ?>"/>
+ <input type="hidden" id="queries_<?php echo $key; ?>_get" name="queries[<?php echo $key; ?>][get]" value="<?php echo isset($query['get']) ? $query['get'] : ""; ?>"/>
+
+ <div class="stick">
+ <input class="extend"
+ type="text"
+ id="queries_<?php echo $key; ?>_name"
+ name="queries[<?php echo $key; ?>][name]"
+ value="<?php echo $query['name']; ?>"
+ />
+
+ <a class="btn" href="<?php echo $query['url']; ?>">
+ <?php echo _i('link'); ?>
+ </a>
+
+ <a class="btn btn-attention remove" href="#" data-remove="query-group-<?php echo $key; ?>">
+ <?php echo _i('close'); ?>
+ </a>
+ </div>
+
+ <?php
+ $exist = (isset($query['search']) ? 1 : 0)
+ + (isset($query['state']) ? 1 : 0)
+ + (isset($query['order']) ? 1 : 0)
+ + (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;
+ ?>
+
+ <?php if ($exist === 0) { ?>
+ <div class="alert alert-warn">
+ <div class="alert-head"><?php echo _t('no_query_filter'); ?></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; ?>
+ <li class="item"><?php echo _t('query_search', $query['search']); ?></li>
+ <?php } ?>
+
+ <?php if (isset($query['state'])) { $exist = true; ?>
+ <li class="item"><?php echo _t('query_state_' . $query['state']); ?></li>
+ <?php } ?>
+
+ <?php if (isset($query['order'])) { $exist = true; ?>
+ <li class="item"><?php echo _t('query_order_' . strtolower($query['order'])); ?></li>
+ <?php } ?>
+
+ <?php if (isset($query['get'])) { $exist = true; ?>
+ <li class="item"><?php echo _t('query_get_' . $this->query_get[$key]['type'], $this->query_get[$key]['name']); ?></li>
+ <?php } ?>
+ </ul>
+ </div>
+ <?php } ?>
+ </div>
+ </div>
+ <?php } ?>
+
+ <?php if (count($this->conf->queries) > 0) { ?>
+ <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>
+ </div>
+ </div>
+ <?php } else { ?>
+ <p class="alert alert-warn"><span class="alert-head"><?php echo _t('damn'); ?></span> <?php echo _t('no_query'); ?></p>
+ <?php } ?>
+ </form>
+
+</div> \ No newline at end of file
diff --git a/app/views/configure/reading.phtml b/app/views/configure/reading.phtml
index 456ab9522..4d439e83d 100644
--- a/app/views/configure/reading.phtml
+++ b/app/views/configure/reading.phtml
@@ -83,11 +83,21 @@
</div>
<div class="form-group">
+ <div class="group-controls">
+ <label class="checkbox" for="reading_confirm">
+ <input type="checkbox" name="reading_confirm" id="reading_confirm" value="1"<?php echo $this->conf->reading_confirm ? ' checked="checked"' : ''; ?> />
+ <?php echo Minz_Translate::t ('reading_confirm'); ?>
+ <noscript> — <strong><?php echo Minz_Translate::t ('javascript_should_be_activated'); ?></strong></noscript>
+ </label>
+ </div>
+ </div>
+
+ <div class="form-group">
<label class="group-name"><?php echo Minz_Translate::t ('auto_read_when'); ?></label>
<div class="group-controls">
<label class="checkbox" for="check_open_article">
<input type="checkbox" name="mark_open_article" id="check_open_article" value="1"<?php echo $this->conf->mark_when['article'] ? ' checked="checked"' : ''; ?> />
- <?php echo Minz_Translate::t ('article_selected'); ?>
+ <?php echo Minz_Translate::t('article_viewed'); ?>
</label>
<label class="checkbox" for="check_open_site">
<input type="checkbox" name="mark_open_site" id="check_open_site" value="1"<?php echo $this->conf->mark_when['site'] ? ' checked="checked"' : ''; ?> />
diff --git a/app/views/configure/sharing.phtml b/app/views/configure/sharing.phtml
index a952bc3b4..02ce331da 100644
--- a/app/views/configure/sharing.phtml
+++ b/app/views/configure/sharing.phtml
@@ -4,35 +4,35 @@
<a href="<?php echo _url ('index', 'index'); ?>"><?php echo Minz_Translate::t ('back_to_rss_feeds'); ?></a>
<form method="post" action="<?php echo _url ('configure', 'sharing'); ?>"
- data-simple='<div class="form-group"><label class="group-name">##label##</label><div class="group-controls"><a href="#" class="share remove btn btn-attention"><?php echo FreshRSS_Themes::icon('close'); ?></a>
+ data-simple='<div class="form-group" id="group-share-##key##"><label class="group-name">##label##</label><div class="group-controls"><a href="#" class="remove btn btn-attention" data-remove="group-share-##key##"><?php echo FreshRSS_Themes::icon('close'); ?></a>
<input type="hidden" id="share_##key##_type" name="share[##key##][type]" value="##type##" /></div></div>'
- data-advanced='<div class="form-group"><label class="group-name">##label##</label><div class="group-controls">
+ data-advanced='<div class="form-group" id="group-share-##key##"><label class="group-name">##label##</label><div class="group-controls">
<input type="hidden" id="share_##key##_type" name="share[##key##][type]" value="##type##" />
<div class="stick">
<input type="text" id="share_##key##_name" name="share[##key##][name]" class="extend" value="" placeholder="<?php echo Minz_Translate::t ('share_name'); ?>" size="64" />
<input type="url" id="share_##key##_url" name="share[##key##][url]" class="extend" value="" placeholder="<?php echo Minz_Translate::t ('share_url'); ?>" size="64" />
- <a href="#" class="share remove btn btn-attention"><?php echo FreshRSS_Themes::icon('close'); ?></a></div>
+ <a href="#" class="remove btn btn-attention" data-remove="group-share-##key##"><?php echo FreshRSS_Themes::icon('close'); ?></a></div>
<a target="_blank" class="btn" title="<?php echo Minz_Translate::t('more_information'); ?>" href="##help##"><?php echo FreshRSS_Themes::icon('help'); ?></a>
</div></div>'>
<legend><?php echo Minz_Translate::t ('sharing'); ?></legend>
<?php foreach ($this->conf->sharing as $key => $sharing): ?>
<?php $share = $this->conf->shares[$sharing['type']]; ?>
- <div class="form-group">
+ <div class="form-group" id="group-share-<?php echo $key; ?>">
<label class="group-name">
<?php echo Minz_Translate::t ($sharing['type']); ?>
</label>
<div class="group-controls">
<input type='hidden' id='share_<?php echo $key;?>_type' name="share[<?php echo $key;?>][type]" value='<?php echo $sharing['type']?>' />
- <?php if ($share['form'] === 'advanced'){ ?>
+ <?php if ($share['form'] === 'advanced') { ?>
<div class="stick">
<input type="text" id="share_<?php echo $key;?>_name" name="share[<?php echo $key;?>][name]" class="extend" value="<?php echo $sharing['name']?>" placeholder="<?php echo Minz_Translate::t ('share_name'); ?>" size="64" />
<input type="url" id="share_<?php echo $key;?>_url" name="share[<?php echo $key;?>][url]" class="extend" value="<?php echo $sharing['url']?>" placeholder="<?php echo Minz_Translate::t ('share_url'); ?>" size="64" />
- <a href='#' class='share remove btn btn-attention'><?php echo FreshRSS_Themes::icon('close'); ?></a>
+ <a href='#' class='remove btn btn-attention' data-remove="group-share-<?php echo $key; ?>"><?php echo FreshRSS_Themes::icon('close'); ?></a>
</div>
<a target="_blank" class="btn" title="<?php echo Minz_Translate::t('more_information'); ?>" href="<?php echo $share['help']?>"><?php echo FreshRSS_Themes::icon('help'); ?></a>
<?php } else { ?>
- <a href='#' class='share remove btn btn-attention'><?php echo FreshRSS_Themes::icon('close'); ?></a>
+ <a href='#' class='remove btn btn-attention' data-remove="group-share-<?php echo $key; ?>"><?php echo FreshRSS_Themes::icon('close'); ?></a>
<?php } ?>
</div>
</div>
diff --git a/app/views/helpers/pagination.phtml b/app/views/helpers/pagination.phtml
index d4983a32e..f38913c06 100755
--- a/app/views/helpers/pagination.phtml
+++ b/app/views/helpers/pagination.phtml
@@ -12,7 +12,7 @@
<?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 } elseif ($markReadUrl) { ?>
- <a id="bigMarkAsRead" href="<?php echo $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'); ?>
diff --git a/app/views/javascript/actualize.phtml b/app/views/javascript/actualize.phtml
index 6a92e5642..d08dc47d1 100644
--- a/app/views/javascript/actualize.phtml
+++ b/app/views/javascript/actualize.phtml
@@ -10,7 +10,7 @@ var feeds = [<?php
function initProgressBar(init) {
if (init) {
$("body").after("\<div id=\"actualizeProgress\" class=\"notification good\">\
- <?php echo Minz_Translate::t ('refresh'); ?> <span class=\"progress\">0 / " + feed_count + "</span><br />\
+ <?php echo _t('refresh'); ?> <span class=\"progress\">0 / " + feed_count + "</span><br />\
<progress id=\"actualizeProgressBar\" value=\"0\" max=\"" + feed_count + "\"></progress>\
</div>");
} else {
@@ -24,7 +24,8 @@ function updateProgressBar(i) {
function updateFeeds() {
if (feed_count === 0) {
- openNotification("<?php echo Minz_Translate::t ('no_feed_to_refresh'); ?>", "good");
+ openNotification("<?php echo _t('no_feed_to_refresh'); ?>", "good");
+ ajax_loading = false;
return;
}
initProgressBar(true);
@@ -39,6 +40,7 @@ function updateFeed() {
if (feed == undefined) {
return;
}
+
$.ajax({
type: 'POST',
url: feed,
diff --git a/app/views/stats/idle.phtml b/app/views/stats/idle.phtml
new file mode 100644
index 000000000..356fea20f
--- /dev/null
+++ b/app/views/stats/idle.phtml
@@ -0,0 +1,19 @@
+<?php $this->partial('aside_stats'); ?>
+
+<div class="post content">
+ <a href="<?php echo _url ('index', 'index'); ?>"><?php echo _t ('back_to_rss_feeds'); ?></a>
+
+ <h1><?php echo _t ('stats_idle'); ?></h1>
+
+ <?php foreach ($this->idleFeeds as $period => $feeds){ ?>
+ <div class="stat">
+ <h2><?php echo _t ($period); ?></h2>
+
+ <ul>
+ <?php foreach ($feeds as $feed){ ?>
+ <li><?php echo $feed; ?></li>
+ <?php } ?>
+ </ul>
+ </div>
+ <?php } ?>
+</div>
diff --git a/app/views/stats/index.phtml b/app/views/stats/index.phtml
new file mode 100644
index 000000000..a48181fe4
--- /dev/null
+++ b/app/views/stats/index.phtml
@@ -0,0 +1,127 @@
+<?php $this->partial('aside_stats'); ?>
+
+<div class="post content">
+ <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">
+ <h2><?php echo _t ('stats_entry_repartition'); ?></h2>
+ <table>
+ <thead>
+ <tr>
+ <th> </th>
+ <th><?php echo _t ('main_stream'); ?></th>
+ <th><?php echo _t ('all_feeds'); ?></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <th><?php echo _t ('status_total'); ?></th>
+ <td class="numeric"><?php echo formatNumber($this->repartition['main_stream']['total']); ?></td>
+ <td class="numeric"><?php echo formatNumber($this->repartition['all_feeds']['total']); ?></td>
+ </tr>
+ <tr>
+ <th><?php echo _t ('status_read'); ?></th>
+ <td class="numeric"><?php echo formatNumber($this->repartition['main_stream']['read']); ?></td>
+ <td class="numeric"><?php echo formatNumber($this->repartition['all_feeds']['read']); ?></td>
+ </tr>
+ <tr>
+ <th><?php echo _t ('status_unread'); ?></th>
+ <td class="numeric"><?php echo formatNumber($this->repartition['main_stream']['unread']); ?></td>
+ <td class="numeric"><?php echo formatNumber($this->repartition['all_feeds']['unread']); ?></td>
+ </tr>
+ <tr>
+ <th><?php echo _t ('status_favorites'); ?></th>
+ <td class="numeric"><?php echo formatNumber($this->repartition['main_stream']['favorite']); ?></td>
+ <td class="numeric"><?php echo formatNumber($this->repartition['all_feeds']['favorite']); ?></td>
+ </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">
+ <h2><?php echo _t ('stats_top_feed'); ?></h2>
+ <table>
+ <thead>
+ <tr>
+ <th><?php echo _t ('feed'); ?></th>
+ <th><?php echo _t ('category'); ?></th>
+ <th><?php echo _t ('stats_entry_count'); ?></th>
+ </tr>
+ </thead>
+ <tbody>
+ <?php foreach ($this->topFeed as $feed): ?>
+ <tr>
+ <td><?php echo $feed['name']; ?></td>
+ <td><?php echo $feed['category']; ?></td>
+ <td class="numeric"><?php echo formatNumber($feed['count']); ?></td>
+ </tr>
+ <?php endforeach;?>
+ </tbody>
+ </table>
+ </div>
+</div>
+
+<script>
+"use strict";
+function initStats() {
+ if (!window.Flotr) {
+ if (window.console) {
+ console.log('FreshRSS waiting for Flotr…');
+ }
+ window.setTimeout(initStats, 50);
+ return;
+ }
+ // Entry per day
+ Flotr.draw(document.getElementById('statsEntryPerDay'),
+ [<?php echo $this->count ?>],
+ {
+ grid: {verticalLines: false},
+ bars: {horizontal: false, show: true},
+ xaxis: {noTicks: 6, showLabels: false, tickDecimals: 0},
+ yaxis: {min: 0},
+ mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
+ });
+ // Feed per category
+ Flotr.draw(document.getElementById('statsFeedPerCategory'),
+ <?php echo $this->feedByCategory ?>,
+ {
+ grid: {verticalLines: false, horizontalLines: false},
+ pie: {explode: 10, show: true, labelFormatter: function(){return '';}},
+ xaxis: {showLabels: false},
+ yaxis: {showLabels: false},
+ mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return obj.series.label + ' - '+ numberFormat(obj.y) + ' ('+ (obj.fraction * 100).toFixed(1) + '%)';}},
+ legend: {container: document.getElementById('statsFeedPerCategoryLegend'), noColumns: 3}
+ });
+ // Entry per category
+ Flotr.draw(document.getElementById('statsEntryPerCategory'),
+ <?php echo $this->entryByCategory ?>,
+ {
+ grid: {verticalLines: false, horizontalLines: false},
+ pie: {explode: 10, show: true, labelFormatter: function(){return '';}},
+ xaxis: {showLabels: false},
+ yaxis: {showLabels: false},
+ mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return obj.series.label + ' - '+ numberFormat(obj.y) + ' ('+ (obj.fraction * 100).toFixed(1) + '%)';}},
+ legend: {container: document.getElementById('statsEntryPerCategoryLegend'), noColumns: 3}
+ });
+}
+initStats();
+</script>
diff --git a/app/views/index/stats.phtml b/app/views/stats/main.phtml
index b5c18813d..fe372e221 100644
--- a/app/views/index/stats.phtml
+++ b/app/views/stats/main.phtml
@@ -1,9 +1,11 @@
+<?php $this->partial('aside_stats'); ?>
+
<div class="post content">
- <a href="<?php echo _url ('index', 'index'); ?>"><?php echo Minz_Translate::t ('back_to_rss_feeds'); ?></a>
-
- <h1><?php echo Minz_Translate::t ('stats'); ?></h1>
-
- <div class="stat">
+ <a href="<?php echo _url ('index', 'index'); ?>"><?php echo Minz_Translate::t ('back_to_rss_feeds'); ?></a>
+
+ <h1><?php echo Minz_Translate::t ('stats_main'); ?></h1>
+
+ <div class="stat">
<h2><?php echo Minz_Translate::t ('stats_entry_repartition'); ?></h2>
<table>
<thead>