summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGravatar Marien Fressinaud <dev@marienfressinaud.fr> 2014-09-08 19:26:35 +0200
committerGravatar Marien Fressinaud <dev@marienfressinaud.fr> 2014-09-08 19:26:35 +0200
commitef1b35fc4385c99c4d38e3f87e8126d0dbe21519 (patch)
treec2127f92281084c3cb28f635dea63a9a179eabbb /app
parent909d8747ba09f9c9a6ac895f1f4f0763bdb27a55 (diff)
parentc3fd8877c021b86180b3bea4d4260e6478f0558e (diff)
Merge branch 'dev' into 411-update-system
Conflicts: constants.php
Diffstat (limited to 'app')
-rwxr-xr-xapp/Controllers/configureController.php1
-rw-r--r--app/Controllers/importExportController.php252
-rwxr-xr-xapp/Controllers/indexController.php52
-rw-r--r--app/Controllers/statsController.php5
-rw-r--r--app/FreshRSS.php31
-rw-r--r--app/Models/Configuration.php7
-rw-r--r--app/Models/EntryDAO.php4
-rw-r--r--app/Models/Feed.php6
-rw-r--r--app/Models/StatsDAO.php62
-rw-r--r--app/i18n/de.php326
-rw-r--r--app/i18n/en.php9
-rw-r--r--app/i18n/fr.php9
-rw-r--r--app/layout/aside_flux.phtml8
-rw-r--r--app/layout/nav_menu.phtml16
-rw-r--r--app/views/configure/categorize.phtml3
-rw-r--r--app/views/configure/reading.phtml10
-rw-r--r--app/views/configure/shortcut.phtml15
-rw-r--r--app/views/helpers/javascript_vars.phtml7
-rw-r--r--app/views/helpers/view/normal_view.phtml7
-rw-r--r--app/views/importExport/index.phtml4
-rw-r--r--app/views/index/formLogin.phtml23
-rw-r--r--app/views/javascript/actualize.phtml21
-rw-r--r--app/views/stats/repartition.phtml87
23 files changed, 775 insertions, 190 deletions
diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php
index 0bf58880f..bb96bfae3 100755
--- a/app/Controllers/configureController.php
+++ b/app/Controllers/configureController.php
@@ -184,6 +184,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
$this->view->conf->_default_view((int)Minz_Request::param('default_view', FreshRSS_Entry::STATE_ALL));
$this->view->conf->_auto_load_more(Minz_Request::param('auto_load_more', false));
$this->view->conf->_display_posts(Minz_Request::param('display_posts', false));
+ $this->view->conf->_display_categories(Minz_Request::param('display_categories', false));
$this->view->conf->_hide_read_feeds(Minz_Request::param('hide_read_feeds', false));
$this->view->conf->_onread_jump_next(Minz_Request::param('onread_jump_next', false));
$this->view->conf->_lazyload(Minz_Request::param('lazyload', false));
diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php
index 2b3353d93..5adf3878a 100644
--- a/app/Controllers/importExportController.php
+++ b/app/Controllers/importExportController.php
@@ -5,7 +5,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
if (!$this->view->loginOk) {
Minz_Error::error(
403,
- array('error' => array(Minz_Translate::t('access_denied')))
+ array('error' => array(_t('access_denied')))
);
}
@@ -20,33 +20,51 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
$this->view->categories = $this->catDAO->listCategories();
$this->view->feeds = $this->feedDAO->listFeeds();
- Minz_View::prependTitle(Minz_Translate::t('import_export') . ' · ');
+ Minz_View::prependTitle(_t('import_export') . ' · ');
}
public function importAction() {
- if (Minz_Request::isPost() && $_FILES['file']['error'] == 0) {
- @set_time_limit(300);
+ if (!Minz_Request::isPost()) {
+ Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true);
+ }
- $file = $_FILES['file'];
- $type_file = $this->guessFileType($file['name']);
+ $file = $_FILES['file'];
+ $status_file = $file['error'];
- $list_files = array(
- 'opml' => array(),
- 'json_starred' => array(),
- 'json_feed' => array()
- );
+ if ($status_file !== 0) {
+ Minz_Log::error('File cannot be uploaded. Error code: ' . $status_file);
+ Minz_Request::bad(_t('file_cannot_be_uploaded'),
+ array('c' => 'importExport', 'a' => 'index'));
+ }
- // We try to list all files according to their type
- // A zip file is first opened and then its files are listed
- $list = array();
- if ($type_file === 'zip') {
- $zip = zip_open($file['tmp_name']);
+ @set_time_limit(300);
- while (($zipfile = zip_read($zip)) !== false) {
- $type_zipfile = $this->guessFileType(
- zip_entry_name($zipfile)
- );
+ $type_file = $this->guessFileType($file['name']);
+ $list_files = array(
+ 'opml' => array(),
+ 'json_starred' => array(),
+ 'json_feed' => array()
+ );
+
+ // We try to list all files according to their type
+ $list = array();
+ if ($type_file === 'zip' && extension_loaded('zip')) {
+ $zip = zip_open($file['tmp_name']);
+
+ if (!is_resource($zip)) {
+ // zip_open cannot open file: something is wrong
+ Minz_Log::error('Zip archive cannot be imported. Error code: ' . $zip);
+ Minz_Request::bad(_t('zip_error'),
+ array('c' => 'importExport', 'a' => 'index'));
+ }
+
+ while (($zipfile = zip_read($zip)) !== false) {
+ if (!is_resource($zipfile)) {
+ // zip_entry() can also return an error code!
+ Minz_Log::error('Zip file cannot be imported. Error code: ' . $zipfile);
+ } else {
+ $type_zipfile = $this->guessFileType(zip_entry_name($zipfile));
if ($type_file !== 'unknown') {
$list_files[$type_zipfile][] = zip_entry_read(
$zipfile,
@@ -54,59 +72,37 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
);
}
}
-
- zip_close($zip);
- } elseif ($type_file !== 'unknown') {
- $list_files[$type_file][] = file_get_contents(
- $file['tmp_name']
- );
- }
-
- // Import different files.
- // OPML first(so categories and feeds are imported)
- // Starred articles then so the "favourite" status is already set
- // And finally all other files.
- $error = false;
- foreach ($list_files['opml'] as $opml_file) {
- $error = $this->importOpml($opml_file);
- }
- foreach ($list_files['json_starred'] as $article_file) {
- $error = $this->importArticles($article_file, true);
- }
- foreach ($list_files['json_feed'] as $article_file) {
- $error = $this->importArticles($article_file);
}
- // And finally, we get import status and redirect to the home page
- $notif = null;
- if ($error === true) {
- $content_notif = Minz_Translate::t(
- 'feeds_imported_with_errors'
- );
- } else {
- $content_notif = Minz_Translate::t(
- 'feeds_imported'
- );
- }
-
- Minz_Session::_param('notification', array(
- 'type' => 'good',
- 'content' => $content_notif
- ));
- Minz_Session::_param('actualize_feeds', true);
+ zip_close($zip);
+ } elseif ($type_file === 'zip') {
+ // Zip extension is not loaded
+ Minz_Request::bad(_t('no_zip_extension'),
+ array('c' => 'importExport', 'a' => 'index'));
+ } elseif ($type_file !== 'unknown') {
+ $list_files[$type_file][] = file_get_contents($file['tmp_name']);
+ }
- Minz_Request::forward(array(
- 'c' => 'index',
- 'a' => 'index'
- ), true);
+ // Import file contents.
+ // OPML first(so categories and feeds are imported)
+ // Starred articles then so the "favourite" status is already set
+ // And finally all other files.
+ $error = false;
+ foreach ($list_files['opml'] as $opml_file) {
+ $error = $this->importOpml($opml_file);
+ }
+ foreach ($list_files['json_starred'] as $article_file) {
+ $error = $this->importArticles($article_file, true);
+ }
+ foreach ($list_files['json_feed'] as $article_file) {
+ $error = $this->importArticles($article_file);
}
- // What are you doing? you have to call this controller
- // with a POST request!
- Minz_Request::forward(array(
- 'c' => 'importExport',
- 'a' => 'index'
- ));
+ // And finally, we get import status and redirect to the home page
+ Minz_Session::_param('actualize_feeds', true);
+ $content_notif = $error === true ? _t('feeds_imported_with_errors') :
+ _t('feeds_imported');
+ Minz_Request::good($content_notif);
}
private function guessFileType($filename) {
@@ -120,7 +116,8 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
} elseif (substr_compare($filename, '.opml', -5) === 0 ||
substr_compare($filename, '.xml', -4) === 0) {
return 'opml';
- } elseif (strcmp($filename, 'starred.json') === 0) {
+ } elseif (substr_compare($filename, '.json', -5) === 0 &&
+ strpos($filename, 'starred') !== false) {
return 'json_starred';
} elseif (substr_compare($filename, '.json', -5) === 0 &&
strpos($filename, 'feed_') === 0) {
@@ -176,15 +173,15 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
}
// We get different useful information
- $url = html_chars_utf8($feed_elt['xmlUrl']);
- $name = html_chars_utf8($feed_elt['text']);
+ $url = Minz_Helper::htmlspecialchars_utf8($feed_elt['xmlUrl']);
+ $name = Minz_Helper::htmlspecialchars_utf8($feed_elt['text']);
$website = '';
if (isset($feed_elt['htmlUrl'])) {
- $website = html_chars_utf8($feed_elt['htmlUrl']);
+ $website = Minz_Helper::htmlspecialchars_utf8($feed_elt['htmlUrl']);
}
$description = '';
if (isset($feed_elt['description'])) {
- $description = html_chars_utf8($feed_elt['description']);
+ $description = Minz_Helper::htmlspecialchars_utf8($feed_elt['description']);
}
$error = false;
@@ -210,7 +207,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
private function addCategoryOpml($cat_elt, $parent_cat) {
// Create a new Category object
- $cat = new FreshRSS_Category(html_chars_utf8($cat_elt['text']));
+ $cat = new FreshRSS_Category(Minz_Helper::htmlspecialchars_utf8($cat_elt['text']));
$id = $this->catDAO->addCategoryObject($cat);
$error = ($id === false);
@@ -287,7 +284,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
$url = $origin[$key];
$name = $origin['title'];
$website = $origin['htmlUrl'];
- $error = false;
+
try {
// Create a Feed object and add it in DB
$feed = new FreshRSS_Feed($url);
@@ -311,61 +308,53 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
}
public function exportAction() {
- if (Minz_Request::isPost()) {
- $this->view->_useLayout(false);
+ if (!Minz_Request::isPost()) {
+ Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true);
+ }
- $export_opml = Minz_Request::param('export_opml', false);
- $export_starred = Minz_Request::param('export_starred', false);
- $export_feeds = Minz_Request::param('export_feeds', array ());
+ $this->view->_useLayout(false);
- $export_files = array ();
- if ($export_opml) {
- $export_files['feeds.opml'] = $this->generateOpml();
- }
+ $export_opml = Minz_Request::param('export_opml', false);
+ $export_starred = Minz_Request::param('export_starred', false);
+ $export_feeds = Minz_Request::param('export_feeds', array());
- if ($export_starred) {
- $export_files['starred.json'] = $this->generateArticles('starred');
- }
+ $export_files = array();
+ if ($export_opml) {
+ $export_files['feeds.opml'] = $this->generateOpml();
+ }
- foreach ($export_feeds as $feed_id) {
- $feed = $this->feedDAO->searchById($feed_id);
- if ($feed) {
- $filename = 'feed_' . $feed->category() . '_'
- . $feed->id() . '.json';
- $export_files[$filename] = $this->generateArticles(
- 'feed', $feed
- );
- }
- }
+ if ($export_starred) {
+ $export_files['starred.json'] = $this->generateArticles('starred');
+ }
- $nb_files = count($export_files);
- if ($nb_files > 1) {
- // If there are more than 1 file to export, we need an .zip
- try {
- $this->exportZip($export_files);
- } catch (Exception $e) {
- # Oops, there is no Zip extension!
- $notif = array(
- 'type' => 'bad',
- 'content' => _t('export_no_zip_extension')
- );
- Minz_Session::_param('notification', $notif);
- Minz_Request::forward(array('c' => 'importExport'), true);
- }
- } elseif ($nb_files === 1) {
- // Only one file? Guess its type and export it.
- $filename = key($export_files);
- $type = null;
- if (substr_compare($filename, '.opml', -5) === 0) {
- $type = "text/xml";
- } elseif (substr_compare($filename, '.json', -5) === 0) {
- $type = "text/json";
- }
+ foreach ($export_feeds as $feed_id) {
+ $feed = $this->feedDAO->searchById($feed_id);
+ if ($feed) {
+ $filename = 'feed_' . $feed->category() . '_'
+ . $feed->id() . '.json';
+ $export_files[$filename] = $this->generateArticles(
+ 'feed', $feed
+ );
+ }
+ }
- $this->exportFile($filename, $export_files[$filename], $type);
- } else {
- Minz_Request::forward(array('c' => 'importExport'), true);
+ $nb_files = count($export_files);
+ if ($nb_files > 1) {
+ // If there are more than 1 file to export, we need a zip archive.
+ try {
+ $this->exportZip($export_files);
+ } catch (Exception $e) {
+ # Oops, there is no Zip extension!
+ Minz_Request::bad(_t('export_no_zip_extension'),
+ array('c' => 'importExport', 'a' => 'index'));
}
+ } elseif ($nb_files === 1) {
+ // Only one file? Guess its type and export it.
+ $filename = key($export_files);
+ $type = $this->guessFileType($filename);
+ $this->exportFile('freshrss_' . $filename, $export_files[$filename], $type);
+ } else {
+ Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true);
}
}
@@ -384,7 +373,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
$this->view->categories = $this->catDAO->listCategories();
if ($type == 'starred') {
- $this->view->list_title = Minz_Translate::t('starred_list');
+ $this->view->list_title = _t('starred_list');
$this->view->type = 'starred';
$unread_fav = $this->entryDAO->countUnreadReadFavorites();
$this->view->entries = $this->entryDAO->listWhere(
@@ -392,9 +381,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
$unread_fav['all']
);
} elseif ($type == 'feed' && !is_null($feed)) {
- $this->view->list_title = Minz_Translate::t(
- 'feed_list', $feed->name()
- );
+ $this->view->list_title = _t('feed_list', $feed->name());
$this->view->type = 'feed/' . $feed->id();
$this->view->entries = $this->entryDAO->listWhere(
'f', $feed->id(), FreshRSS_Entry::STATE_ALL, 'ASC',
@@ -430,11 +417,18 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
}
private function exportFile($filename, $content, $type) {
- if (is_null($type)) {
+ if ($type === 'unknown') {
return;
}
- header('Content-Type: ' . $type . '; charset=utf-8');
+ $content_type = '';
+ if ($type === 'opml') {
+ $content_type = "text/opml";
+ } elseif ($type === 'json_feed' || $type === 'json_starred') {
+ $content_type = "text/json";
+ }
+
+ header('Content-Type: ' . $content_type . '; charset=utf-8');
header('Content-disposition: attachment; filename=' . $filename);
print($content);
}
diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php
index 3119073b8..b0b051119 100755
--- a/app/Controllers/indexController.php
+++ b/app/Controllers/indexController.php
@@ -76,14 +76,14 @@ class FreshRSS_index_Controller extends Minz_ActionController {
);
// On récupère les différents éléments de filtrage
- $this->view->state = $state = Minz_Request::param ('state', $this->view->conf->default_view);
+ $this->view->state = Minz_Request::param('state', $this->view->conf->default_view);
$state_param = Minz_Request::param ('state', null);
$filter = Minz_Request::param ('search', '');
$this->view->order = $order = Minz_Request::param ('order', $this->view->conf->sort_order);
$nb = Minz_Request::param ('nb', $this->view->conf->posts_per_page);
$first = Minz_Request::param ('next', '');
- if ($state === FreshRSS_Entry::STATE_NOT_READ) { //Any unread article in this category at all?
+ if ($this->view->state === FreshRSS_Entry::STATE_NOT_READ) { //Any unread article in this category at all?
switch ($getType) {
case 'a':
$hasUnread = $this->view->nb_not_read > 0;
@@ -104,7 +104,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
break;
}
if (!$hasUnread && ($state_param === null)) {
- $this->view->state = $state = FreshRSS_Entry::STATE_ALL;
+ $this->view->state = FreshRSS_Entry::STATE_ALL;
}
}
@@ -117,11 +117,11 @@ class FreshRSS_index_Controller extends Minz_ActionController {
$keepHistoryDefault = $this->view->conf->keep_history_default;
try {
- $entries = $entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min, true, $keepHistoryDefault);
+ $entries = $entryDAO->listWhere($getType, $getId, $this->view->state, $order, $nb + 1, $first, $filter, $date_min, true, $keepHistoryDefault);
// Si on a récupéré aucun article "non lus"
// on essaye de récupérer tous les articles
- if ($state === FreshRSS_Entry::STATE_NOT_READ && empty($entries) && ($state_param === null) && ($filter == '')) {
+ if ($this->view->state === FreshRSS_Entry::STATE_NOT_READ && empty($entries) && ($state_param === null) && ($filter == '')) {
Minz_Log::record('Conflicting information about nbNotRead!', Minz_Log::DEBUG);
$feedDAO = FreshRSS_Factory::createFeedDao();
try {
@@ -132,6 +132,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
$this->view->state = FreshRSS_Entry::STATE_ALL;
$entries = $entryDAO->listWhere($getType, $getId, $this->view->state, $order, $nb, $first, $filter, $date_min, true, $keepHistoryDefault);
}
+ Minz_Request::_param('state', $this->view->state);
if (count($entries) <= $nb) {
$this->view->nextId = '';
@@ -295,6 +296,41 @@ class FreshRSS_index_Controller extends Minz_ActionController {
Minz_Session::_param('passwordHash');
}
+ private static function makeLongTermCookie($username, $passwordHash) {
+ do {
+ $token = sha1(Minz_Configuration::salt() . $username . uniqid(mt_rand(), true));
+ $tokenFile = DATA_PATH . '/tokens/' . $token . '.txt';
+ } while (file_exists($tokenFile));
+ if (@file_put_contents($tokenFile, $username . "\t" . $passwordHash) === false) {
+ return false;
+ }
+ $expire = time() + 2629744; //1 month //TODO: Use a configuration instead
+ Minz_Session::setLongTermCookie('FreshRSS_login', $token, $expire);
+ Minz_Session::_param('token', $token);
+ return $token;
+ }
+
+ private static function deleteLongTermCookie() {
+ Minz_Session::deleteLongTermCookie('FreshRSS_login');
+ $token = Minz_Session::param('token', null);
+ if (ctype_alnum($token)) {
+ @unlink(DATA_PATH . '/tokens/' . $token . '.txt');
+ }
+ Minz_Session::_param('token');
+ if (rand(0, 10) === 1) {
+ self::purgeTokens();
+ }
+ }
+
+ private static function purgeTokens() {
+ $oldest = time() - 2629744; //1 month //TODO: Use a configuration instead
+ foreach (new DirectoryIterator(DATA_PATH . '/tokens/') as $fileInfo) {
+ if ($fileInfo->getExtension() === 'txt' && $fileInfo->getMTime() < $oldest) {
+ @unlink($fileInfo->getPathname());
+ }
+ }
+ }
+
public function formLoginAction () {
if (Minz_Request::isPost()) {
$ok = false;
@@ -312,6 +348,11 @@ class FreshRSS_index_Controller extends Minz_ActionController {
if ($ok) {
Minz_Session::_param('currentUser', $username);
Minz_Session::_param('passwordHash', $s);
+ if (Minz_Request::param('keep_logged_in', false)) {
+ self::makeLongTermCookie($username, $s);
+ } else {
+ self::deleteLongTermCookie();
+ }
} else {
Minz_Log::record('Password mismatch for user ' . $username . ', nonce=' . $nonce . ', c=' . $c, Minz_Log::WARNING);
}
@@ -371,6 +412,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
Minz_Session::_param('currentUser');
Minz_Session::_param('mail');
Minz_Session::_param('passwordHash');
+ self::deleteLongTermCookie();
Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true);
}
}
diff --git a/app/Controllers/statsController.php b/app/Controllers/statsController.php
index 06a20c2a6..000b41dd2 100644
--- a/app/Controllers/statsController.php
+++ b/app/Controllers/statsController.php
@@ -58,15 +58,20 @@ class FreshRSS_stats_Controller extends Minz_ActionController {
public function repartitionAction() {
$statsDAO = FreshRSS_Factory::createStatsDAO();
+ $categoryDAO = new FreshRSS_CategoryDAO();
$feedDAO = FreshRSS_Factory::createFeedDao();
Minz_View::appendScript(Minz_Url::display('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js')));
$id = Minz_Request::param ('id', null);
+ $this->view->categories = $categoryDAO->listCategories();
$this->view->feed = $feedDAO->searchById($id);
$this->view->days = $statsDAO->getDays();
$this->view->months = $statsDAO->getMonths();
$this->view->repartitionHour = $statsDAO->calculateEntryRepartitionPerFeedPerHour($id);
+ $this->view->averageHour = $statsDAO->calculateEntryAveragePerFeedPerHour($id);
$this->view->repartitionDayOfWeek = $statsDAO->calculateEntryRepartitionPerFeedPerDayOfWeek($id);
+ $this->view->averageDayOfWeek = $statsDAO->calculateEntryAveragePerFeedPerDayOfWeek($id);
$this->view->repartitionMonth = $statsDAO->calculateEntryRepartitionPerFeedPerMonth($id);
+ $this->view->averageMonth = $statsDAO->calculateEntryAveragePerFeedPerMonth($id);
}
public function firstAction() {
diff --git a/app/FreshRSS.php b/app/FreshRSS.php
index 7c333b090..cf6390f68 100644
--- a/app/FreshRSS.php
+++ b/app/FreshRSS.php
@@ -6,8 +6,7 @@ class FreshRSS extends Minz_FrontController {
}
$loginOk = $this->accessControl(Minz_Session::param('currentUser', ''));
$this->loadParamsView();
- if (Minz_Request::isPost() && (empty($_SERVER['HTTP_REFERER']) ||
- Minz_Request::getDomainName() !== parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST))) {
+ if (Minz_Request::isPost() && !Minz_Request::isRefererFromSameDomain()) {
$loginOk = false; //Basic protection against XSRF attacks
Minz_Error::error(
403,
@@ -20,13 +19,35 @@ class FreshRSS extends Minz_FrontController {
$this->loadNotifications();
}
+ private static function getCredentialsFromLongTermCookie() {
+ $token = Minz_Session::getLongTermCookie('FreshRSS_login');
+ if (!ctype_alnum($token)) {
+ return array();
+ }
+ $tokenFile = DATA_PATH . '/tokens/' . $token . '.txt';
+ $mtime = @filemtime($tokenFile);
+ if ($mtime + 2629744 < time()) { //1 month //TODO: Use a configuration instead
+ @unlink($tokenFile);
+ return array(); //Expired or token does not exist
+ }
+ $credentials = @file_get_contents($tokenFile);
+ return $credentials === false ? array() : explode("\t", $credentials, 2);
+ }
+
private function accessControl($currentUser) {
if ($currentUser == '') {
switch (Minz_Configuration::authType()) {
case 'form':
- $currentUser = Minz_Configuration::defaultUser();
- Minz_Session::_param('passwordHash');
- $loginOk = false;
+ $credentials = self::getCredentialsFromLongTermCookie();
+ if (isset($credentials[1])) {
+ $currentUser = trim($credentials[0]);
+ Minz_Session::_param('passwordHash', trim($credentials[1]));
+ }
+ $loginOk = $currentUser != '';
+ if (!$loginOk) {
+ $currentUser = Minz_Configuration::defaultUser();
+ Minz_Session::_param('passwordHash');
+ }
break;
case 'http_auth':
$currentUser = httpAuthUser();
diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php
index 2f47312c0..3a408faa5 100644
--- a/app/Models/Configuration.php
+++ b/app/Models/Configuration.php
@@ -17,6 +17,7 @@ class FreshRSS_Configuration {
'default_view' => FreshRSS_Entry::STATE_NOT_READ,
'auto_load_more' => true,
'display_posts' => false,
+ 'display_categories' => false,
'hide_read_feeds' => true,
'onread_jump_next' => true,
'lazyload' => true,
@@ -44,6 +45,8 @@ class FreshRSS_Configuration {
'load_more' => 'm',
'auto_share' => 's',
'focus_search' => 'a',
+ 'user_filter' => 'u',
+ 'help' => 'f1',
),
'topline_read' => true,
'topline_favorite' => true,
@@ -60,6 +63,7 @@ class FreshRSS_Configuration {
);
private $available_languages = array(
+ 'de' => 'Deutsch',
'en' => 'English',
'fr' => 'Français',
);
@@ -142,6 +146,9 @@ class FreshRSS_Configuration {
public function _display_posts ($value) {
$this->data['display_posts'] = ((bool)$value) && $value !== 'no';
}
+ public function _display_categories ($value) {
+ $this->data['display_categories'] = ((bool)$value) && $value !== 'no';
+ }
public function _hide_read_feeds($value) {
$this->data['hide_read_feeds'] = (bool)$value;
}
diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php
index a9ffa138b..75a8aeba4 100644
--- a/app/Models/EntryDAO.php
+++ b/app/Models/EntryDAO.php
@@ -65,7 +65,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
}
if (!isset($existingGuids[$entry->guid()]) &&
- ($feedHistory != 0 || $eDate >= $date_min)) {
+ ($feedHistory != 0 || $eDate >= $date_min || $entry->isFavorite())) {
$values = $entry->toArray();
$useDeclaredDate = empty($existingGuids);
@@ -230,7 +230,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
}
$this->bd->beginTransaction();
- $sql = 'UPDATE `' . $this->prefix . 'entry` '
+ $sql = 'UPDATE `' . $this->prefix . 'entry` '
. 'SET is_read=1 '
. 'WHERE id_feed=? AND is_read=0 AND id <= ?';
$values = array($id, $idMax);
diff --git a/app/Models/Feed.php b/app/Models/Feed.php
index fe1e52ea2..2a5ea45ac 100644
--- a/app/Models/Feed.php
+++ b/app/Models/Feed.php
@@ -28,6 +28,12 @@ class FreshRSS_Feed extends Minz_Model {
}
}
+ public static function example() {
+ $f = new FreshRSS_Feed('http://example.net/', false);
+ $f->faviconPrepare();
+ return $f;
+ }
+
public function id() {
return $this->id;
}
diff --git a/app/Models/StatsDAO.php b/app/Models/StatsDAO.php
index 89be76a26..bd4271ba8 100644
--- a/app/Models/StatsDAO.php
+++ b/app/Models/StatsDAO.php
@@ -152,6 +152,68 @@ SQL;
}
/**
+ * Calculates the average number of article per hour per feed
+ *
+ * @param integer $feed id
+ * @return integer
+ */
+ public function calculateEntryAveragePerFeedPerHour($feed = null) {
+ return $this->calculateEntryAveragePerFeedPerPeriod(1/24, $feed);
+ }
+
+ /**
+ * Calculates the average number of article per day of week per feed
+ *
+ * @param integer $feed id
+ * @return integer
+ */
+ public function calculateEntryAveragePerFeedPerDayOfWeek($feed = null) {
+ return $this->calculateEntryAveragePerFeedPerPeriod(7, $feed);
+ }
+
+ /**
+ * Calculates the average number of article per month per feed
+ *
+ * @param integer $feed id
+ * @return integer
+ */
+ public function calculateEntryAveragePerFeedPerMonth($feed = null) {
+ return $this->calculateEntryAveragePerFeedPerPeriod(30, $feed);
+ }
+
+ /**
+ * Calculates the average number of article per feed
+ *
+ * @param float $period number used to divide the number of day in the period
+ * @param integer $feed id
+ * @return integer
+ */
+ protected function calculateEntryAveragePerFeedPerPeriod($period, $feed = null) {
+ if ($feed) {
+ $restrict = "WHERE e.id_feed = {$feed}";
+ } else {
+ $restrict = '';
+ }
+ $sql = <<<SQL
+SELECT COUNT(1) AS count
+, MIN(date) AS date_min
+, MAX(date) AS date_max
+FROM {$this->prefix}entry AS e
+{$restrict}
+SQL;
+ $stm = $this->bd->prepare($sql);
+ $stm->execute();
+ $res = $stm->fetch(PDO::FETCH_NAMED);
+ $date_min = new \DateTime();
+ $date_min->setTimestamp($res['date_min']);
+ $date_max = new \DateTime();
+ $date_max->setTimestamp($res['date_max']);
+ $interval = $date_max->diff($date_min, true);
+
+ return round($res['count'] / ($interval->format('%a') / ($period)), 2);
+ }
+
+ /**
* Initialize an array for statistics depending on a range
*
* @param integer $min
diff --git a/app/i18n/de.php b/app/i18n/de.php
new file mode 100644
index 000000000..4301a8b6d
--- /dev/null
+++ b/app/i18n/de.php
@@ -0,0 +1,326 @@
+<?php
+
+return array (
+ // LAYOUT
+ 'login' => 'Login',
+ 'login_with_persona' => 'Login mit Persona',
+ 'logout' => 'Logout',
+ 'search' => 'Suche nach Worten oder #tags',
+ 'search_short' => 'Suche',
+
+ 'configuration' => 'Konfiguration',
+ 'users' => 'Nutzer',
+ 'categories' => 'Kategorien',
+ 'category' => 'Kategorie',
+ 'feed' => 'Feed',
+ 'feeds' => 'Feeds',
+ 'shortcuts' => 'Shortcuts',
+ 'about' => '&Uuml;ber',
+ 'stats' => 'Statistiken',
+
+ 'your_rss_feeds' => 'Ihre RSS Feeds',
+ 'add_rss_feed' => 'RSS-Feed hinzuf&uuml;gen',
+ 'no_rss_feed' => 'Kein RSS Feed',
+ 'import_export_opml' => 'Import / Export (OPML)',
+
+ 'subscription_management' => 'Abonnementsverwaltung',
+ 'main_stream' => 'Haupt-Nachrichtenflu&szlig;',
+ 'all_feeds' => 'Alle Feeds',
+ 'favorite_feeds' => 'Favoriten (%d)',
+ 'not_read' => '%d ungelesen',
+ 'not_reads' => '%d ungelesen',
+
+ 'filter' => 'Filter',
+ 'see_website' => 'Website ansehen',
+ 'administration' => 'Verwaltung',
+ 'actualize' => 'Aktualisierung',
+
+ 'mark_read' => 'Als gelesen markieren',
+ 'mark_favorite' => 'Als Favoriten markieren',
+ 'mark_all_read' => 'Alle als gelesen markieren',
+ 'mark_feed_read' => 'Feed als gelesen markieren',
+ 'mark_cat_read' => 'Kategorie als gelesen markieren',
+ 'before_one_day' => 'Vor einem Tag',
+ 'before_one_week' => 'Vor einer Woche',
+ 'display' => 'Anzeige',
+ 'normal_view' => 'Normale Anzeige',
+ 'reader_view' => 'Leseanzeige-Modus',
+ 'global_view' => 'Globale Anzeige',
+ 'rss_view' => 'RSS-Feed',
+ 'show_all_articles' => 'zeige alle Artikel',
+ 'show_not_reads' => 'zeige nicht gelesene',
+ 'show_read' => 'zeige nur gelesene',
+ 'show_favorite' => 'Favoriten anzeigen',
+ 'older_first' => '&Auml;lteste zuerst',
+ 'newer_first' => 'Neuere zuerst',
+
+ // Pagination
+ 'first' => 'Erste',
+ 'previous' => 'Vorherige',
+ 'next' => 'N&auml;chste',
+ 'last' => 'Letzte',
+
+ // CONTROLLERS
+ 'article_published_on' => 'Dieser Artikel erschien im Original bei <a href="%s">%s</a>',
+ 'article_published_on_author' => 'Dieser Artikel erschien im Original bei <a href="%s">%s</a> von %s',
+
+ 'access_denied' => 'Sie haben nicht die Berechtigung, diese Seite aufzurufen',
+ 'page_not_found' => 'Sie suchen nach einer Seite, die es nicht gibt',
+ 'error_occurred' => 'Es gab einen Fehler',
+ 'error_occurred_update' => 'Es wurde nichts ge&auml;ndert',
+
+ 'default_category' => 'Unkategorisiert',
+ 'categories_updated' => 'Kategorien wurden aktualisiert',
+ 'categories_management' => 'Kategorienverwaltung',
+ 'feed_updated' => 'Der Feed wurde aktualisiert',
+ 'rss_feed_management' => 'Verwaltung der RSS Feeds',
+ 'configuration_updated' => 'Die Konfiguration wurde aktualisiert',
+ 'sharing_management' => 'Verwaltung der Optionen f&uuml;r das Teilen',
+ 'bad_opml_file' => 'Ihre OPML-Datei ist ung&uuml;ltig',
+ 'shortcuts_updated' => 'Shortcuts wurden aktualisiert',
+ 'shortcuts_management' => 'Verwaltung der Shortcuts',
+ 'shortcuts_navigation' => 'Navigation',
+ 'shortcuts_navigation_help' => 'Mit der "Shift" Taste gelten die Navigations-Shortcuts f&uuml;r Feeds.<br/>Mit der "Alt" Taste gelten die Navigations-Shortcuts f&uuml;r Kategorien.',
+ 'shortcuts_article_action' => 'Artikelaktionen',
+ 'shortcuts_other_action' => 'Andere Aktionen',
+ 'feeds_marked_read' => 'Die Feeds wurden als gelesen markiert',
+ 'updated' => 'Die &Auml;nderungen wurden aktualisiert',
+
+ 'already_subscribed' => 'Sie haben bereits <em>%s</em> abonniert',
+ 'feed_added' => 'Der RSS Feed <em>%s</em> wurde hinzugef&uuml;gt',
+ 'feed_not_added' => '<em>%s</em> konnte nicht hinzugef&uuml;gt werden',
+ 'internal_problem_feed' => 'Der RSS Feed konnte nicht hinzugef&uuml;gt werden. &uuml;berpr&uuml;fen Sie die Protokolldateien von FressRSS f&uuml;r weitere Informationen.',
+ 'invalid_url' => 'URL <em>%s</em> ist ung&uuml;ltig',
+ 'feed_actualized' => '<em>%s</em> wurde aktualisiert',
+ 'n_feeds_actualized' => '%d Feeds wurden aktualisiert',
+ 'feeds_actualized' => 'RSS Feeds wurden aktualisiert',
+ 'no_feed_actualized' => 'Es wurden keine RSS Feeds aktualisiert',
+ 'n_entries_deleted' => '%d Artikel wurden gel&ouml;scht',
+ 'feeds_imported_with_errors' => 'Ihre Feeds wurden importiert, es gab aber einige Fehler',
+ 'feeds_imported' => 'Ihre Feeds wurden importiert und werden jetzt aktualisiert',
+ 'category_emptied' => 'Die Kategorie wurde geleert',
+ 'feed_deleted' => 'Der Feed wurde gel&ouml;scht',
+ 'feed_validator' => '&Üuml;berpr&uuml;fen Sie die G&uuml;ltigkeit des Feeds',
+
+ 'optimization_complete' => 'Die Optimierung ist beendet',
+
+ 'your_rss_feeds' => 'Ihre RSS Feeds',
+ 'your_favorites' => 'Ihre Favoriten',
+ 'public' => '&Ouml;ffentlich',
+ 'invalid_login' => 'Das Login ist ung&uuml;ltig',
+
+ // VIEWS
+ 'save' => 'Speichern',
+ 'delete' => 'L&ouml;schen',
+ 'cancel' => 'Abbrechen',
+
+ 'back_to_rss_feeds' => '← Zur&uuml;ck zu den RSS Feeds gehen',
+ 'feeds_moved_category_deleted' => 'Wenn Sie eine Kategorie l&ouml;schen, werden deren Feeds automatisch in die Kategorie <em>%s</em> eingef&uuml;gt.',
+ 'category_number' => 'Kategorie n°%d',
+ 'ask_empty' => 'Leeren?',
+ 'number_feeds' => '%d Feeds',
+ 'can_not_be_deleted' => 'Kann nicht gel&ouml;scht werden',
+ 'add_category' => 'F&uuml;ge eine Kategorie hinzu',
+ 'new_category' => 'Neue Kategorie',
+
+ 'javascript_for_shortcuts' => 'JavaScript muss erm&ouml;glicht werden, wenn Shortcuts verwendet werden sollen',
+ 'javascript_should_be_activated'=> 'JavaScript muss erm&ouml;glicht werden',
+ 'shift_for_all_read' => '+ <code>shift</code> um alle Artikel als gelesen zu markieren',
+ 'see_on_website' => 'Auf der Originalwebseite anschauen',
+ 'next_article' => 'Zum n&auml;chsten Artikel springen',
+ 'last_article' => 'Zum letzten Artikel springen',
+ 'previous_article' => 'Zum vorherigen Artikel springen',
+ 'first_article' => 'Zum ersten Artikel springen',
+ 'next_page' => 'Zur n&auml;chsten Seite springen',
+ 'previous_page' => 'Zur vorherigen Seite springen',
+ 'collapse_article' => 'Zusammenfalten',
+ 'auto_share' => 'Teilen',
+ 'auto_share_help' => 'Wenn es nur eine Option zum Teilen gibt, wird die verwendet. Ansonsten werden die Optionen &uuml;ber die Nummer ausgew&auml;hlt.',
+
+ 'file_to_import' => 'Datei zum importieren',
+ 'import' => 'Import',
+ 'export' => 'Export',
+ 'or' => 'oder',
+
+ 'informations' => 'Information',
+ 'damn' => 'Verdammt!',
+ 'feed_in_error' => 'Dieser Feed hat ein Problem verursacht. Bitte stellen Sie sicher, dass er immer lesbar ist und aktualisieren Sie ihn dann.',
+ 'feed_empty' => 'Dieser Feed ist leer. Bitte stellen Sie sicher, dass er noch gepflegt wird.',
+ 'feed_description' => 'Beschreibung',
+ 'website_url' => 'Webseiten-Adresse URL',
+ 'feed_url' => 'Feed URL',
+ 'articles' => 'Artikel',
+ 'number_articles' => 'Anzahl der Artikel',
+ 'by_feed' => 'per Feed',
+ 'by_default' => 'Als Vorgabe',
+ 'keep_history' => 'Kleinste Anzahl der Artikel, die behalten werden',
+ 'categorize' => 'In einer Kategorie speichern',
+ 'truncate' => 'Alle Artikel l&ouml;schen',
+ 'advanced' => 'Erweitert',
+ 'show_in_all_flux' => 'Im Hauptstrom anzeigen',
+ 'yes' => 'Ja',
+ 'no' => 'Nein',
+ 'css_path_on_website' => 'Pfad zur CSS-Datei des Artikels auf der Original Webseite',
+ 'retrieve_truncated_feeds' => 'Gek&uuml;rzte RSS Feeds abrufen (Achtung, ben&ouml;tigt mehr Zeit!)',
+ 'http_authentication' => 'HTTP Authentifizierung',
+ 'http_username' => 'HTTP Nutzername',
+ 'http_password' => 'HTTP Passwort',
+ 'blank_to_disable' => 'Zum Ausschalten frei lassen',
+ 'not_yet_implemented' => 'Noch nicht implementiert',
+ 'access_protected_feeds' => 'Die Verbindung erlaubt Zugriff zu HTTP-gesch&uuml;tzten RSS Feeds',
+ 'no_selected_feed' => 'Kein Feed ausgew&auml;hlt.',
+ 'think_to_add' => '<a href="./?c=configure&amp;a=feed">Sie k&ouml;nnen Feeds hinzuf&uuml;gen</a>.',
+
+ 'current_user' => 'Aktuelle Nutzung',
+ 'default_user' => 'Nutzername des Standardnutzers <small>(maximal 16 Zeichen - alphanumerisch)</small>',
+ 'password_form' => 'Passwort<br /><small>(f&uuml;r die Anmeldemethode per Webformular)</small>',
+ 'persona_connection_email' => 'Login E-Mail Adresse<br /><small>(f&uuml;r <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
+ 'allow_anonymous' => 'Anonymes lesen der Artikel des Standardnutzers (%s) wird erlaubt',
+ 'allow_anonymous_refresh' => 'Aktualisieren der Artikel wird anonymen Nutzern erlaubt',
+ 'auth_token' => 'Authentifizierungs-Token',
+ 'explain_token' => 'Erlaube den Zugriff auf die RSS-Ausgabe des Standardnutzers ohne Authentifizierung.<br /><kbd>%s?output=rss&token=%s</kbd>',
+ 'login_configuration' => 'Login',
+ 'is_admin' => 'ist Administrator',
+ 'auth_type' => 'Authentifizierungsmethode',
+ 'auth_none' => 'Keine (gef&auml;hrlich)',
+ 'auth_form' => 'Webformular (traditionell, JavaScript wird ben&ouml;tigt)',
+ 'http_auth' => 'HTTP (mit HTTPS f&uuml;r erfahrene Nutzer)',
+ 'auth_persona' => 'Mozilla Persona (modern, JavaScript wird ben&ouml;tigt)',
+ 'users_list' => 'Liste der Nutzer',
+ 'create_user' => 'Neuen Nutzer erstellen',
+ 'username' => 'Nutzername',
+ 'password' => 'Passwort',
+ 'create' => 'Erstellen',
+ 'user_created' => 'Nutzer %s wurde erstellt',
+ 'user_deleted' => 'Nutzer %s wurde gel&ouml;scht',
+
+ 'language' => 'Sprache',
+ 'month' => 'Monate',
+ 'archiving_configuration' => 'Archivieren',
+ 'delete_articles_every' => 'Entfernen von Artikeln nach',
+ 'purge_now' => 'Jetzt bereinigen',
+ 'purge_completed' => 'Die Bereinigung ist abgeschlossen (%d Artikel wurden gel&ouml;scht)',
+ 'archiving_configuration_help' => 'Es gibt weitere Optionen bei den Einstellungen der individuellen Nachrichtenstr&ouml;me',
+ 'reading_configuration' => 'Lesen',
+ 'articles_per_page' => 'Anzahl der Artikel pro Seite',
+ 'default_view' => 'Standard-Ansicht',
+ 'sort_order' => 'Sortierreihenfolge',
+ 'auto_load_more' => 'Die n&auml;chsten Artikel am Seitenende laden',
+ 'display_articles_unfolded' => 'Die Artikel als Standard zusammen gefaltet anzeigen',
+ 'after_onread' => 'Nach “als gelesen markieren”',
+ 'jump_next' => 'springe zum n&auml;chsten ungelesenen Geschwisterelement (Feed oder Kategorie)',
+ 'reading_icons' => 'Lese Symbol',
+ 'top_line' => 'Kopfzeile',
+ 'bottom_line' => 'Fusszeile',
+ 'img_with_lazyload' => 'Verwende die "tr&auml;ge laden" Methode zum laden von Bildern',
+ 'auto_read_when' => 'Artikel als gelesen markieren…',
+ 'article_selected' => 'wenn der Artikel ausgew&auml;hlt ist',
+ 'article_open_on_website' => 'wenn der Artikel auf der Originalwebseite ge&ouml;ffnet ist',
+ 'scroll' => 'w&auml;hrend des Seiten-Scrollens',
+ 'upon_reception' => 'beim Empfang des Artikels',
+ 'your_shaarli' => 'Ihr Shaarli',
+ 'your_wallabag' => 'Ihr wallabag',
+ 'your_diaspora_pod' => 'Ihr Diaspora* pod',
+ 'sharing' => 'Teilen',
+ 'share' => 'teile',
+ 'by_email' => 'Per E-Mail',
+ 'optimize_bdd' => 'Datenbank optimieren',
+ 'optimize_todo_sometimes' => 'Sollte gelegentlich gemacht werden, um die Gr&ouml;ße der Datenbank zu reduzieren',
+ 'theme' => 'Thema',
+ 'more_information' => 'Weitere Informationen',
+ 'activate_sharing' => 'Teilen aktivieren',
+ 'shaarli' => 'Shaarli',
+ 'wallabag' => 'wallabag',
+ 'diaspora' => 'Diaspora*',
+ 'twitter' => 'Twitter',
+ 'g+' => 'Google+',
+ 'facebook' => 'Facebook',
+ 'email' => 'E-Mail',
+ 'print' => 'Drucken',
+
+ 'article' => 'Artikel',
+ 'title' => 'Titel',
+ 'author' => 'Autor',
+ 'publication_date' => 'Datum der Ver&ouml;ffentlichung',
+ 'by' => 'von',
+
+ 'load_more' => 'Weitere Artikel laden',
+ 'nothing_to_load' => 'Es gibt keine weiteren Artikel',
+
+ 'rss_feeds_of' => 'RSS Feed von %s',
+
+ 'refresh' => 'Aktualisieren',
+ 'no_feed_to_refresh' => 'Es gibt keinen Feed zum aktualisieren',
+
+ 'today' => 'Heute',
+ 'yesterday' => 'Gestern',
+ 'before_yesterday' => 'vor Gestern',
+ 'new_article' => 'Es gibt neue Artikel. Bitte klicken Sie hier, um die Seite erneut zu laden.',
+ 'by_author' => 'Von <em>%s</em>',
+ 'related_tags' => 'Verwandte tags',
+ 'no_feed_to_display' => 'Es gibt keinen Artikel zum anzeigen.',
+
+ 'about_freshrss' => '&Uuml;ber FreshRSS',
+ 'project_website' => 'Projekt Webseite',
+ 'lead_developer' => 'Hauptentwickler',
+ 'website' => 'Webseite',
+ 'bugs_reports' => 'Fehlerberichte',
+ 'github_or_email' => '<a href="https://github.com/marienfressinaud/FreshRSS/issues">auf Github</a> oder <a href="mailto:dev@marienfressinaud.fr">per Mail</a>',
+ 'license' => 'Lizenz',
+ 'agpl3' => '<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL 3</a>',
+ 'freshrss_description' => 'FreshRSS ist ein RSS Feedsaggregator zum selbst hosten wie zum Beispiel <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> oder <a href="http://projet.idleman.fr/leed/">Leed</a>. Es ist leicht und einfach zu handhaben und gleichzeitig ein leistungsstark und konfigurierbares Werkzeug.',
+ 'credits' => 'Credits',
+ 'credits_content' => 'Einige Designelemente sind von <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> obwohl FreshRSS dieses Framework nicht nutzt. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Icons</a> sind vom <a href="https://www.gnome.org/">GNOME Projekt</a>. <em>Open Sans</em> Font police wurde von <a href="https://www.google.com/webfonts/specimen/Open+Sans">Steve Matteson erstellt</a>. Favicons wurden mit <a href="https://getfavicon.appspot.com/">getFavicon API gesammelt</a>. FreshRSS basiert auf <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, einem PHP Framework.',
+ 'version' => 'Version',
+
+ 'logs' => 'Protokolle',
+ 'logs_empty' => 'Die Protokolldatei ist leer',
+ 'clear_logs' => 'Protokolldateien leeren',
+
+ 'forbidden_access' => 'Der Zugriff ist verboten!',
+ 'login_required' => 'Das Login ist n&ouml;tig:',
+
+ 'confirm_action' => 'Sind Sie sicher, dass Sie diese Aktion durchf&uuml;hren wollen? Die Aktion kann nicht abgebrochen werden!',
+
+ // DATE
+ 'january' => 'januar',
+ 'february' => 'februar',
+ 'march' => 'm&auml;rz',
+ 'april' => 'april',
+ 'may' => 'mai',
+ 'june' => 'juni',
+ 'july' => 'juli',
+ 'august' => 'august',
+ 'september' => 'september',
+ 'october' => 'oktober',
+ 'november' => 'november',
+ 'december' => 'dezember',
+ // special format for date() function
+ 'Jan' => '\J\a\n\u\a\r',
+ 'Feb' => '\F\e\b\r\u\a\r',
+ 'Mar' => '\M\a\e\r\z',
+ 'Apr' => '\A\p\r\i\l',
+ 'May' => '\M\a\i',
+ 'Jun' => '\J\u\n\i',
+ 'Jul' => '\J\u\l\i',
+ 'Aug' => '\A\u\g\u\s\t',
+ 'Sep' => '\S\e\p\t\e\m\b\e\r',
+ 'Oct' => '\O\k\t\o\b\e\r',
+ 'Nov' => '\N\o\v\e\m\b\e\r',
+ 'Dec' => '\D\e\z\e\m\b\e\r',
+ // format for date() function, %s allows to indicate month in letter
+ 'format_date' => 'd\.\ %s Y',
+ 'format_date_hour' => 'd\.\ %s Y \u\m H\:i',
+
+ 'status_favorites' => 'Favoriten',
+ 'status_read' => 'Gelesen',
+ 'status_unread' => 'Ungelesen',
+ 'status_total' => 'Gesamt',
+
+ 'stats_entry_repartition' => 'Verteilung der Eintr&auml;ge',
+ 'stats_entry_per_day' => 'Eintr&auml;ge pro Tag (w&auml;hrend der letzten 30 Tage)',
+ 'stats_feed_per_category' => 'Feeds pro Kategorie',
+ 'stats_entry_per_category' => 'Eintr&auml;ge pro Kategorie',
+ 'stats_top_feed' => 'Top 10 Feeds',
+ 'stats_entry_count' => 'Z&auml;hler f&uuml;r Eintr&auml;ge',
+);
diff --git a/app/i18n/en.php b/app/i18n/en.php
index 6a0b4a139..95356af2c 100644
--- a/app/i18n/en.php
+++ b/app/i18n/en.php
@@ -3,6 +3,7 @@
return array (
// LAYOUT
'login' => 'Login',
+ 'keep_logged_in' => 'Keep me logged in <small>(1 month)</small>',
'login_with_persona' => 'Login with Persona',
'logout' => 'Logout',
'search' => 'Search words or #tags',
@@ -179,9 +180,16 @@ return array (
'auto_share' => 'Share',
'auto_share_help' => 'If there is only one sharing mode, it is used. Else modes are accessible by their number.',
'focus_search' => 'Access search box',
+ 'user_filter' => 'Access user filters',
+ 'user_filter_help' => 'If there is only one user filter, it is used. Else filters are accessible by their number.',
+ 'help' => 'Display documentation',
'file_to_import' => 'File to import<br />(OPML, Json or Zip)',
+ 'file_to_import_no_zip' => 'File to import<br />(OPML or Json)',
'import' => 'Import',
+ 'file_cannot_be_uploaded' => 'File cannot be uploaded!',
+ 'zip_error' => 'An error occured during Zip import.',
+ 'no_zip_extension' => 'Zip extension is not present on your server.',
'export' => 'Export',
'export_opml' => 'Export list of feeds (OPML)',
'export_starred' => 'Export your favourites',
@@ -263,6 +271,7 @@ return array (
'sort_order' => 'Sort order',
'auto_load_more' => 'Load next articles at the page bottom',
'display_articles_unfolded' => 'Show articles unfolded by default',
+ 'display_categories_unfolded' => 'Show categories folded by default',
'hide_read_feeds' => 'Hide categories &amp; feeds with no unread article (only in “unread articles” display mode)',
'after_onread' => 'After “mark all as read”,',
'jump_next' => 'jump to next unread sibling (feed or category)',
diff --git a/app/i18n/fr.php b/app/i18n/fr.php
index d0637b9f7..8437e872e 100644
--- a/app/i18n/fr.php
+++ b/app/i18n/fr.php
@@ -3,6 +3,7 @@
return array (
// LAYOUT
'login' => 'Connexion',
+ 'keep_logged_in' => 'Rester connecté <small>(1 mois)</small>',
'login_with_persona' => 'Connexion avec Persona',
'logout' => 'Déconnexion',
'search' => 'Rechercher des mots ou des #tags',
@@ -179,9 +180,16 @@ return array (
'auto_share' => 'Partager',
'auto_share_help' => 'S’il n’y a qu’un mode de partage, celui ci est utilisé automatiquement. Sinon ils sont accessibles par leur numéro.',
'focus_search' => 'Accéder à la recherche',
+ 'user_filter' => 'Accéder aux filtres utilisateur',
+ 'user_filter_help' => 'S’il n’y a qu’un filtre utilisateur, celui ci est utilisé automatiquement. Sinon ils sont accessibles par leur numéro.',
+ 'help' => 'Afficher la documentation',
'file_to_import' => 'Fichier à importer<br />(OPML, Json ou Zip)',
+ 'file_to_import_no_zip' => 'Fichier à importer<br />(OPML ou Json)',
'import' => 'Importer',
+ 'file_cannot_be_uploaded' => 'Le fichier ne peut pas être téléchargé!',
+ 'zip_error' => 'Une erreur est survenue durant l’import du fichier Zip.',
+ 'no_zip_extension' => 'L’extension Zip n’est pas présente sur votre serveur.',
'export' => 'Exporter',
'export_opml' => 'Exporter la liste des flux (OPML)',
'export_starred' => 'Exporter les favoris',
@@ -263,6 +271,7 @@ return array (
'sort_order' => 'Ordre de tri',
'auto_load_more' => 'Charger les articles suivants en bas de page',
'display_articles_unfolded' => 'Afficher les articles dépliés par défaut',
+ 'display_categories_unfolded' => 'Afficher les catégories pliées par défaut',
'hide_read_feeds' => 'Cacher les catégories &amp; flux sans article non-lu (uniquement en affichage “articles non lus”)',
'after_onread' => 'Après “marquer tout comme lu”,',
'jump_next' => 'sauter au prochain voisin non lu (flux ou catégorie)',
diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml
index aee8f8754..357aa1cd3 100644
--- a/app/layout/aside_flux.phtml
+++ b/app/layout/aside_flux.phtml
@@ -42,15 +42,19 @@
$feeds = $cat->feeds ();
if (!empty ($feeds)) {
$c_active = false;
+ $c_show = false;
if ($this->get_c == $cat->id ()) {
$c_active = true;
+ if (!$this->conf->display_categories || $this->get_f) {
+ $c_show = true;
+ }
}
?><li data-unread="<?php echo $cat->nbNotRead(); ?>"<?php if ($c_active) echo ' class="active"'; ?>><?php
?><div class="category stick<?php echo $c_active ? ' active' : ''; ?>"><?php
?><a data-unread="<?php echo formatNumber($cat->nbNotRead()); ?>" class="btn<?php echo $c_active ? ' active' : ''; ?>" href="<?php $arUrl['params']['get'] = 'c_' . $cat->id(); echo Minz_Url::display($arUrl); ?>"><?php echo $cat->name (); ?></a><?php
- ?><a class="btn dropdown-toggle" href="#"><?php echo FreshRSS_Themes::icon($c_active ? 'up' : 'down'); ?></a><?php
+ ?><a class="btn dropdown-toggle" href="#"><?php echo FreshRSS_Themes::icon($c_show ? 'up' : 'down'); ?></a><?php
?></div><?php
- ?><ul class="feeds<?php echo $c_active ? ' active' : ''; ?>"><?php
+ ?><ul class="feeds<?php echo $c_show ? ' active' : ''; ?>"><?php
foreach ($feeds as $feed) {
$feed_id = $feed->id ();
$nbEntries = $feed->nbEntries ();
diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml
index 73a921c5d..7cd15c1a3 100644
--- a/app/layout/nav_menu.phtml
+++ b/app/layout/nav_menu.phtml
@@ -96,7 +96,7 @@
<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">
+ <li class="item query">
<a href="<?php echo $query['url']; ?>"><?php echo $query['name']; ?></a>
</li>
<?php } ?>
@@ -164,11 +164,15 @@
break;
}
}
- if ($this->order === 'ASC') {
- $idMax = 0;
- } else {
- $p = isset($this->entries[0]) ? $this->entries[0] : null;
- $idMax = $p === null ? '0' : $p->id();
+
+ $p = isset($this->entries[0]) ? $this->entries[0] : null;
+ $idMax = $p === null ? (time() - 1) . '000000' : $p->id();
+
+ if ($this->order === 'ASC') { //In this case we do not know but we guess idMax
+ $idMax2 = (time() - 1) . '000000';
+ if (strcmp($idMax2, $idMax) > 0) {
+ $idMax = $idMax2;
+ }
}
$arUrl = array('c' => 'entry', 'a' => 'read', 'params' => array('get' => $get, 'nextGet' => $nextGet, 'idMax' => $idMax));
diff --git a/app/views/configure/categorize.phtml b/app/views/configure/categorize.phtml
index 9bae99b39..2f0e554ca 100644
--- a/app/views/configure/categorize.phtml
+++ b/app/views/configure/categorize.phtml
@@ -18,6 +18,9 @@
<input type="text" id="cat_<?php echo $cat->id (); ?>" name="categories[]" value="<?php echo $cat->name (); ?>" />
<?php if ($cat->nbFeed () > 0) { ?>
+ <a class="btn" href="<?php echo _url('index', 'index', 'get', 'c_' . $cat->id ()); ?>">
+ <?php echo _i('link'); ?>
+ </a>
<button type="submit" class="btn btn-attention confirm" formaction="<?php echo _url ('feed', 'delete', 'id', $cat->id (), 'type', 'category'); ?>"><?php echo Minz_Translate::t ('ask_empty'); ?></button>
<?php } ?>
</div>
diff --git a/app/views/configure/reading.phtml b/app/views/configure/reading.phtml
index e96bcea42..5a26501a4 100644
--- a/app/views/configure/reading.phtml
+++ b/app/views/configure/reading.phtml
@@ -63,6 +63,16 @@
<div class="form-group">
<div class="group-controls">
+ <label class="checkbox" for="display_categories">
+ <input type="checkbox" name="display_categories" id="display_categories" value="1"<?php echo $this->conf->display_categories ? ' checked="checked"' : ''; ?> />
+ <?php echo Minz_Translate::t ('display_categories_unfolded'); ?>
+ <noscript> — <strong><?php echo Minz_Translate::t ('javascript_should_be_activated'); ?></strong></noscript>
+ </label>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <div class="group-controls">
<label class="checkbox" for="sticky_post">
<input type="checkbox" name="sticky_post" id="sticky_post" value="1"<?php echo $this->conf->sticky_post ? ' checked="checked"' : ''; ?> />
<?php echo Minz_Translate::t ('sticky_post'); ?>
diff --git a/app/views/configure/shortcut.phtml b/app/views/configure/shortcut.phtml
index bfb13f003..a4029b676 100644
--- a/app/views/configure/shortcut.phtml
+++ b/app/views/configure/shortcut.phtml
@@ -103,6 +103,21 @@
</div>
</div>
+ <div class="form-group">
+ <label class="group-name" for="user_filter_shortcut"><?php echo Minz_Translate::t ('user_filter'); ?></label>
+ <div class="group-controls">
+ <input type="text" id="user_filter_shortcut" name="shortcuts[user_filter]" list="keys" value="<?php echo $s['user_filter']; ?>" />
+ <?php echo Minz_Translate::t ('user_filter_help'); ?>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label class="group-name" for="help_shortcut"><?php echo Minz_Translate::t ('help'); ?></label>
+ <div class="group-controls">
+ <input type="text" id="help_shortcut" name="shortcuts[help]" list="keys" value="<?php echo $s['help']; ?>" />
+ </div>
+ </div>
+
<div class="form-group form-actions">
<div class="group-controls">
<button type="submit" class="btn btn-important"><?php echo Minz_Translate::t ('save'); ?></button>
diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml
index 7144c519a..2144f1576 100644
--- a/app/views/helpers/javascript_vars.phtml
+++ b/app/views/helpers/javascript_vars.phtml
@@ -4,7 +4,8 @@ echo '"use strict";', "\n";
$mark = $this->conf->mark_when;
echo 'var ',
- 'hide_posts=', ($this->conf->display_posts || Minz_Request::param('output') === 'reader') ? 'false' : 'true',
+ 'help_url="', FRESHRSS_WIKI, '"',
+ ',hide_posts=', ($this->conf->display_posts || Minz_Request::param('output') === 'reader') ? 'false' : 'true',
',display_order="', Minz_Request::param('order', $this->conf->sort_order), '"',
',auto_mark_article=', $mark['article'] ? 'true' : 'false',
',auto_mark_site=', $mark['site'] ? 'true' : 'false',
@@ -25,7 +26,9 @@ echo ',shortcuts={',
'collapse_entry:"', $s['collapse_entry'], '",',
'load_more:"', $s['load_more'], '",',
'auto_share:"', $s['auto_share'], '",',
- 'focus_search:"', $s['focus_search'], '"',
+ 'focus_search:"', $s['focus_search'], '",',
+ 'user_filter:"', $s['user_filter'], '",',
+ 'help:"', $s['help'], '"',
"},\n";
if (Minz_Request::param ('output') === 'global') {
diff --git a/app/views/helpers/view/normal_view.phtml b/app/views/helpers/view/normal_view.phtml
index 55ef6bdf6..87bf2e22a 100644
--- a/app/views/helpers/view/normal_view.phtml
+++ b/app/views/helpers/view/normal_view.phtml
@@ -81,7 +81,12 @@ if (!empty($this->entries)) {
}
}
$feed = FreshRSS_CategoryDAO::findFeed($this->cat_aside, $item->feed ()); //We most likely already have the feed object in cache
- if (empty($feed)) $feed = $item->feed (true);
+ if ($feed == null) {
+ $feed = $item->feed(true);
+ if ($feed == null) {
+ $feed = FreshRSS_Feed::example();
+ }
+ }
?><li class="item website"><a href="<?php echo _url ('index', 'index', 'get', 'f_' . $feed->id ()); ?>"><img class="favicon" src="<?php echo $feed->favicon (); ?>" alt="✇" /> <span><?php echo $feed->name(); ?></span></a></li>
<li class="item title"><a target="_blank" href="<?php echo $item->link (); ?>"><?php echo $item->title (); ?></a></li>
<?php if ($topline_date) { ?><li class="item date"><?php echo $item->date (); ?> </li><?php } ?>
diff --git a/app/views/importExport/index.phtml b/app/views/importExport/index.phtml
index e1458e916..35371faca 100644
--- a/app/views/importExport/index.phtml
+++ b/app/views/importExport/index.phtml
@@ -6,7 +6,9 @@
<form method="post" action="<?php echo _url('importExport', 'import'); ?>" enctype="multipart/form-data">
<legend><?php echo _t('import'); ?></legend>
<div class="form-group">
- <label class="group-name" for="file"><?php echo _t('file_to_import'); ?></label>
+ <label class="group-name" for="file">
+ <?php echo extension_loaded('zip') ? _t('file_to_import') : _t('file_to_import_no_zip'); ?>
+ </label>
<div class="group-controls">
<input type="file" name="file" id="file" />
</div>
diff --git a/app/views/index/formLogin.phtml b/app/views/index/formLogin.phtml
index cc925ea59..b79c1b614 100644
--- a/app/views/index/formLogin.phtml
+++ b/app/views/index/formLogin.phtml
@@ -1,32 +1,39 @@
<div class="prompt">
- <h1><?php echo Minz_Translate::t('login'); ?></h1><?php
+ <h1><?php echo _t('login'); ?></h1><?php
switch (Minz_Configuration::authType()) {
case 'form':
?><form id="loginForm" method="post" action="<?php echo _url('index', 'formLogin'); ?>">
<div>
- <label for="username"><?php echo Minz_Translate::t('username'); ?></label>
+ <label for="username"><?php echo _t('username'); ?></label>
<input type="text" id="username" name="username" size="16" required="required" maxlength="16" pattern="[0-9a-zA-Z]{1,16}" autofocus="autofocus" />
</div>
<div>
- <label for="passwordPlain"><?php echo Minz_Translate::t('password'); ?></label>
+ <label for="passwordPlain"><?php echo _t('password'); ?></label>
<input type="password" id="passwordPlain" required="required" />
<input type="hidden" id="challenge" name="challenge" /><br />
- <noscript><strong><?php echo Minz_Translate::t('javascript_should_be_activated'); ?></strong></noscript>
+ <noscript><strong><?php echo _t('javascript_should_be_activated'); ?></strong></noscript>
</div>
<div>
- <button id="loginButton" type="submit" class="btn btn-important"><?php echo Minz_Translate::t('login'); ?></button>
+ <label class="checkbox" for="keep_logged_in">
+ <input type="checkbox" name="keep_logged_in" id="keep_logged_in" value="1" />
+ <?php echo _t('keep_logged_in'); ?>
+ </label>
+ <br />
+ </div>
+ <div>
+ <button id="loginButton" type="submit" class="btn btn-important"><?php echo _t('login'); ?></button>
</div>
</form><?php
break;
case 'persona':
?><p>
- <?php echo FreshRSS_Themes::icon('login'); ?>
- <a class="signin" href="#"><?php echo Minz_Translate::t('login_with_persona'); ?></a>
+ <?php echo _i('login'); ?>
+ <a class="signin" href="#"><?php echo _t('login_with_persona'); ?></a>
</p><?php
break;
} ?>
- <p><a href="<?php echo _url('index', 'about'); ?>"><?php echo Minz_Translate::t('about_freshrss'); ?></a></p>
+ <p><a href="<?php echo _url('index', 'about'); ?>"><?php echo _t('about_freshrss'); ?></a></p>
</div>
diff --git a/app/views/javascript/actualize.phtml b/app/views/javascript/actualize.phtml
index d08dc47d1..74cef4998 100644
--- a/app/views/javascript/actualize.phtml
+++ b/app/views/javascript/actualize.phtml
@@ -1,25 +1,24 @@
"use strict";
-var feeds = [<?php
- foreach ($this->feeds as $feed) {
- echo "'", Minz_Url::display(array('c' => 'feed', 'a' => 'actualize', 'params' => array('id' => $feed->id(), 'ajax' => '1')), 'php'), "',\n";
- }
- ?>],
+var feeds = [<?php foreach ($this->feeds as $feed) { ?>{<?php
+ ?>url: "<?php echo Minz_Url::display(array('c' => 'feed', 'a' => 'actualize', 'params' => array('id' => $feed->id(), 'ajax' => '1')), 'php'); ?>",<?php
+ ?>title: "<?php echo $feed->name(); ?>"<?php
+?>},<?php } ?>],
feed_processed = 0,
feed_count = feeds.length;
function initProgressBar(init) {
if (init) {
$("body").after("\<div id=\"actualizeProgress\" class=\"notification good\">\
- <?php echo _t('refresh'); ?> <span class=\"progress\">0 / " + feed_count + "</span><br />\
- <progress id=\"actualizeProgressBar\" value=\"0\" max=\"" + feed_count + "\"></progress>\
+ <?php echo _t('refresh'); ?><br /><span class=\"title\">/</span><br />\
+ <span class=\"progress\">0 / " + feed_count + "</span>\
</div>");
} else {
window.location.reload();
}
}
-function updateProgressBar(i) {
- $("#actualizeProgressBar").val(i);
+function updateProgressBar(i, title_feed) {
$("#actualizeProgress .progress").html(i + " / " + feed_count);
+ $("#actualizeProgress .title").html(title_feed);
}
function updateFeeds() {
@@ -43,10 +42,10 @@ function updateFeed() {
$.ajax({
type: 'POST',
- url: feed,
+ url: feed['url'],
}).complete(function (data) {
feed_processed++;
- updateProgressBar(feed_processed);
+ updateProgressBar(feed_processed, feed['title']);
if (feed_processed === feed_count) {
initProgressBar(false);
diff --git a/app/views/stats/repartition.phtml b/app/views/stats/repartition.phtml
index 09892d3c5..d9dc4c89d 100644
--- a/app/views/stats/repartition.phtml
+++ b/app/views/stats/repartition.phtml
@@ -2,23 +2,38 @@
<div class="post content">
<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('back_to_rss_feeds'); ?></a>
-
+
+ <h1><?php echo _t('stats_repartition'); ?></h1>
+
+ <select id="feed_select">
+ <option data-url="<?php echo _url('stats', 'repartition')?>"><?php echo _t('all_feeds')?></option>
+ <?php foreach ($this->categories as $category) {
+ $feeds = $category->feeds();
+ if (!empty($feeds)) {
+ echo '<optgroup label="', $category->name(), '">';
+ foreach ($feeds as $feed) {
+ if ($this->feed && $feed->id() == $this->feed->id()){
+ echo '<option value="', $feed->id(), '" selected="selected" data-url="', _url('stats', 'repartition', 'id', $feed->id()), '">', $feed->name(), '</option>';
+ } else {
+ echo '<option value="', $feed->id(), '" data-url="', _url('stats', 'repartition', 'id', $feed->id()), '">', $feed->name(), '</option>';
+ }
+ }
+ echo '</optgroup>';
+ }
+ }?>
+ </select>
+
<?php if ($this->feed) {?>
- <h1>
- <?php echo _t('stats_repartition'), " - "; ?>
- <a href="<?php echo _url('configure', 'feed', 'id', $this->feed->id()); ?>">
- <?php echo $this->feed->name(); ?>
- </a>
- </h1>
- <?php } else {?>
- <h1><?php echo _t('stats_repartition'); ?></h1>
+ <a href="<?php echo _url('configure', 'feed', 'id', $this->feed->id()); ?>">
+ <?php echo _t('administration'); ?>
+ </a>
<?php }?>
-
+
<div class="stat">
<h2><?php echo _t('stats_entry_per_hour'); ?></h2>
<div id="statsEntryPerHour" style="height: 300px"></div>
</div>
-
+
<div class="stat">
<h2><?php echo _t('stats_entry_per_day_of_week'); ?></h2>
<div id="statsEntryPerDayOfWeek" style="height: 300px"></div>
@@ -41,11 +56,22 @@ function initStats() {
return;
}
// Entry per hour
+ var avg_h = [];
+ for (var i = -1; i <= 24; i++) {
+ avg_h.push([i, <?php echo $this->averageHour?>]);
+ }
Flotr.draw(document.getElementById('statsEntryPerHour'),
- [<?php echo $this->repartitionHour ?>],
+ [{
+ data: <?php echo $this->repartitionHour ?>,
+ bars: {horizontal: false, show: true}
+ }, {
+ data: avg_h,
+ lines: {show: true},
+ label: <?php echo $this->averageHour?>,
+ yaxis: 2
+ }],
{
grid: {verticalLines: false},
- bars: {horizontal: false, show: true},
xaxis: {noTicks: 23,
tickFormatter: function(x) {
var x = parseInt(x);
@@ -55,14 +81,26 @@ function initStats() {
max: 23.9,
tickDecimals: 0},
yaxis: {min: 0},
+ y2axis: {showLabels: false},
mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
});
// Entry per day of week
+ var avg_dow = [];
+ for (var i = -1; i <= 7; i++) {
+ avg_dow.push([i, <?php echo $this->averageDayOfWeek?>]);
+ }
Flotr.draw(document.getElementById('statsEntryPerDayOfWeek'),
- [<?php echo $this->repartitionDayOfWeek ?>],
+ [{
+ data: <?php echo $this->repartitionDayOfWeek ?>,
+ bars: {horizontal: false, show: true}
+ }, {
+ data: avg_dow,
+ lines: {show: true},
+ label: <?php echo $this->averageDayOfWeek?>,
+ yaxis: 2
+ }],
{
grid: {verticalLines: false},
- bars: {horizontal: false, show: true},
xaxis: {noTicks: 6,
tickFormatter: function(x) {
var x = parseInt(x),
@@ -73,14 +111,26 @@ function initStats() {
max: 6.9,
tickDecimals: 0},
yaxis: {min: 0},
+ y2axis: {showLabels: false},
mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
});
// Entry per month
+ var avg_m = [];
+ for (var i = 0; i <= 13; i++) {
+ avg_m.push([i, <?php echo $this->averageMonth?>]);
+ }
Flotr.draw(document.getElementById('statsEntryPerMonth'),
- [<?php echo $this->repartitionMonth ?>],
+ [{
+ data: <?php echo $this->repartitionMonth ?>,
+ bars: {horizontal: false, show: true}
+ }, {
+ data: avg_m,
+ lines: {show: true},
+ label: <?php echo $this->averageMonth?>,
+ yaxis: 2
+ }],
{
grid: {verticalLines: false},
- bars: {horizontal: false, show: true},
xaxis: {noTicks: 12,
tickFormatter: function(x) {
var x = parseInt(x),
@@ -91,9 +141,10 @@ function initStats() {
max: 12.9,
tickDecimals: 0},
yaxis: {min: 0},
+ y2axis: {showLabels: false},
mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
});
-
+
}
initStats();
</script>