summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG27
-rw-r--r--CREDITS39
-rw-r--r--README.fr.md15
-rw-r--r--README.md23
-rw-r--r--app/Controllers/errorController.php40
-rw-r--r--app/Controllers/importExportController.php2
-rw-r--r--app/Controllers/subscriptionController.php7
-rw-r--r--app/Controllers/updateController.php5
-rw-r--r--app/Models/ConfigurationSetter.php12
-rw-r--r--app/Models/Feed.php11
-rwxr-xr-xapp/actualize_script.php15
-rw-r--r--app/i18n/de/admin.php170
-rw-r--r--app/i18n/de/conf.php169
-rw-r--r--app/i18n/de/feedback.php110
-rw-r--r--app/i18n/de/gen.php163
-rw-r--r--app/i18n/de/index.php61
-rw-r--r--app/i18n/de/install.php107
-rw-r--r--app/i18n/de/sub.php61
-rw-r--r--app/i18n/en/admin.php1
-rw-r--r--app/i18n/en/conf.php1
-rw-r--r--app/i18n/en/gen.php8
-rw-r--r--app/i18n/en/index.php3
-rw-r--r--app/i18n/en/install.php1
-rw-r--r--app/i18n/fr/admin.php1
-rw-r--r--app/i18n/fr/conf.php1
-rw-r--r--app/i18n/fr/gen.php8
-rw-r--r--app/i18n/fr/index.php3
-rw-r--r--app/i18n/fr/install.php1
-rw-r--r--app/install.php1
-rw-r--r--app/layout/aside_feed.phtml10
-rw-r--r--app/layout/header.phtml3
-rw-r--r--app/layout/nav_menu.phtml3
-rw-r--r--app/views/configure/shortcut.phtml2
-rw-r--r--app/views/helpers/javascript_vars.phtml33
-rw-r--r--app/views/index/about.phtml5
-rw-r--r--app/views/index/global.phtml2
-rw-r--r--app/views/subscription/index.phtml2
-rw-r--r--app/views/user/manage.phtml1
-rw-r--r--app/views/user/profile.phtml1
-rw-r--r--data/shares.php15
-rw-r--r--lib/Minz/Configuration.php7
-rw-r--r--lib/Minz/Error.php39
-rw-r--r--lib/Minz/ExtensionManager.php5
-rw-r--r--lib/Minz/Session.php4
-rw-r--r--lib/Minz/Url.php34
-rw-r--r--lib/SimplePie/SimplePie.php4
-rw-r--r--lib/SimplePie/SimplePie/File.php2
-rw-r--r--lib/lib_opml.php128
-rw-r--r--lib/lib_rss.php14
-rw-r--r--p/f.php21
-rw-r--r--p/scripts/main.js8
-rw-r--r--p/themes/BlueLagoon/README.md2
-rw-r--r--p/themes/Dark/dark.css6
-rw-r--r--p/themes/Screwdriver/README.md2
-rw-r--r--p/themes/base-theme/README.md2
-rw-r--r--p/themes/base-theme/template.css3
-rw-r--r--tests/app/Models/CategoryTest.php32
-rw-r--r--tests/bootstrap.php7
-rw-r--r--tests/phpunit.xml13
59 files changed, 1258 insertions, 218 deletions
diff --git a/CHANGELOG b/CHANGELOG
index f35169f59..b389f184b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,29 @@
-# Journal des modifications
+# Changelog
+
+## 2015-01-31 FreshRSS 1.0.0 / 1.1.0 (beta)
+
+* UI
+ * Slider math with Dark theme
+ * Add a message if request failed for mark as read / favourite
+* I18n
+ * Fix some sentences
+ * Add German as a supported language
+ * Add some indications on password format
+* Bug fixing
+ * Some shortcuts was never saved
+ * Global view didn't work if set by default
+ * Minz_Error was badly raised
+ * Feed update failed if nothing had changed (MySQL only)
+ * CRON task failed with multiple users
+ * Tricky bug caused by cookie path
+ * Email sharing was badly supported (no urlencode())
+* Misc.
+ * Add a CREDIT file with contributor names
+ * Update lib_opml
+ * Default favicon is now served by HTTP code 200
+ * Change calls to syslog by Minz_Log::notice
+ * HTTP credentials are no longer logged
+
## 2015-01-15 FreshRSS 0.9.4 (beta)
diff --git a/CREDITS b/CREDITS
new file mode 100644
index 000000000..47a968deb
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,39 @@
+This is a credit file of people who have contributed to FreshRSS with, at least,
+one commit on the FreshRSS repository (at https://github.com/FreshRSS/FreshRSS).
+Please note a commit on THIS specific file is not considered as a contribution
+(too easy!). It's purpose is to show even the smallest contribution is important.
+People are sorted by name so please keep this order.
+
+---
+
+Alexandre Alapetite
+https://github.com/Alkarex
+
+Alexis Degrugillier
+https://github.com/aledeg
+
+Alwaysin
+https://github.com/Alwaysin
+
+Amaury Carrade
+https://github.com/AmauryCarrade
+
+ealdraed
+https://github.com/ealdraed
+
+Luc Didry
+https://github.com/ldidry
+
+Marien Fressinaud
+dev@marienfressinaud.fr
+http://marienfressinaud.fr
+https://github.com/marienfressinaud
+
+Nicolas Elie
+https://github.com/nicolaselie
+
+plopoyop
+https://github.com/plopoyop
+
+tomgue
+https://github.com/tomgue
diff --git a/README.fr.md b/README.fr.md
index f1cf64a08..380d7bc1e 100644
--- a/README.fr.md
+++ b/README.fr.md
@@ -9,26 +9,23 @@ Il permet de gérer plusieurs utilisateurs, et dispose d’un mode de lecture an
* Site officiel : http://freshrss.org
* Démo : http://demo.freshrss.org/
-* Développeur : Marien Fressinaud <dev@marienfressinaud.fr>
-* Version actuelle : 0.9.4
-* Date de publication : 2015-01-15
-* License [GNU AGPL 3](http://www.gnu.org/licenses/agpl-3.0.html)
+* Licence : [GNU AGPL 3](http://www.gnu.org/licenses/agpl-3.0.html)
![Logo de FreshRSS](http://marienfressinaud.fr/data/images/freshrss/freshrss_title.png)
# Note sur les branches
**Ce logiciel est encore en développement !** Veuillez vous assurer d'utiliser la branche qui vous correspond :
-* Utilisez [la branche master](https://github.com/marienfressinaud/FreshRSS/tree/master/) si vous visez la stabilité.
-* [La branche beta](https://github.com/marienfressinaud/FreshRSS/tree/beta) est celle par défaut : les nouveautés y sont ajoutées environ tous les mois.
-* Pour les développeurs et ceux qui savent ce qu'ils font, [la branche dev](https://github.com/marienfressinaud/FreshRSS/tree/dev) vous ouvre les bras !
+* Utilisez [la branche master](https://github.com/FreshRSS/FreshRSS/tree/master/) si vous visez la stabilité.
+* [La branche beta](https://github.com/FreshRSS/FreshRSS/tree/beta) est celle par défaut : les nouveautés y sont ajoutées environ tous les mois.
+* Pour les développeurs et ceux qui savent ce qu'ils font, [la branche dev](https://github.com/FreshRSS/FreshRSS/tree/dev) vous ouvre les bras !
# Disclaimer
Cette application a été développée pour s’adapter à des besoins personnels et non professionnels.
Je ne garantis en aucun cas la sécurité de celle-ci, ni son bon fonctionnement.
Je m’engage néanmoins à répondre dans la mesure du possible aux demandes d’évolution si celles-ci me semblent justifiées.
Privilégiez pour cela des demandes sur GitHub
-(https://github.com/marienfressinaud/FreshRSS/issues) ou par mail (dev@marienfressinaud.fr)
+(https://github.com/FreshRSS/FreshRSS/issues).
# Pré-requis
* Serveur modeste, par exemple sous Linux ou Windows
@@ -44,7 +41,7 @@ Privilégiez pour cela des demandes sur GitHub
![Capture d’écran de FreshRSS](http://marienfressinaud.fr/data/images/freshrss/freshrss_default-design.png)
# Installation
-1. Récupérez l’application FreshRSS via la commande git ou [en téléchargeant l’archive](https://github.com/marienfressinaud/FreshRSS/archive/master.zip)
+1. Récupérez l’application FreshRSS via la commande git ou [en téléchargeant l’archive](https://github.com/FreshRSS/FreshRSS/archive/master.zip)
2. Placez l’application sur votre serveur (la partie à exposer au Web est le répertoire `./p/`)
3. Le serveur Web doit avoir les droits d’écriture dans le répertoire `./data/`
4. Accédez à FreshRSS à travers votre navigateur Web et suivez les instructions d’installation
diff --git a/README.md b/README.md
index 9ff10747e..a09a64639 100644
--- a/README.md
+++ b/README.md
@@ -9,26 +9,23 @@ It is a multi-user application with an anonymous reading mode.
* Official website: http://freshrss.org
* Demo: http://demo.freshrss.org/
-* Developer: Marien Fressinaud <dev@marienfressinaud.fr>
-* Current version: 0.9.4
-* Publication date: 2015-01-15
-* License [GNU AGPL 3](http://www.gnu.org/licenses/agpl-3.0.html)
+* License: [GNU AGPL 3](http://www.gnu.org/licenses/agpl-3.0.html)
![FreshRSS logo](http://marienfressinaud.fr/data/images/freshrss/freshrss_title.png)
# Note on branches
**This application is still in development!** Please use the branch that suits your needs:
-* Use [the master branch](https://github.com/marienfressinaud/FreshRSS/tree/master/) if you need a stable version.
-* [The beta branch](https://github.com/marienfressinaud/FreshRSS/tree/beta) is the default branch: new features are added on a monthly basis.
-* For developers and tech savvy persons, [the dev branch](https://github.com/marienfressinaud/FreshRSS/tree/dev) is waiting for you!
+* Use [the master branch](https://github.com/FreshRSS/FreshRSS/tree/master/) if you need a stable version.
+* [The beta branch](https://github.com/FreshRSS/FreshRSS/tree/beta) is the default branch: new features are added on a monthly basis.
+* For developers and tech savvy persons, [the dev branch](https://github.com/FreshRSS/FreshRSS/tree/dev) is waiting for you!
# Disclaimer
This application was developed to fulfill personal needs not professional needs.
There is no guarantee neither on its security nor its proper functioning.
If there is feature requests which I think are good for the project, I'll do my best to include them.
The best way is to open issues on GitHub
-(https://github.com/marienfressinaud/FreshRSS/issues) or by email (dev@marienfressinaud.fr)
+(https://github.com/FreshRSS/FreshRSS/issues).
# Requirements
* Light server running Linux or Windows
@@ -37,14 +34,14 @@ The best way is to open issues on GitHub
* PHP 5.2.1+ (PHP 5.3.7+ recommanded)
* Required extensions: [PDO_MySQL](http://php.net/pdo-mysql) or [PDO_SQLite](http://php.net/pdo-sqlite), [cURL](http://php.net/curl), [GMP](http://php.net/gmp) (only for API access on platforms under 64 bits)
* Recommanded extensions : [JSON](http://php.net/json), [mbstring](http://php.net/mbstring), [zlib](http://php.net/zlib), [Zip](http://php.net/zip)
-* MySQL 5.0.3+ (recommanded) ou SQLite 3.7.4+
+* MySQL 5.0.3+ (recommanded) or SQLite 3.7.4+
* A recent browser like Firefox 4+, Chrome, Opera, Safari, Internet Explorer 9+
* Works on mobile
![FreshRSS screenshot](http://marienfressinaud.fr/data/images/freshrss/freshrss_default-design.png)
# Installation
-1. Get FreshRSS with git or [by downloading the archive](https://github.com/marienfressinaud/FreshRSS/archive/master.zip)
+1. Get FreshRSS with git or [by downloading the archive](https://github.com/FreshRSS/FreshRSS/archive/master.zip)
2. Dump the application on your server (expose only the `./p/` folder)
3. Add write access on `./data/` folder to the webserver user
4. Access FreshRSS with your browser and follow the installation process
@@ -70,9 +67,9 @@ For example, if you want to run the script every hour:
# Advices
* For a better security, expose only the `./p/` folder on the web.
- * Be aware that the `./data/` folder contain all personal data, so it is a bad idea to expose it.
-* The `./constants.php` file define access to application folder. If you want to customize your installation, every thing happens here.
-* If you encounter some problem, logs are accessibles from the interface or manually in `./data/log/*.log` files.
+ * Be aware that the `./data/` folder contains all personal data, so it is a bad idea to expose it.
+* The `./constants.php` file defines access to application folder. If you want to customize your installation, every thing happens here.
+* If you encounter any problem, logs are accessibles from the interface or manually in `./data/log/*.log` files.
# Backup
* You need to keep `./data/config.php`, `./data/*_user.php` and `./data/persona/` files
diff --git a/app/Controllers/errorController.php b/app/Controllers/errorController.php
index 06fa186cf..b0bafda72 100644
--- a/app/Controllers/errorController.php
+++ b/app/Controllers/errorController.php
@@ -9,41 +9,43 @@ class FreshRSS_error_Controller extends Minz_ActionController {
*
* It is called by Minz_Error::error() method.
*
- * Parameters are:
- * - code (default: 404)
- * - logs (default: array())
+ * Parameters are passed by Minz_Session to have a proper url:
+ * - error_code (default: 404)
+ * - error_logs (default: array())
*/
public function indexAction() {
- $code_int = Minz_Request::param('code', 404);
+ $code_int = Minz_Session::param('error_code', 404);
+ $error_logs = Minz_Session::param('error_logs', array());
+ Minz_Session::_param('error_code');
+ Minz_Session::_param('error_logs');
+
switch ($code_int) {
+ case 200 :
+ header('HTTP/1.1 200 OK');
+ break;
case 403:
+ header('HTTP/1.1 403 Forbidden');
$this->view->code = 'Error 403 - Forbidden';
- break;
- case 404:
- $this->view->code = 'Error 404 - Not found';
+ $this->view->errorMessage = _t('feedback.access.denied');
break;
case 500:
+ header('HTTP/1.1 500 Internal Server Error');
$this->view->code = 'Error 500 - Internal Server Error';
break;
case 503:
+ header('HTTP/1.1 503 Service Unavailable');
$this->view->code = 'Error 503 - Service Unavailable';
break;
+ case 404:
default:
+ header('HTTP/1.1 404 Not Found');
$this->view->code = 'Error 404 - Not found';
+ $this->view->errorMessage = _t('feedback.access.not_found');
}
- $errors = Minz_Request::param('logs', array());
- $this->view->errorMessage = trim(implode($errors));
- if ($this->view->errorMessage == '') {
- switch($code_int) {
- case 403:
- $this->view->errorMessage = _t('feedback.access.denied');
- break;
- case 404:
- default:
- $this->view->errorMessage = _t('feedback.access.not_found');
- break;
- }
+ $error_message = trim(implode($error_logs));
+ if ($error_message !== '') {
+ $this->view->errorMessage = $error_message;
}
Minz_View::prependTitle($this->view->code . ' · ');
diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php
index db9db66a7..589777b2a 100644
--- a/app/Controllers/importExportController.php
+++ b/app/Controllers/importExportController.php
@@ -151,7 +151,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
private function importOpml($opml_file) {
$opml_array = array();
try {
- $opml_array = libopml_parse_string($opml_file);
+ $opml_array = libopml_parse_string($opml_file, false);
} catch (LibOPML_Exception $e) {
Minz_Log::warning($e->getMessage());
return true;
diff --git a/app/Controllers/subscriptionController.php b/app/Controllers/subscriptionController.php
index 6152b7252..333565faf 100644
--- a/app/Controllers/subscriptionController.php
+++ b/app/Controllers/subscriptionController.php
@@ -102,13 +102,14 @@ class FreshRSS_subscription_Controller extends Minz_ActionController {
invalidateHttpCache();
- if ($feedDAO->updateFeed($id, $values)) {
+ $url_redirect = array('c' => 'subscription', 'params' => array('id' => $id));
+ if ($feedDAO->updateFeed($id, $values) !== false) {
$this->view->feed->_category($cat);
$this->view->feed->faviconPrepare();
- Minz_Request::good(_t('feedback.sub.feed.updated'), array('c' => 'subscription', 'params' => array('id' => $id)));
+ Minz_Request::good(_t('feedback.sub.feed.updated'), $url_redirect);
} else {
- Minz_Request::bad(_t('feedback.sub.error'), array('c' => 'subscription'));
+ Minz_Request::bad(_t('feedback.sub.feed.error'), $url_redirect);
}
}
}
diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php
index 61b62773b..4797a3486 100644
--- a/app/Controllers/updateController.php
+++ b/app/Controllers/updateController.php
@@ -28,7 +28,10 @@ class FreshRSS_update_Controller extends Minz_ActionController {
);
} elseif (file_exists(UPDATE_FILENAME)) {
// There is an update file to apply!
- $version = file_get_contents(join_path(DATA_PATH, 'last_update.txt'));
+ $version = @file_get_contents(join_path(DATA_PATH, 'last_update.txt'));
+ if (empty($version)) {
+ $version = 'unknown';
+ }
$this->view->update_to_apply = true;
$this->view->message = array(
'status' => 'good',
diff --git a/app/Models/ConfigurationSetter.php b/app/Models/ConfigurationSetter.php
index f1c465c7c..eeb1f2f4c 100644
--- a/app/Models/ConfigurationSetter.php
+++ b/app/Models/ConfigurationSetter.php
@@ -153,11 +153,11 @@ class FreshRSS_ConfigurationSetter {
}
private function _shortcuts(&$data, $values) {
- foreach ($values as $key => $value) {
- if (isset($data['shortcuts'][$key])) {
- $data['shortcuts'][$key] = $value;
- }
+ if (!is_array($values)) {
+ return;
}
+
+ $data['shortcuts'] = $values;
}
private function _sort_order(&$data, $value) {
@@ -210,9 +210,7 @@ class FreshRSS_ConfigurationSetter {
private function _mark_when(&$data, $values) {
foreach ($values as $key => $value) {
- if (isset($data['mark_when'][$key])) {
- $data['mark_when'][$key] = $this->handleBool($value);
- }
+ $data['mark_when'][$key] = $this->handleBool($value);
}
}
diff --git a/app/Models/Feed.php b/app/Models/Feed.php
index 86cbb783e..5ce03be5d 100644
--- a/app/Models/Feed.php
+++ b/app/Models/Feed.php
@@ -240,19 +240,16 @@ class FreshRSS_Feed extends Minz_Model {
$subscribe_url = $feed->subscribe_url(true);
}
+ $clean_url = url_remove_credentials($subscribe_url);
if ($subscribe_url !== null && $subscribe_url !== $url) {
- if ($this->httpAuth != '') {
- // on enlève les id si authentification HTTP
- $subscribe_url = preg_replace('#((.+)://)((.+)@)(.+)#', '${1}${5}', $subscribe_url);
- }
- $this->_url($subscribe_url);
+ $this->_url($clean_url);
}
if (($mtime === true) ||($mtime > $this->lastUpdate)) {
- syslog(LOG_DEBUG, 'FreshRSS no cache ' . $mtime . ' > ' . $this->lastUpdate . ' for ' . $subscribe_url);
+ Minz_Log::notice('FreshRSS no cache ' . $mtime . ' > ' . $this->lastUpdate . ' for ' . $clean_url);
$this->loadEntries($feed); // et on charge les articles du flux
} else {
- syslog(LOG_DEBUG, 'FreshRSS use cache for ' . $subscribe_url);
+ Minz_Log::notice('FreshRSS use cache for ' . $clean_url);
$this->entries = array();
}
diff --git a/app/actualize_script.php b/app/actualize_script.php
index c7959be82..fc4f9bfbb 100755
--- a/app/actualize_script.php
+++ b/app/actualize_script.php
@@ -21,8 +21,10 @@ $_GET['force'] = true;
$_SERVER['HTTP_HOST'] = '';
+$log_file = join_path(USERS_PATH, '_', 'log.txt');
+
+
$app = new FreshRSS();
-$app->init();
$system_conf = Minz_Configuration::get('system');
$system_conf->auth_type = 'none'; // avoid necessity to be logged in (not saved!)
@@ -42,13 +44,13 @@ $min_last_activity = time() - $limits['max_inactivity'];
foreach ($users as $user) {
if (($user !== $system_conf->default_user) &&
(FreshRSS_UserDAO::mtime($user) < $min_last_activity)) {
- syslog(LOG_INFO, 'FreshRSS skip inactive user ' . $user);
+ Minz_Log::notice('FreshRSS skip inactive user ' . $user, $log_file);
if (defined('STDOUT')) {
fwrite(STDOUT, 'FreshRSS skip inactive user ' . $user . "\n"); //Unbuffered
}
continue;
}
- syslog(LOG_INFO, 'FreshRSS actualize ' . $user);
+ Minz_Log::notice('FreshRSS actualize ' . $user, $log_file);
if (defined('STDOUT')) {
fwrite(STDOUT, 'Actualize ' . $user . "...\n"); //Unbuffered
}
@@ -56,12 +58,15 @@ foreach ($users as $user) {
Minz_Session::_param('currentUser', $user);
+ new Minz_ModelPdo($user); //TODO: FIXME: Quick-fix while waiting for a better FreshRSS() constructor/init
FreshRSS_Auth::giveAccess();
+ $app->init();
$app->run();
if (!invalidateHttpCache()) {
- syslog(LOG_NOTICE, 'FreshRSS write access problem in ' . join_path(USERS_PATH, $user, 'log.txt'));
+ Minz_Log::notice('FreshRSS write access problem in ' . join_path(USERS_PATH, $user, 'log.txt'),
+ $log_file);
if (defined('STDERR')) {
fwrite(STDERR, 'Write access problem in ' . join_path(USERS_PATH, $user, 'log.txt') . "\n");
}
@@ -69,7 +74,7 @@ foreach ($users as $user) {
}
-syslog(LOG_INFO, 'FreshRSS actualize done.');
+Minz_Log::notice('FreshRSS actualize done.', $log_file);
if (defined('STDOUT')) {
fwrite(STDOUT, 'Done.' . "\n");
$end_date = date_create('now');
diff --git a/app/i18n/de/admin.php b/app/i18n/de/admin.php
new file mode 100644
index 000000000..bcd0fcc61
--- /dev/null
+++ b/app/i18n/de/admin.php
@@ -0,0 +1,170 @@
+<?php
+
+return array(
+ 'auth' => array(
+ 'allow_anonymous' => 'Anonymes Lesen der Artikel des Standardbenutzers (%s) erlauben',
+ 'allow_anonymous_refresh' => 'Anonymes Aktualisieren der Artikel erlauben',
+ 'api_enabled' => '<abbr>API</abbr>-Zugriff erlauben <small>(für mobile Anwendungen benötigt)</small>',
+ 'form' => 'Webformular (traditionell, benötigt JavaScript)',
+ 'http' => 'HTTP (HTTPS für erfahrene Benutzer)',
+ 'none' => 'Keine (gefährlich)',
+ 'persona' => 'Mozilla Persona (modern, benötigt JavaScript)',
+ 'title' => 'Authentifizierung',
+ 'title_reset' => 'Zurücksetzen der Authentifizierung',
+ 'token' => 'Authentifizierungs-Token',
+ 'token_help' => 'Erlaubt den Zugriff auf die RSS-Ausgabe des Standardbenutzers ohne Authentifizierung.',
+ 'type' => 'Authentifizierungsmethode',
+ 'unsafe_autologin' => 'Erlaube unsicheres automatisches Anmelden mit folgendem Format: ',
+ ),
+ 'check_install' => array(
+ 'cache' => array(
+ 'nok' => 'Überprüfen Sie die Berechtigungen des Verzeichnisses <em>./data/cache</em>. Der HTTP-Server muss Schreibrechte besitzen.',
+ 'ok' => 'Berechtigungen des Verzeichnisses <em>./data/cache</em> sind in Ordnung.',
+ ),
+ 'categories' => array(
+ 'nok' => 'Die Tabelle <em>category</em> ist schlecht konfiguriert.',
+ 'ok' => 'Die Tabelle <em>category</em> ist in Ordnung.',
+ ),
+ 'connection' => array(
+ 'nok' => 'Verbindung zur Datenbank kann nicht aufgebaut werden.',
+ 'ok' => 'Verbindung zur Datenbank ist in Ordnung.',
+ ),
+ 'ctype' => array(
+ 'nok' => 'Ihnen fehlt eine benötigte Bibliothek für die Überprüfung von Zeichentypen (php-ctype).',
+ 'ok' => 'Sie haben die benötigte Bibliothek für die Überprüfung von Zeichentypen (ctype).',
+ ),
+ 'curl' => array(
+ 'nok' => 'Ihnen fehlt cURL (Paket php5-curl).',
+ 'ok' => 'Sie haben die cURL-Erweiterung.',
+ ),
+ 'data' => array(
+ 'nok' => 'Überprüfen Sie die Berechtigungen des Verzeichnisses <em>./data</em>. Der HTTP-Server muss Schreibrechte besitzen.',
+ 'ok' => 'Berechtigungen des Verzeichnisses <em>./data</em> sind in Ordnung.',
+ ),
+ 'database' => 'Datenbank-Installation',
+ 'dom' => array(
+ 'nok' => 'Ihnen fehlt eine benötigte Bibliothek um DOM zu durchstöbern (Paket php-xml).',
+ 'ok' => 'Sie haben die benötigte Bibliothek um DOM zu durchstöbern.',
+ ),
+ 'entries' => array(
+ 'nok' => 'Die Tabelle <em>entry</em> ist schlecht konfiguriert.',
+ 'ok' => 'Die Tabelle <em>entry</em> ist in Ordnung.',
+ ),
+ 'favicons' => array(
+ 'nok' => 'Überprüfen Sie die Berechtigungen des Verzeichnisses <em>./data/favicons</em>. Der HTTP-Server muss Schreibrechte besitzen.',
+ 'ok' => 'Berechtigungen des Verzeichnisses <em>./data/favicons</em> sind in Ordnung.',
+ ),
+ 'feeds' => array(
+ 'nok' => 'Die Tabelle <em>feed</em> ist schlecht konfiguriert.',
+ 'ok' => 'Die Tabelle <em>feed</em> ist in Ordnung.',
+ ),
+ 'files' => 'Datei-Installation',
+ 'json' => array(
+ 'nok' => 'Ihnen fehlt JSON (Paket php5-json).',
+ 'ok' => 'Sie haben die JSON-Erweiterung.',
+ ),
+ 'minz' => array(
+ 'nok' => 'Ihnen fehlt das Minz-Framework.',
+ 'ok' => 'Sie haben das Minz-Framework.',
+ ),
+ 'pcre' => array(
+ 'nok' => 'Ihnen fehlt eine benötigte Bibliothek für reguläre Ausdrücke (php-pcre).',
+ 'ok' => 'Sie haben die benötigte Bibliothek für reguläre Ausdrücke (PCRE).',
+ ),
+ 'pdo' => array(
+ 'nok' => 'Ihnen fehlt PDO oder einer der unterstützten Treiber (pdo_mysql, pdo_sqlite).',
+ 'ok' => 'Sie haben PDO und mindestens einen der unterstützten Treiber (pdo_mysql, pdo_sqlite).',
+ ),
+ 'persona' => array(
+ 'nok' => 'Überprüfen Sie die Berechtigungen des Verzeichnisses <em>./data/persona</em>. Der HTTP-Server muss Schreibrechte besitzen.',
+ 'ok' => 'Berechtigungen des Verzeichnisses <em>./data/persona</em> sind in Ordnung.',
+ ),
+ 'php' => array(
+ '_' => 'PHP-Installation',
+ 'nok' => 'Ihre PHP-Version ist %s aber FreshRSS benötigt mindestens Version %s.',
+ 'ok' => 'Ihre PHP-Version ist %s, welche kompatibel mit FreshRSS ist.',
+ ),
+ 'tables' => array(
+ 'nok' => 'Es fehlen eine oder mehrere Tabellen in der Datenbank.',
+ 'ok' => 'Tabellen existieren in der Datenbank.',
+ ),
+ 'title' => 'Installationsüberprüfung',
+ 'tokens' => array(
+ 'nok' => 'Überprüfen Sie die Berechtigungen des Verzeichnisses <em>./data/tokens</em>. Der HTTP-Server muss Schreibrechte besitzen.',
+ 'ok' => 'Berechtigungen des Verzeichnisses <em>./data/tokens</em> sind in Ordnung.',
+ ),
+ 'users' => array(
+ 'nok' => 'Überprüfen Sie die Berechtigungen des Verzeichnisses <em>./data/users</em>. Der HTTP-Server muss Schreibrechte besitzen.',
+ 'ok' => 'Berechtigungen des Verzeichnisses <em>./data/users</em> sind in Ordnung.',
+ ),
+ 'zip' => array(
+ 'nok' => 'Ihnen fehlt die ZIP-Erweiterung (Paket php5-zip).',
+ 'ok' => 'Sie haben die ZIP-Erweiterung.',
+ ),
+ ),
+ 'extensions' => array(
+ 'disabled' => 'Deaktiviert',
+ 'empty_list' => 'Es gibt keine installierte Erweiterung.',
+ 'enabled' => 'Aktiviert',
+ 'no_configure_view' => 'Diese Erweiterung kann nicht konfiguriert werden.',
+ 'system' => array(
+ '_' => 'System-Erweiterungen',
+ 'no_rights' => 'System-Erweiterung (Sie haben keine Berechtigung dafür)',
+ ),
+ 'title' => 'Erweiterungen',
+ 'user' => 'Benutzer-Erweiterungen',
+ ),
+ 'stats' => array(
+ '_' => 'Statistiken',
+ 'all_feeds' => 'Alle Feeds',
+ 'category' => 'Kategorie',
+ 'entry_count' => 'Anzahl der Einträge',
+ 'entry_per_category' => 'Einträge pro Kategorie',
+ 'entry_per_day' => 'Einträge pro Tag (letzte 30 Tage)',
+ 'entry_per_day_of_week' => 'Pro Wochentag (Durchschnitt: %.2f Nachrichten)',
+ 'entry_per_hour' => 'Pro Stunde (Durchschnitt: %.2f Nachrichten)',
+ 'entry_per_month' => 'Pro Monat (Durchschnitt: %.2f Nachrichten)',
+ 'entry_repartition' => 'Einträge-Verteilung',
+ 'feed' => 'Feed',
+ 'feed_per_category' => 'Feeds pro Kategorie',
+ 'idle' => 'Untätige Feeds',
+ 'main' => 'Haupt-Statistiken',
+ 'main_stream' => 'Haupt-Feeds',
+ 'menu' => array(
+ 'idle' => 'Untätige Feeds',
+ 'main' => 'Haupt-Statistiken',
+ 'repartition' => 'Artikel-Verteilung',
+ ),
+ 'no_idle' => 'Es gibt keinen untätigen Feed!',
+ 'number_entries' => '%d Artikel',
+ 'percent_of_total' => '%% Gesamt',
+ 'repartition' => 'Artikel-Verteilung',
+ 'status_favorites' => 'Favoriten',
+ 'status_read' => 'Gelesen',
+ 'status_total' => 'Gesamt',
+ 'status_unread' => 'Ungelesen',
+ 'title' => 'Statistiken',
+ 'top_feed' => 'Top 10-Feeds',
+ ),
+ 'update' => array(
+ '_' => 'System aktualisieren',
+ 'apply' => 'Anwenden',
+ 'check' => 'Auf neue Aktualisierungen prüfen',
+ 'current_version' => 'Ihre aktuelle Version von FreshRSS ist %s.',
+ 'last' => 'Letzte Überprüfung: %s',
+ 'none' => 'Keine Aktualisierung zum Anwenden',
+ 'title' => 'System aktualisieren',
+ ),
+ 'user' => array(
+ 'articles_and_size' => '%s Artikel (%s)',
+ 'create' => 'Neuen Benutzer erstellen',
+ 'email_persona' => 'Anmelde-E-Mail-Adresse<br /><small>(für <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
+ 'language' => 'Sprache',
+ 'password_form' => 'Passwort<br /><small>(für die Anmeldemethode per Webformular)</small>',
+ 'password_format' => 'mindestens 7 Zeichen',
+ 'title' => 'Benutzer verwalten',
+ 'user_list' => 'Liste der Benutzer',
+ 'username' => 'Nutzername',
+ 'users' => 'Benutzer',
+ ),
+);
diff --git a/app/i18n/de/conf.php b/app/i18n/de/conf.php
new file mode 100644
index 000000000..64c2c0945
--- /dev/null
+++ b/app/i18n/de/conf.php
@@ -0,0 +1,169 @@
+<?php
+
+return array(
+ 'archiving' => array(
+ '_' => 'Archivierung',
+ 'advanced' => 'Erweitert',
+ 'delete_after' => 'Entferne Artikel nach',
+ 'help' => 'Weitere Optionen sind in den Einstellungen der individuellen Nachrichten-Feeds vorhanden.',
+ 'keep_history_by_feed' => 'Minimale Anzahl an Artikeln, die pro Feed behalten wird',
+ 'optimize' => 'Datenbank optimieren',
+ 'optimize_help' => 'Sollte gelegentlich durchgeführt werden, um die Größe der Datenbank zu reduzieren.',
+ 'purge_now' => 'Jetzt bereinigen',
+ 'title' => 'Archivierung',
+ 'ttl' => 'Aktualisiere automatisch nicht öfter als',
+ ),
+ 'display' => array(
+ '_' => 'Anzeige',
+ 'icon' => array(
+ 'bottom_line' => 'Fußzeile',
+ 'entry' => 'Artikel-Symbole',
+ 'publication_date' => 'Datum der Veröffentlichung',
+ 'related_tags' => 'Verwandte Tags',
+ 'sharing' => 'Teilen',
+ 'top_line' => 'Kopfzeile',
+ ),
+ 'language' => 'Sprache',
+ 'notif_html5' => array(
+ 'seconds' => 'Sekunden (0 bedeutet keine Zeitüberschreitung)',
+ 'timeout' => 'Zeitüberschreitung für HTML5-Benachrichtigung',
+ ),
+ 'theme' => 'Erscheinungsbild',
+ 'title' => 'Anzeige',
+ 'width' => array(
+ 'content' => 'Inhaltsbreite',
+ 'large' => 'Weit',
+ 'medium' => 'Mittel',
+ 'no_limit' => 'Keine Begrenzung',
+ 'thin' => 'Schmal',
+ ),
+ ),
+ 'query' => array(
+ '_' => 'Benutzerabfragen',
+ 'deprecated' => 'Diese Abfrage ist nicht länger gültig. Die referenzierte Kategorie oder der Feed ist gelöscht worden.',
+ 'filter' => 'Angewendeter Filter:',
+ 'get_all' => 'Alle Artikel anzeigen',
+ 'get_category' => 'Kategorie "%s" anzeigen',
+ 'get_favorite' => 'Lieblingsartikel anzeigen',
+ 'get_feed' => 'Feed "%s" anzeigen',
+ 'no_filter' => 'Kein Filter',
+ 'none' => 'Sie haben bisher keine Benutzerabfrage erstellt.',
+ 'number' => 'Abfrage Nr. %d',
+ 'order_asc' => 'Älteste Artikel zuerst anzeigen',
+ 'order_desc' => 'Neueste Artikel zuerst anzeigen',
+ 'search' => 'Suche nach "%s"',
+ 'state_0' => 'Alle Artikel anzeigen',
+ 'state_1' => 'Gelesene Artikel anzeigen',
+ 'state_2' => 'Ungelesene Artikel anzeigen',
+ 'state_3' => 'Alle Artikel anzeigen',
+ 'state_4' => 'Lieblingsartikel anzeigen',
+ 'state_5' => 'Gelesene Lieblingsartikel anzeigen',
+ 'state_6' => 'Ungelesene Lieblingsartikel anzeigen',
+ 'state_7' => 'Lieblingsartikel anzeigen',
+ 'state_8' => 'Keine Lieblingsartikel anzeigen',
+ 'state_9' => 'Gelesene ohne Lieblingsartikel anzeigen',
+ 'state_10' => 'Ungelesene ohne Lieblingsartikel anzeigen',
+ 'state_11' => 'Keine Lieblingsartikel anzeigen',
+ 'state_12' => 'Alle Artikel anzeigen',
+ 'state_13' => 'Gelesene Artikel anzeigen',
+ 'state_14' => 'Ungelesene Artikel anzeigen',
+ 'state_15' => 'Alle Artikel anzeigen',
+ 'title' => 'Benutzerabfragen',
+ ),
+ 'profile' => array(
+ '_' => 'Profil-Verwaltung',
+ 'email_persona' => 'Anmelde-E-Mail-Adresse<br /><small>(für <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
+ 'password_api' => 'Passwort-API<br /><small>(z. B. für mobile Anwendungen)</small>',
+ 'password_form' => 'Passwort<br /><small>(für die Anmeldemethode per Webformular)</small>',
+ 'password_format' => 'mindestens 7 Zeichen',
+ 'title' => 'Profil',
+ ),
+ 'reading' => array(
+ '_' => 'Lesen',
+ 'after_onread' => 'Nach „Alle als gelesen markieren“,',
+ 'articles_per_page' => 'Anzahl der Artikel pro Seite',
+ 'auto_load_more' => 'Die nächsten Artikel am Seitenende laden',
+ 'auto_remove_article' => 'Artikel nach dem Lesen verstecken',
+ 'confirm_enabled' => 'Bei der Aktion „Alle als gelesen markieren“ einen Bestätigungsdialog anzeigen',
+ 'display_articles_unfolded' => 'Artikel standardmäßig ausgeklappt zeigen',
+ 'display_categories_unfolded' => 'Kategorien standardmäßig eingeklappt zeigen',
+ 'hide_read_feeds' => 'Kategorien & Feeds ohne ungelesene Artikel verstecken (funktioniert nicht mit der Einstellung „Alle Artikel zeigen“)',
+ 'img_with_lazyload' => 'Verwende die "träges Laden"-Methode zum Laden von Bildern',
+ 'jump_next' => 'springe zum nächsten ungelesenen Geschwisterelement (Feed oder Kategorie)',
+ 'number_divided_when_reader' => 'Geteilt durch 2 in der Lese-Ansicht.',
+ 'read' => array(
+ 'article_open_on_website' => 'wenn der Artikel auf der Original-Webseite geöffnet wird',
+ 'article_viewed' => 'wenn der Artikel angesehen wird',
+ 'scroll' => 'beim Blättern',
+ 'upon_reception' => 'beim Empfang des Artikels',
+ 'when' => 'Artikel als gelesen markieren…',
+ ),
+ 'show' => array(
+ '_' => 'Artikel zum Anzeigen',
+ 'adaptive' => 'Anzeige anpassen',
+ 'all_articles' => 'Alle Artikel zeigen',
+ 'unread' => 'Nur ungelesene zeigen',
+ ),
+ 'sort' => array(
+ '_' => 'Sortierreihenfolge',
+ 'newer_first' => 'Neuere zuerst',
+ 'older_first' => 'Ältere zuerst',
+ ),
+ 'sticky_post' => 'Wenn geöffnet, den Artikel ganz oben anheften',
+ 'title' => 'Lesen',
+ 'view' => array(
+ 'default' => 'Standard-Ansicht',
+ 'global' => 'Globale Ansicht',
+ 'normal' => 'Normale Ansicht',
+ 'reader' => 'Lese-Ansicht',
+ ),
+ ),
+ 'sharing' => array(
+ '_' => 'Teilen',
+ 'blogotext' => 'Blogotext',
+ 'diaspora' => 'Diaspora*',
+ 'email' => 'E-Mail',
+ 'facebook' => 'Facebook',
+ 'g+' => 'Google+',
+ 'more_information' => 'Weitere Informationen',
+ 'print' => 'Drucken',
+ 'shaarli' => 'Shaarli',
+ 'share_name' => 'Anzuzeigender Teilen-Name',
+ 'share_url' => 'Zu verwendende Teilen-URL',
+ 'title' => 'Teilen',
+ 'twitter' => 'Twitter',
+ 'wallabag' => 'wallabag',
+ ),
+ 'shortcut' => array(
+ '_' => 'Tastaturkürzel',
+ 'article_action' => 'Artikelaktionen',
+ 'auto_share' => 'Teilen',
+ 'auto_share_help' => 'Wenn es nur eine Option zum Teilen gibt, wird diese verwendet. Ansonsten sind die Optionen über ihre Nummer erreichbar.',
+ 'close_dropdown' => 'Menüs schließen',
+ 'collapse_article' => 'Zusammenfalten',
+ 'first_article' => 'Zum ersten Artikel springen',
+ 'focus_search' => 'Auf Suchfeld zugreifen',
+ 'help' => 'Dokumentation anzeigen',
+ 'javascript' => 'JavaScript muss aktiviert sein, um Tastaturkürzel benutzen zu können',
+ 'last_article' => 'Zum letzten Artikel springen',
+ 'load_more' => 'Weitere Artikel laden',
+ 'mark_read' => 'Als gelesen markieren',
+ 'mark_favorite' => 'Als Favorit markieren',
+ 'navigation' => 'Navigation',
+ 'navigation_help' => 'Mit der "Umschalttaste" finden die Tastaturkürzel auf Feeds Anwendung.<br/>Mit der "Alt-Taste" finden die Tastaturkürzel auf Kategorien Anwendung.',
+ 'next_article' => 'Zum nächsten Artikel springen',
+ 'other_action' => 'Andere Aktionen',
+ 'previous_article' => 'Zum vorherigen Artikel springen',
+ 'see_on_website' => 'Auf der Original-Webseite ansehen',
+ 'shift_for_all_read' => '+ <code>Umschalttaste</code>, um alle Artikel als gelesen zu markieren.',
+ 'title' => 'Tastaturkürzel',
+ 'user_filter' => 'Auf Benutzerfilter zugreifen',
+ 'user_filter_help' => 'Wenn es nur einen Benutzerfilter gibt, wird dieser verwendet. Ansonsten sind die Filter über ihre Nummer erreichbar.',
+ ),
+ 'user' => array(
+ 'articles_and_size' => '%s Artikel (%s)',
+ 'current' => 'Aktueller Benutzer',
+ 'is_admin' => 'ist Administrator',
+ 'users' => 'Benutzer',
+ ),
+);
diff --git a/app/i18n/de/feedback.php b/app/i18n/de/feedback.php
new file mode 100644
index 000000000..48f8b74f5
--- /dev/null
+++ b/app/i18n/de/feedback.php
@@ -0,0 +1,110 @@
+<?php
+
+return array(
+ 'admin' => array(
+ 'optimization_complete' => 'Optimierung abgeschlossen',
+ ),
+ 'access' => array(
+ 'denied' => 'Sie haben nicht die Berechtigung, diese Seite aufzurufen',
+ 'not_found' => 'Sie suchen nach einer Seite, die nicht existiert',
+ ),
+ 'auth' => array(
+ 'form' => array(
+ 'not_set' => 'Während der Konfiguration des Authentifikationssystems trat ein Fehler auf. Bitte versuchen Sie es später erneut.',
+ 'set' => 'Formular ist ab sofort ihr Standard-Authentifikationssystem.',
+ ),
+ 'login' => array(
+ 'invalid' => 'Anmeldung ist ungültig',
+ 'success' => 'Sie sind verbunden',
+ ),
+ 'logout' => array(
+ 'success' => 'Sie sind getrennt',
+ ),
+ 'no_password_set' => 'Administrator-Passwort ist nicht gesetzt worden. Dieses Feature ist nicht verfügbar.',
+ 'not_persona' => 'Nur das Persona-System kann zurückgesetzt werden.',
+ ),
+ 'conf' => array(
+ 'error' => 'Während des Speicherung der Konfiguration trat ein Fehler auf',
+ 'query_created' => 'Abfrage "%s" ist erstellt worden.',
+ 'shortcuts_updated' => 'Tastaturkürzel sind aktualisiert worden',
+ 'updated' => 'Konfiguration ist aktualisiert worden',
+ ),
+ 'extensions' => array(
+ 'already_enabled' => '%s ist bereits aktiviert',
+ 'disable' => array(
+ 'ko' => '%s kann nicht deaktiviert werden. Für Details <a href="%s">prüfen Sie die FressRSS-Protokolle</a>.',
+ 'ok' => '%s ist jetzt deaktiviert',
+ ),
+ 'enable' => array(
+ 'ko' => '%s kann nicht aktiviert werden. Für Details <a href="%s">prüfen Sie die FressRSS-Protokolle</a>.',
+ 'ok' => '%s ist jetzt aktiviert',
+ ),
+ 'no_access' => 'Sie haben keinen Zugang zu %s',
+ 'not_enabled' => '%s ist noch nicht aktiviert',
+ 'not_found' => '%s existiert nicht',
+ ),
+ 'import_export' => array(
+ 'export_no_zip_extension' => 'Die Zip-Erweiterung fehlt auf Ihrem Server. Bitte versuchen Sie, Dateien eine nach der anderen zu exportieren.',
+ 'feeds_imported' => 'Ihre Feeds sind importiert worden und werden jetzt aktualisiert',
+ 'feeds_imported_with_errors' => 'Ihre Feeds sind importiert worden, aber es traten einige Fehler auf',
+ 'file_cannot_be_uploaded' => 'Datei kann nicht hochgeladen werden!',
+ 'no_zip_extension' => 'Die Zip-Erweiterung ist auf Ihrem Server nicht vorhanden.',
+ 'zip_error' => 'Ein Fehler trat während des Zip-Imports auf.',
+ ),
+ 'sub' => array(
+ 'actualize' => 'Aktualisieren',
+ 'category' => array(
+ 'created' => 'Kategorie %s ist erstellt worden.',
+ 'deleted' => 'Kategorie ist gelöscht worden.',
+ 'emptied' => 'Kategorie ist geleert worden.',
+ 'error' => 'Kategorie kann nicht aktualisiert werden',
+ 'name_exists' => 'Kategorie-Name existiert bereits.',
+ 'no_id' => 'Sie müssen die ID der Kategorie präzisieren.',
+ 'no_name' => 'Kategorie-Name kann nicht leer sein.',
+ 'not_delete_default' => 'Sie können die Vorgabe-Kategorie nicht löschen!',
+ 'not_exist' => 'Die Kategorie existiert nicht!',
+ 'over_max' => 'Sie haben Ihr Kategorien-Limit erreicht (%d)',
+ 'updated' => 'Kategorie ist aktualisiert worden.',
+ ),
+ 'feed' => array(
+ 'actualized' => '<em>%s</em> ist aktualisiert worden',
+ 'actualizeds' => 'RSS-Feeds sind aktualisiert worden',
+ 'added' => 'RSS-Feed <em>%s</em> ist hinzugefügt worden',
+ 'already_subscribed' => 'Sie haben <em>%s</em> bereits abonniert',
+ 'deleted' => 'Feed ist gelöscht worden',
+ 'error' => 'Feed kann nicht aktualisiert werden',
+ 'internal_problem' => 'Der RSS-Feed konnte nicht hinzugefügt werden. Für Details <a href="%s">prüfen Sie die FressRSS-Protokolle</a>.',
+ 'invalid_url' => 'URL <em>%s</em> ist ungültig',
+ 'marked_read' => 'Feeds sind als gelesen markiert worden',
+ 'n_actualized' => '%d Feeds sind aktualisiert worden',
+ 'n_entries_deleted' => '%d Artikel sind gelöscht worden',
+ 'no_refresh' => 'Es gibt keinen Feed zum Aktualisieren…',
+ 'not_added' => '<em>%s</em> konnte nicht hinzugefügt werden',
+ 'over_max' => 'Sie haben Ihr Feeds-Limit erreicht (%d)',
+ 'updated' => 'Feed ist aktualisiert worden',
+ ),
+ 'purge_completed' => 'Bereinigung abgeschlossen (%d Artikel gelöscht)',
+ ),
+ 'update' => array(
+ 'can_apply' => 'FreshRSS wird nun auf die <strong>Version %s</strong> aktualisiert.',
+ 'error' => 'Der Aktualisierungsvorgang stieß auf einen Fehler: %s',
+ 'file_is_nok' => 'Überprüfen Sie die Berechtigungen des Verzeichnisses <em>%s</em>. Der HTTP-Server muss Schreibrechte besitzen',
+ 'finished' => 'Aktualisierung abgeschlossen!',
+ 'none' => 'Keine Aktualisierung zum Anwenden',
+ 'server_not_found' => 'Aktualisierungs-Server kann nicht gefunden werden. [%s]',
+ ),
+ 'user' => array(
+ 'created' => array(
+ '_' => 'Benutzer %s ist erstellt worden',
+ 'error' => 'Benutzer %s kann nicht erstellt werden',
+ ),
+ 'deleted' => array(
+ '_' => 'Benutzer %s ist gelöscht worden',
+ 'error' => 'Benutzer %s kann nicht gelöscht werden',
+ ),
+ ),
+ 'profile' => array(
+ 'error' => 'Ihr Profil kann nicht geändert werden',
+ 'updated' => 'Ihr Profil ist geändert worden',
+ ),
+);
diff --git a/app/i18n/de/gen.php b/app/i18n/de/gen.php
new file mode 100644
index 000000000..f3479ed53
--- /dev/null
+++ b/app/i18n/de/gen.php
@@ -0,0 +1,163 @@
+<?php
+
+return array(
+ 'action' => array(
+ 'actualize' => 'Aktualisieren',
+ 'back_to_rss_feeds' => '← Zurück zu Ihren RSS-Feeds gehen',
+ 'cancel' => 'Abbrechen',
+ 'create' => 'Erstellen',
+ 'disable' => 'Deaktivieren',
+ 'empty' => 'Leeren',
+ 'enable' => 'Aktivieren',
+ 'export' => 'Exportieren',
+ 'filter' => 'Filtern',
+ 'import' => 'Importieren',
+ 'manage' => 'Verwalten',
+ 'mark_read' => 'Als gelesen markieren',
+ 'mark_favorite' => 'Als Favorit markieren',
+ 'remove' => 'Entfernen',
+ 'see_website' => 'Webseite ansehen',
+ 'submit' => 'Abschicken',
+ 'truncate' => 'Alle Artikel löschen',
+ ),
+ 'auth' => array(
+ 'keep_logged_in' => 'Eingeloggt bleiben <small>(1 Monat)</small>',
+ 'login' => 'Anmelden',
+ 'login_persona' => 'Anmelden mit Persona',
+ 'login_persona_problem' => 'Verbindungsproblem mit Persona?',
+ 'logout' => 'Abmelden',
+ 'password' => 'Passwort',
+ 'reset' => 'Zurücksetzen der Authentifizierung',
+ 'username' => 'Nutzername',
+ 'username_admin' => 'Administrator-Nutzername',
+ 'will_reset' => 'Authentifikationssystem wird zurückgesetzt: ein Formular wird anstelle von Persona benutzt.',
+ ),
+ 'date' => array(
+ 'Apr' => '\\A\\p\\r\\i\\l',
+ 'Aug' => '\\A\\u\\g\\u\\s\\t',
+ 'Dec' => '\\D\\e\\z\\e\\m\\b\\e\\r',
+ 'Feb' => '\\F\\e\\b\\r\\u\\a\\r',
+ 'Jan' => '\\J\\a\\n\\u\\a\\r',
+ 'Jul' => '\\J\\u\\l\\i',
+ 'Jun' => '\\J\\u\\n\\i',
+ 'Mar' => '\\M\\ä\\r\\z',
+ 'May' => '\\M\\a\\i',
+ 'Nov' => '\\N\\o\\v\\e\\m\\b\\e\\r',
+ 'Oct' => '\\O\\k\\t\\o\\b\\e\\r',
+ 'Sep' => '\\S\\e\\p\\t\\e\\m\\b\\e\\r',
+ 'apr' => 'Apr',
+ 'april' => 'April',
+ 'aug' => 'Aug',
+ 'august' => 'August',
+ 'before_yesterday' => 'Vor gestern',
+ 'dec' => 'Dez',
+ 'december' => 'Dezember',
+ 'feb' => 'Feb',
+ 'february' => 'Februar',
+ 'format_date' => 'd\\. %s Y',
+ 'format_date_hour' => 'd\\. %s Y \\u\\m H\\:i',
+ 'fri' => 'Fr',
+ 'jan' => 'Jan',
+ 'january' => 'Januar',
+ 'jul' => 'Jul',
+ 'july' => 'Juli',
+ 'jun' => 'Jun',
+ 'june' => 'Juni',
+ 'last_3_month' => 'Letzte drei Monate',
+ 'last_6_month' => 'Letzte sechs Monate',
+ 'last_month' => 'Letzter Monat',
+ 'last_week' => 'Letzte Woche',
+ 'last_year' => 'Letztes Jahr',
+ 'mar' => 'Mär',
+ 'march' => 'März',
+ 'may' => 'Mai',
+ 'mon' => 'Mo',
+ 'month' => 'Monat(en)',
+ 'nov' => 'Nov',
+ 'november' => 'November',
+ 'oct' => 'Okt',
+ 'october' => 'Oktober',
+ 'sat' => 'Sa',
+ 'sep' => 'Sep',
+ 'september' => 'September',
+ 'sun' => 'So',
+ 'thu' => 'Do',
+ 'today' => 'Heute',
+ 'tue' => 'Di',
+ 'wed' => 'Mi',
+ 'yesterday' => 'Gestern',
+ ),
+ 'freshrss' => array(
+ '_' => 'FreshRSS',
+ 'about' => 'Über FreshRSS',
+ ),
+ 'js' => array(
+ 'category_empty' => 'Kategorie leeren',
+ 'confirm_action' => 'Sind Sie sicher, dass Sie diese Aktion durchführen wollen? Dies kann nicht abgebrochen werden!',
+ 'confirm_action_feed_cat' => 'Sind Sie sicher, dass Sie diese Aktion durchführen wollen? Sie werden zugehörige Favoriten und Benutzerabfragen verlieren. Dies kann nicht abgebrochen werden!',
+ 'feedback' => array(
+ 'body_new_articles' => 'Es gibt \\d neue Artikel zum Lesen auf FreshRSS.',
+ 'request_failed' => 'Eine Anfrage ist fehlgeschlagen, dies könnte durch Probleme mit der Internetverbindung verursacht worden sein.',
+ 'title_new_articles' => 'FreshRSS: neue Artikel!',
+ ),
+ 'new_article' => 'Es gibt neue verfügbare Artikel. Klicken Sie, um die Seite zu aktualisieren.',
+ 'should_be_activated' => 'JavaScript muss aktiviert sein',
+ ),
+ 'lang' => array(
+ 'de' => 'Deutsch',
+ 'en' => 'English',
+ 'fr' => 'Français',
+ ),
+ 'menu' => array(
+ 'about' => 'Über',
+ 'admin' => 'Administration',
+ 'archiving' => 'Archivierung',
+ 'authentication' => 'Authentifizierung',
+ 'check_install' => 'Installationsüberprüfung',
+ 'configuration' => 'Konfiguration',
+ 'display' => 'Anzeige',
+ 'extensions' => 'Erweiterungen',
+ 'logs' => 'Protokolle',
+ 'queries' => 'Benutzerabfragen',
+ 'reading' => 'Lesen',
+ 'search' => 'Suche Worte oder #Tags',
+ 'sharing' => 'Teilen',
+ 'shortcuts' => 'Tastaturkürzel',
+ 'stats' => 'Statistiken',
+ 'update' => 'Aktualisieren',
+ 'user_management' => 'Benutzer verwalten',
+ 'user_profile' => 'Profil',
+ ),
+ 'pagination' => array(
+ 'first' => 'Erste',
+ 'last' => 'Letzte',
+ 'load_more' => 'Weitere Artikel laden',
+ 'mark_all_read' => 'Alle als gelesen markieren',
+ 'next' => 'Nächste',
+ 'nothing_to_load' => 'Es gibt keine weiteren Artikel',
+ 'previous' => 'Vorherige',
+ ),
+ 'share' => array(
+ 'blogotext' => 'Blogotext',
+ 'diaspora' => 'Diaspora*',
+ 'email' => 'E-Mail',
+ 'facebook' => 'Facebook',
+ 'g+' => 'Google+',
+ 'print' => 'Drucken',
+ 'shaarli' => 'Shaarli',
+ 'twitter' => 'Twitter',
+ 'wallabag' => 'wallabag',
+ ),
+ 'short' => array(
+ 'attention' => 'Achtung!',
+ 'blank_to_disable' => 'Zum Deaktivieren frei lassen',
+ 'by_author' => 'Von <em>%s</em>',
+ 'by_default' => 'standardmäßig',
+ 'damn' => 'Verdammt!',
+ 'default_category' => 'Unkategorisiert',
+ 'no' => 'Nein',
+ 'ok' => 'OK!',
+ 'or' => 'oder',
+ 'yes' => 'Ja',
+ ),
+);
diff --git a/app/i18n/de/index.php b/app/i18n/de/index.php
new file mode 100644
index 000000000..3449de87d
--- /dev/null
+++ b/app/i18n/de/index.php
@@ -0,0 +1,61 @@
+<?php
+
+return array(
+ 'about' => array(
+ '_' => 'Über',
+ 'agpl3' => '<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL 3</a>',
+ 'bugs_reports' => 'Fehlerberichte',
+ 'credits' => 'Credits',
+ 'credits_content' => 'Einige Designelemente stammen 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> stammen vom <a href="https://www.gnome.org/">GNOME project</a>. <em>Open Sans</em> Font wurde von <a href="https://www.google.com/webfonts/specimen/Open+Sans">Steve Matteson</a> erstellt. Favicons werden mit <a href="https://getfavicon.appspot.com/">getFavicon API</a> gesammelt. FreshRSS basiert auf <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, einem PHP-Framework.',
+ '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>. Er ist leicht und einfach zu handhaben und gleichzeitig ein leistungsstarkes und konfigurierbares Werkzeug.',
+ 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">on Github</a>',
+ 'license' => 'Lizenz',
+ 'project_website' => 'Projekt-Webseite',
+ 'title' => 'Über',
+ 'version' => 'Version',
+ 'website' => 'Webseite',
+ ),
+ 'feed' => array(
+ 'add' => 'Sie können Feeds hinzufügen.',
+ 'empty' => 'Es gibt keinen Artikel zum Zeigen.',
+ 'rss_of' => 'RSS-Feed von %s',
+ 'title' => 'Ihre RSS-Feeds',
+ 'title_global' => 'Globale Ansicht',
+ 'title_fav' => 'Ihre Favoriten',
+ ),
+ 'log' => array(
+ '_' => 'Protokolle',
+ 'clear' => 'Protokolle leeren',
+ 'empty' => 'Protokolldatei ist leer.',
+ 'title' => 'Protokolle',
+ ),
+ 'menu' => array(
+ 'about' => 'Über FreshRSS',
+ 'add_query' => 'Eine Abfrage hinzufügen',
+ 'before_one_day' => 'Vor einem Tag',
+ 'before_one_week' => 'Vor einer Woche',
+ 'favorites' => 'Favoriten (%s)',
+ 'global_view' => 'Globale Ansicht',
+ 'main_stream' => 'Haupt-Feeds',
+ 'mark_all_read' => 'Alle als gelesen markieren',
+ 'mark_cat_read' => 'Kategorie als gelesen markieren',
+ 'mark_feed_read' => 'Feed als gelesen markieren',
+ 'newer_first' => 'Neuere zuerst',
+ 'non-starred' => 'Alle außer Favoriten zeigen',
+ 'normal_view' => 'Normale Ansicht',
+ 'older_first' => 'Ältere zuerst',
+ 'queries' => 'Benutzerabfragen',
+ 'read' => 'Nur gelesene zeigen',
+ 'reader_view' => 'Lese-Ansicht',
+ 'rss_view' => 'RSS-Feed',
+ 'search_short' => 'Suchen',
+ 'starred' => 'Nur Favoriten zeigen',
+ 'stats' => 'Statistiken',
+ 'subscription' => 'Abonnementverwaltung',
+ 'unread' => 'Nur ungelesene zeigen',
+ ),
+ 'share' => 'Teilen',
+ 'tag' => array(
+ 'related' => 'Verwandte Tags',
+ ),
+);
diff --git a/app/i18n/de/install.php b/app/i18n/de/install.php
new file mode 100644
index 000000000..e9267bbbd
--- /dev/null
+++ b/app/i18n/de/install.php
@@ -0,0 +1,107 @@
+<?php
+
+return array(
+ 'action' => array(
+ 'finish' => 'Installation fertigstellen',
+ 'fix_errors_before' => 'Fehler korrigieren, bevor zum nächsten Schritt gesprungen wird.',
+ 'next_step' => 'Zum nächsten Schritt gehen',
+ ),
+ 'auth' => array(
+ 'email_persona' => 'Anmelde-E-Mail-Adresse<br /><small>(für <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
+ 'form' => 'Webformular (traditionell, benötigt JavaScript)',
+ 'http' => 'HTTP (HTTPS für erfahrene Benutzer)',
+ 'none' => 'Keine (gefährlich)',
+ 'password_form' => 'Passwort<br /><small>(für die Anmeldemethode per Webformular)</small>',
+ 'password_format' => 'mindestens 7 Zeichen',
+ 'persona' => 'Mozilla Persona (modern, benötigt JavaScript)',
+ 'type' => 'Authentifizierungsmethode',
+ ),
+ 'bdd' => array(
+ '_' => 'Datenbank',
+ 'conf' => array(
+ '_' => 'Datenbank-Konfiguration',
+ 'ko' => 'Überprüfen Sie Ihre Datenbank-Information.',
+ 'ok' => 'Datenbank-Konfiguration ist gespeichert worden.',
+ ),
+ 'host' => 'Host',
+ 'prefix' => 'Tabellen-Präfix',
+ 'password' => 'HTTP-Password',
+ 'type' => 'Datenbank-Typ',
+ 'username' => 'HTTP-Nutzername',
+ ),
+ 'check' => array(
+ '_' => 'Überprüfungen',
+ 'cache' => array(
+ 'nok' => 'Überprüfen Sie die Berechtigungen des Verzeichnisses <em>./data/cache</em>. Der HTTP-Server muss Schreibrechte besitzen.',
+ 'ok' => 'Berechtigungen des Verzeichnisses <em>./data/cache</em> sind in Ordnung.',
+ ),
+ 'ctype' => array(
+ 'nok' => 'Ihnen fehlt eine benötigte Bibliothek für die Überprüfung von Zeichentypen (php-ctype).',
+ 'ok' => 'Sie haben die benötigte Bibliothek für die Überprüfung von Zeichentypen (ctype).',
+ ),
+ 'curl' => array(
+ 'nok' => 'Ihnen fehlt cURL (Paket php5-curl).',
+ 'ok' => 'Sie haben die cURL-Erweiterung.',
+ ),
+ 'data' => array(
+ 'nok' => 'Überprüfen Sie die Berechtigungen des Verzeichnisses <em>./data</em>. Der HTTP-Server muss Schreibrechte besitzen.',
+ 'ok' => 'Berechtigungen des Verzeichnisses <em>./data</em> sind in Ordnung.',
+ ),
+ 'dom' => array(
+ 'nok' => 'Ihnen fehlt eine benötigte Bibliothek um DOM zu durchstöbern (Paket php-xml).',
+ 'ok' => 'Sie haben die benötigte Bibliothek um DOM zu durchstöbern.',
+ ),
+ 'favicons' => array(
+ 'nok' => 'Überprüfen Sie die Berechtigungen des Verzeichnisses <em>./data/favicons</em>. Der HTTP-Server muss Schreibrechte besitzen.',
+ 'ok' => 'Berechtigungen des Verzeichnisses <em>./data/favicons</em> sind in Ordnung.',
+ ),
+ 'http_referer' => array(
+ 'nok' => 'Bitte stellen Sie sicher, dass Sie Ihren HTTP REFERER nicht abändern.',
+ 'ok' => 'Ihr HTTP REFERER ist bekannt und entspricht Ihrem Server.',
+ ),
+ 'minz' => array(
+ 'nok' => 'Ihnen fehlt das Minz-Framework.',
+ 'ok' => 'Sie haben das Minz-Framework.',
+ ),
+ 'pcre' => array(
+ 'nok' => 'Ihnen fehlt eine benötigte Bibliothek für reguläre Ausdrücke (php-pcre).',
+ 'ok' => 'Sie haben die benötigte Bibliothek für reguläre Ausdrücke (PCRE).',
+ ),
+ 'pdo' => array(
+ 'nok' => 'Ihnen fehlt PDO oder einer der unterstützten Treiber (pdo_mysql, pdo_sqlite).',
+ 'ok' => 'Sie haben PDO und mindestens einen der unterstützten Treiber (pdo_mysql, pdo_sqlite).',
+ ),
+ 'persona' => array(
+ 'nok' => 'Überprüfen Sie die Berechtigungen des Verzeichnisses <em>./data/persona</em>. Der HTTP-Server muss Schreibrechte besitzen.',
+ 'ok' => 'Berechtigungen des Verzeichnisses <em>./data/persona</em> sind in Ordnung.',
+ ),
+ 'php' => array(
+ 'nok' => 'Ihre PHP-Version ist %s aber FreshRSS benötigt mindestens Version %s.',
+ 'ok' => 'Ihre PHP-Version ist %s, welche kompatibel mit FreshRSS ist.',
+ ),
+ 'users' => array(
+ 'nok' => 'Überprüfen Sie die Berechtigungen des Verzeichnisses <em>./data/users</em>. Der HTTP-Server muss Schreibrechte besitzen.',
+ 'ok' => 'Berechtigungen des Verzeichnisses <em>./data/users</em> sind in Ordnung.',
+ ),
+ ),
+ 'conf' => array(
+ '_' => 'Allgemeine Konfiguration',
+ 'ok' => 'Allgemeine Konfiguration ist gespeichert worden.',
+ ),
+ 'congratulations' => 'Glückwunsch!',
+ 'default_user' => 'Nutzername des Standardbenutzers <small>(maximal 16 alphanumerische Zeichen)</small>',
+ 'delete_articles_after' => 'Entferne Artikel nach',
+ 'fix_errors_before' => 'Fehler korrigieren, bevor zum nächsten Schritt gesprungen wird.',
+ 'javascript_is_better' => 'FreshRSS ist ansprechender mit aktiviertem JavaScript',
+ 'language' => array(
+ '_' => 'Sprache',
+ 'choose' => 'Wählen Sie eine Sprache für FreshRSS',
+ 'defined' => 'Sprache ist festgelegt worden.',
+ ),
+ 'not_deleted' => 'Etwas ist schiefgelaufen; Sie müssen die Datei <em>%s</em> manuell löschen.',
+ 'ok' => 'Der Installationsvorgang war erfolgreich.',
+ 'step' => 'Schritt %d',
+ 'steps' => 'Schritte',
+ 'title' => 'Installation · FreshRSS',
+ 'this_is_the_end' => 'Das ist das Ende',
+);
diff --git a/app/i18n/de/sub.php b/app/i18n/de/sub.php
new file mode 100644
index 000000000..0479b8f46
--- /dev/null
+++ b/app/i18n/de/sub.php
@@ -0,0 +1,61 @@
+<?php
+
+return array(
+ 'category' => array(
+ '_' => 'Kategorie',
+ 'add' => 'Eine Kategorie hinzufügen',
+ 'empty' => 'Leere Kategorie',
+ 'new' => 'Neue Kategorie',
+ ),
+ 'feed' => array(
+ 'add' => 'Einen RSS-Feed hinzufügen',
+ 'advanced' => 'Erweitert',
+ 'archiving' => 'Archivierung',
+ 'auth' => array(
+ 'configuration' => 'Anmelden',
+ 'help' => 'Die Verbindung erlaubt Zugriff auf HTTP-geschützte RSS-Feeds',
+ 'http' => 'HTTP-Authentifizierung',
+ 'password' => 'HTTP-Passwort',
+ 'username' => 'HTTP-Nutzername',
+ ),
+ 'css_help' => 'Ruft gekürzte RSS-Feeds ab (Achtung, benötigt mehr Zeit!)',
+ 'css_path' => 'Pfad zur CSS-Datei des Artikels auf der Original-Webseite',
+ 'description' => 'Beschreibung',
+ 'empty' => 'Dieser Feed ist leer. Bitte stellen Sie sicher, dass er noch gepflegt wird.',
+ 'error' => 'Dieser Feed ist auf ein Problem gestoßen. Bitte stellen Sie sicher, dass er immer lesbar ist und aktualisieren Sie ihn dann.',
+ 'in_main_stream' => 'In Haupt-Feeds zeigen',
+ 'informations' => 'Information',
+ 'keep_history' => 'Minimale Anzahl an Artikeln, die behalten wird',
+ 'moved_category_deleted' => 'Wenn Sie eine Kategorie entfernen, werden deren Feeds automatisch in die Kategorie <em>%s</em> eingefügt.',
+ 'no_selected' => 'Kein Feed ausgewählt.',
+ 'number_entries' => '%d Artikel',
+ 'stats' => 'Statistiken',
+ 'think_to_add' => 'Sie können Feeds hinzufügen.',
+ 'title' => 'Titel',
+ 'title_add' => 'Einen RSS-Feed hinzufügen',
+ 'ttl' => 'Aktualisiere automatisch nicht öfter als',
+ 'url' => 'Feed-URL',
+ 'validator' => 'Überprüfen Sie die Gültigkeit des Feeds',
+ 'website' => 'Webseiten-URL',
+ ),
+ 'import_export' => array(
+ 'export' => 'Exportieren',
+ 'export_opml' => 'Liste der Feeds exportieren (OPML)',
+ 'export_starred' => 'Ihre Favoriten exportieren',
+ 'feed_list' => 'Liste von %s Artikeln',
+ 'file_to_import' => 'Zu importierende Datei<br />(OPML, JSON oder Zip)',
+ 'file_to_import_no_zip' => 'Zu importierende Datei<br />(OPML oder JSON)',
+ 'import' => 'Importieren',
+ 'starred_list' => 'Liste der Lieblingsartikel',
+ 'title' => 'Importieren / Exportieren',
+ ),
+ 'menu' => array(
+ 'bookmark' => 'Abonnieren (FreshRSS-Lesezeichen)',
+ 'import_export' => 'Importieren / Exportieren',
+ 'subscription_management' => 'Abonnementverwaltung',
+ ),
+ 'title' => array(
+ '_' => 'Abonnementverwaltung',
+ 'feed_management' => 'Verwaltung der RSS-Feeds',
+ ),
+);
diff --git a/app/i18n/en/admin.php b/app/i18n/en/admin.php
index 947d0e6c7..d2fcd3e82 100644
--- a/app/i18n/en/admin.php
+++ b/app/i18n/en/admin.php
@@ -161,6 +161,7 @@ return array(
'email_persona' => 'Login mail address<br /><small>(for <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
'language' => 'Language',
'password_form' => 'Password<br /><small>(for the Web-form login method)</small>',
+ 'password_format' => 'At least 7 characters',
'title' => 'Manage users',
'user_list' => 'List of users',
'username' => 'Username',
diff --git a/app/i18n/en/conf.php b/app/i18n/en/conf.php
index 13efb650b..308c45d2c 100644
--- a/app/i18n/en/conf.php
+++ b/app/i18n/en/conf.php
@@ -75,6 +75,7 @@ return array(
'email_persona' => 'Login mail address<br /><small>(for <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
'password_api' => 'Password API<br /><small>(e.g., for mobile apps)</small>',
'password_form' => 'Password<br /><small>(for the Web-form login method)</small>',
+ 'password_format' => 'At least 7 characters',
'title' => 'Profile',
),
'reading' => array(
diff --git a/app/i18n/en/gen.php b/app/i18n/en/gen.php
index 6271717d4..2143822ed 100644
--- a/app/i18n/en/gen.php
+++ b/app/i18n/en/gen.php
@@ -95,12 +95,16 @@ return array(
'category_empty' => 'Empty category',
'confirm_action' => 'Are you sure you want to perform this action? It cannot be cancelled!',
'confirm_action_feed_cat' => 'Are you sure you want to perform this action? You will lose related favorites and user queries. It cannot be cancelled!',
+ 'feedback' => array(
+ 'body_new_articles' => 'There are \\d new articles to read on FreshRSS.',
+ 'request_failed' => 'A request has failed, it may have been caused by Internet connection problems.',
+ 'title_new_articles' => 'FreshRSS: new articles!',
+ ),
'new_article' => 'There are new available articles, click to refresh the page.',
- 'notif_body_new_articles' => 'There are \\d new articles to read on FreshRSS.',
- 'notif_title_new_articles' => 'FreshRSS: new articles!',
'should_be_activated' => 'JavaScript must be enabled',
),
'lang' => array(
+ 'de' => 'Deutsch',
'en' => 'English',
'fr' => 'Français',
),
diff --git a/app/i18n/en/index.php b/app/i18n/en/index.php
index 8e7d81db8..80fa3d950 100644
--- a/app/i18n/en/index.php
+++ b/app/i18n/en/index.php
@@ -8,8 +8,7 @@ return array(
'credits' => 'Credits',
'credits_content' => 'Some design elements come from <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> although FreshRSS doesn’t use this framework. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Icons</a> come from <a href="https://www.gnome.org/">GNOME project</a>. <em>Open Sans</em> font police has been created by <a href="https://www.google.com/webfonts/specimen/Open+Sans">Steve Matteson</a>. Favicons are collected with <a href="https://getfavicon.appspot.com/">getFavicon API</a>. FreshRSS is based on <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, a PHP framework.',
'freshrss_description' => 'FreshRSS is a RSS feeds aggregator to self-host like <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> or <a href="http://projet.idleman.fr/leed/">Leed</a>. It is light and easy to take in hand while being powerful and configurable tool.',
- 'github_or_email' => '<a href="https://github.com/marienfressinaud/FreshRSS/issues">on Github</a> or <a href="mailto:dev@marienfressinaud.fr">by mail</a>',
- 'lead_developer' => 'Lead developer',
+ 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">on Github</a>',
'license' => 'License',
'project_website' => 'Project website',
'title' => 'About',
diff --git a/app/i18n/en/install.php b/app/i18n/en/install.php
index e8073e8b6..2bc6bd38f 100644
--- a/app/i18n/en/install.php
+++ b/app/i18n/en/install.php
@@ -12,6 +12,7 @@ return array(
'http' => 'HTTP (for advanced users with HTTPS)',
'none' => 'None (dangerous)',
'password_form' => 'Password<br /><small>(for the Web-form login method)</small>',
+ 'password_format' => 'At least 7 characters',
'persona' => 'Mozilla Persona (modern, requires JavaScript)',
'type' => 'Authentication method',
),
diff --git a/app/i18n/fr/admin.php b/app/i18n/fr/admin.php
index 980f1fccf..b740bd0d2 100644
--- a/app/i18n/fr/admin.php
+++ b/app/i18n/fr/admin.php
@@ -161,6 +161,7 @@ return array(
'email_persona' => 'Adresse courriel de connexion<br /><small>(pour <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
'language' => 'Langue',
'password_form' => 'Mot de passe<br /><small>(pour connexion par formulaire)</small>',
+ 'password_format' => '7 caractères minimum',
'title' => 'Gestion des utilisateurs',
'user_list' => 'Liste des utilisateurs',
'username' => 'Nom d’utilisateur',
diff --git a/app/i18n/fr/conf.php b/app/i18n/fr/conf.php
index e91aeb66a..d38445b99 100644
--- a/app/i18n/fr/conf.php
+++ b/app/i18n/fr/conf.php
@@ -75,6 +75,7 @@ return array(
'email_persona' => 'Adresse courriel de connexion<br /><small>(pour <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
'password_api' => 'Mot de passe API<br /><small>(ex. : pour applis mobiles)</small>',
'password_form' => 'Mot de passe<br /><small>(pour connexion par formulaire)</small>',
+ 'password_format' => '7 caractères minimum',
'title' => 'Profil',
),
'reading' => array(
diff --git a/app/i18n/fr/gen.php b/app/i18n/fr/gen.php
index 6b449484f..1cfec6969 100644
--- a/app/i18n/fr/gen.php
+++ b/app/i18n/fr/gen.php
@@ -95,12 +95,16 @@ return array(
'category_empty' => 'Catégorie vide',
'confirm_action' => 'Êtes-vous sûr(e) de vouloir continuer ? Cette action ne peut être annulée !',
'confirm_action_feed_cat' => 'Êtes-vous sûr(e) de vouloir continuer ? Vous perdrez les favoris et les filtres associés. Cette action ne peut être annulée !',
+ 'feedback' => array(
+ 'body_new_articles' => 'Il y a \\d nouveaux articles à lire sur FreshRSS.',
+ 'request_failed' => 'Une requête a échoué, cela peut être dû à des problèmes de connexion à Internet.',
+ 'title_new_articles' => 'FreshRSS : nouveaux articles !',
+ ),
'new_article' => 'Il y a de nouveaux articles disponibles, cliquez pour rafraîchir la page.',
- 'notif_body_new_articles' => 'Il y a \\d nouveaux articles à lire sur FreshRSS.',
- 'notif_title_new_articles' => 'FreshRSS : nouveaux articles !',
'should_be_activated' => 'Le JavaScript doit être activé.',
),
'lang' => array(
+ 'de' => 'Deutsch',
'en' => 'English',
'fr' => 'Français',
),
diff --git a/app/i18n/fr/index.php b/app/i18n/fr/index.php
index f9975c593..7e028ab92 100644
--- a/app/i18n/fr/index.php
+++ b/app/i18n/fr/index.php
@@ -8,8 +8,7 @@ return array(
'credits' => 'Crédits',
'credits_content' => 'Des éléments de design sont issus du <a href="http://twitter.github.io/bootstrap/">projet Bootstrap</a> bien que FreshRSS n’utilise pas ce framework. Les <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">icônes</a> sont issues du <a href="https://www.gnome.org/">projet GNOME</a>. La police <em>Open Sans</em> utilisée a été créée par <a href="https://www.google.com/webfonts/specimen/Open+Sans">Steve Matteson</a>. Les favicons sont récupérés grâce au site <a href="https://getfavicon.appspot.com/">getFavicon</a>. FreshRSS repose sur <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, un framework PHP.',
'freshrss_description' => 'FreshRSS est un agrégateur de flux RSS à auto-héberger à l’image de <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> ou <a href="http://projet.idleman.fr/leed/">Leed</a>. Il se veut léger et facile à prendre en main tout en étant un outil puissant et paramétrable.',
- 'github_or_email' => '<a href="https://github.com/marienfressinaud/FreshRSS/issues">sur Github</a> ou <a href="mailto:dev@marienfressinaud.fr">par courriel</a>',
- 'lead_developer' => 'Développeur principal',
+ 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">sur Github</a>',
'license' => 'Licence',
'project_website' => 'Site du projet',
'title' => 'À propos',
diff --git a/app/i18n/fr/install.php b/app/i18n/fr/install.php
index d1b78ffb6..245a20c56 100644
--- a/app/i18n/fr/install.php
+++ b/app/i18n/fr/install.php
@@ -12,6 +12,7 @@ return array(
'http' => 'HTTP (pour utilisateurs avancés avec HTTPS)',
'none' => 'Aucune (dangereux)',
'password_form' => 'Mot de passe<br /><small>(pour connexion par formulaire)</small>',
+ 'password_format' => '7 caractères minimum',
'persona' => 'Mozilla Persona (moderne, requiert JavaScript)',
'type' => 'Méthode d’authentification',
),
diff --git a/app/install.php b/app/install.php
index eb2f764f9..177173fdb 100644
--- a/app/install.php
+++ b/app/install.php
@@ -598,6 +598,7 @@ function printStep2() {
<input type="password" id="passwordPlain" name="passwordPlain" pattern=".{7,}" autocomplete="off" <?php echo $auth_type === 'form' ? ' required="required"' : ''; ?> />
<a class="btn toggle-password" data-toggle="passwordPlain"><?php echo FreshRSS_Themes::icon('key'); ?></a>
</div>
+ <?php echo _i('help'); ?> <?php echo _t('install.auth.password_format'); ?>
<noscript><b><?php echo _t('gen.js.should_be_activated'); ?></b></noscript>
</div>
</div>
diff --git a/app/layout/aside_feed.phtml b/app/layout/aside_feed.phtml
index d2612e751..a6d22f878 100644
--- a/app/layout/aside_feed.phtml
+++ b/app/layout/aside_feed.phtml
@@ -74,20 +74,20 @@
<script id="feed_config_template" type="text/html">
<ul class="dropdown-menu">
<li class="dropdown-close"><a href="#close">❌</a></li>
- <li class="item"><a href="<?php echo _url('index', 'index', 'get', 'f_!!!!!!'); ?>"><?php echo _t('gen.action.filter'); ?></a></li>
+ <li class="item"><a href="<?php echo _url('index', 'index', 'get', 'f_------'); ?>"><?php echo _t('gen.action.filter'); ?></a></li>
<?php if (FreshRSS_Auth::hasAccess()) { ?>
- <li class="item"><a href="<?php echo _url('stats', 'repartition', 'id', '!!!!!!'); ?>"><?php echo _t('index.menu.stats'); ?></a></li>
+ <li class="item"><a href="<?php echo _url('stats', 'repartition', 'id', '------'); ?>"><?php echo _t('index.menu.stats'); ?></a></li>
<?php } ?>
<li class="item"><a target="_blank" href="http://example.net/"><?php echo _t('gen.action.see_website'); ?></a></li>
<?php if (FreshRSS_Auth::hasAccess()) { ?>
<li class="separator"></li>
- <li class="item"><a href="<?php echo _url('subscription', 'index', 'id', '!!!!!!'); ?>"><?php echo _t('gen.action.manage'); ?></a></li>
- <li class="item"><a href="<?php echo _url('feed', 'actualize', 'id', '!!!!!!'); ?>"><?php echo _t('gen.action.actualize'); ?></a></li>
+ <li class="item"><a href="<?php echo _url('subscription', 'index', 'id', '------'); ?>"><?php echo _t('gen.action.manage'); ?></a></li>
+ <li class="item"><a href="<?php echo _url('feed', 'actualize', 'id', '------'); ?>"><?php echo _t('gen.action.actualize'); ?></a></li>
<li class="item">
<?php $confirm = FreshRSS_Context::$user_conf->reading_confirm ? 'confirm' : ''; ?>
<button class="read_all as-link <?php echo $confirm; ?>"
form="mark-read-aside"
- formaction="<?php echo _url('entry', 'read', 'get', 'f_!!!!!!'); ?>"
+ formaction="<?php echo _url('entry', 'read', 'get', 'f_------'); ?>"
type="submit"><?php echo _t('gen.action.mark_read'); ?></button>
</li>
<?php } ?>
diff --git a/app/layout/header.phtml b/app/layout/header.phtml
index 2b968252b..41a63a565 100644
--- a/app/layout/header.phtml
+++ b/app/layout/header.phtml
@@ -25,8 +25,7 @@ if (FreshRSS_Auth::accessNeedsAction()) {
<?php if (FreshRSS_Auth::hasAccess() || FreshRSS_Context::$system_conf->allow_anonymous) { ?>
<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 _t('gen.menu.search'); ?>" />
+ <input type="search" name="search" id="search" class="extend" value="<?php echo FreshRSS_Context::$search; ?>" placeholder="<?php echo _t('gen.menu.search'); ?>" />
<?php $get = Minz_Request::param('get', ''); ?>
<?php if ($get != '') { ?>
diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml
index d35a0b5fb..3a755b560 100644
--- a/app/layout/nav_menu.phtml
+++ b/app/layout/nav_menu.phtml
@@ -156,8 +156,7 @@
<div class="item search">
<form action="<?php echo _url('index', 'index'); ?>" method="get">
- <?php $search = Minz_Request::param('search', ''); ?>
- <input type="search" name="search" class="extend" value="<?php echo $search; ?>" placeholder="<?php echo _t('index.menu.search_short'); ?>" />
+ <input type="search" name="search" class="extend" value="<?php echo FreshRSS_Context::$search; ?>" placeholder="<?php echo _t('index.menu.search_short'); ?>" />
<?php $get = Minz_Request::param('get', ''); ?>
<?php if($get != '') { ?>
diff --git a/app/views/configure/shortcut.phtml b/app/views/configure/shortcut.phtml
index fdbeed3ea..f68091af9 100644
--- a/app/views/configure/shortcut.phtml
+++ b/app/views/configure/shortcut.phtml
@@ -114,7 +114,7 @@
<div class="form-group">
<label class="group-name" for="close_dropdown_shortcut"><?php echo _t('conf.shortcut.close_dropdown'); ?></label>
<div class="group-controls">
- <input type="text" id="help_shortcut" name="shortcuts[close_dropdown]" list="keys" value="<?php echo $s['close_dropdown']; ?>" />
+ <input type="text" id="close_dropdown" name="shortcuts[close_dropdown]" list="keys" value="<?php echo $s['close_dropdown']; ?>" />
</div>
</div>
diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml
index fec3a6f7c..adf0783f3 100644
--- a/app/views/helpers/javascript_vars.phtml
+++ b/app/views/helpers/javascript_vars.phtml
@@ -35,20 +35,20 @@ echo 'var context={',
"},\n";
echo 'shortcuts={',
- 'mark_read:"', $s['mark_read'], '",',
- 'mark_favorite:"', $s['mark_favorite'], '",',
- 'go_website:"', $s['go_website'], '",',
- 'prev_entry:"', $s['prev_entry'], '",',
- 'next_entry:"', $s['next_entry'], '",',
- 'first_entry:"', $s['first_entry'], '",',
- 'last_entry:"', $s['last_entry'], '",',
- 'collapse_entry:"', $s['collapse_entry'], '",',
- 'load_more:"', $s['load_more'], '",',
- 'auto_share:"', $s['auto_share'], '",',
- 'focus_search:"', $s['focus_search'], '",',
- 'user_filter:"', $s['user_filter'], '",',
- 'help:"', $s['help'], '",',
- 'close_dropdown:"', $s['close_dropdown'], '"',
+ 'mark_read:"', @$s['mark_read'], '",',
+ 'mark_favorite:"', @$s['mark_favorite'], '",',
+ 'go_website:"', @$s['go_website'], '",',
+ 'prev_entry:"', @$s['prev_entry'], '",',
+ 'next_entry:"', @$s['next_entry'], '",',
+ 'first_entry:"', @$s['first_entry'], '",',
+ 'last_entry:"', @$s['last_entry'], '",',
+ 'collapse_entry:"', @$s['collapse_entry'], '",',
+ 'load_more:"', @$s['load_more'], '",',
+ 'auto_share:"', @$s['auto_share'], '",',
+ 'focus_search:"', @$s['focus_search'], '",',
+ 'user_filter:"', @$s['user_filter'], '",',
+ 'help:"', @$s['help'], '",',
+ 'close_dropdown:"', @$s['close_dropdown'], '"',
"},\n";
echo 'url={',
@@ -60,8 +60,9 @@ echo 'url={',
echo 'i18n={',
'confirmation_default:"', _t('gen.js.confirm_action'), '",',
- 'notif_title_articles:"', _t('gen.js.notif_title_new_articles'), '",',
- 'notif_body_articles:"', _t('gen.js.notif_body_new_articles'), '",',
+ 'notif_title_articles:"', _t('gen.js.feedback.title_new_articles'), '",',
+ 'notif_body_articles:"', _t('gen.js.feedback.body_new_articles'), '",',
+ 'notif_request_failed:"', _t('gen.js.feedback.request_failed'), '",',
'category_empty:"', _t('gen.js.category_empty'), '"',
"},\n";
diff --git a/app/views/index/about.phtml b/app/views/index/about.phtml
index ff2c538a2..3fdb5160d 100644
--- a/app/views/index/about.phtml
+++ b/app/views/index/about.phtml
@@ -7,11 +7,8 @@
<dt><?php echo _t('index.about.project_website'); ?></dt>
<dd><a href="<?php echo FRESHRSS_WEBSITE; ?>"><?php echo FRESHRSS_WEBSITE; ?></a></dd>
- <dt><?php echo _t('index.about.lead_developer'); ?></dt>
- <dd><a href="mailto:contact@marienfressinaud.fr">Marien Fressinaud</a> — <a href="http://marienfressinaud.fr"><?php echo _t('index.about.website'); ?></a></dd>
-
<dt><?php echo _t('index.about.bugs_reports'); ?></dt>
- <dd><?php echo _t('index.about.github_or_email'); ?></dd>
+ <dd><?php echo _t('index.about.github'); ?></dd>
<dt><?php echo _t('index.about.license'); ?></dt>
<dd><?php echo _t('index.about.agpl3'); ?></dd>
diff --git a/app/views/index/global.phtml b/app/views/index/global.phtml
index cf95bd0f5..0ffa3bc54 100644
--- a/app/views/index/global.phtml
+++ b/app/views/index/global.phtml
@@ -13,7 +13,7 @@
<?php
$url_base = array(
'c' => 'index',
- 'a' => 'index',
+ 'a' => 'normal',
'params' => Minz_Request::params()
);
diff --git a/app/views/subscription/index.phtml b/app/views/subscription/index.phtml
index b9e40988c..331e8244e 100644
--- a/app/views/subscription/index.phtml
+++ b/app/views/subscription/index.phtml
@@ -7,7 +7,7 @@
<form id="add_rss" method="post" action="<?php echo _url('feed', 'add'); ?>" autocomplete="off">
<div class="stick">
- <input type="url" name="url_rss" class="extend" placeholder="<?php echo _t('sub.feed.add'); ?>" />
+ <input type="url" name="url_rss" class="long" placeholder="<?php echo _t('sub.feed.add'); ?>" />
<div class="dropdown">
<div id="dropdown-cat" class="dropdown-target"></div>
diff --git a/app/views/user/manage.phtml b/app/views/user/manage.phtml
index 11562093e..fe1b6618b 100644
--- a/app/views/user/manage.phtml
+++ b/app/views/user/manage.phtml
@@ -32,6 +32,7 @@
<input type="password" id="new_user_passwordPlain" name="new_user_passwordPlain" autocomplete="off" pattern=".{7,}" />
<a class="btn toggle-password" data-toggle="new_user_passwordPlain"><?php echo _i('key'); ?></a>
</div>
+ <?php echo _i('help'); ?> <?php echo _t('admin.user.password_format'); ?>
<noscript><b><?php echo _t('gen.js.should_be_activated'); ?></b></noscript>
</div>
</div>
diff --git a/app/views/user/profile.phtml b/app/views/user/profile.phtml
index 4e61664bc..c44202edd 100644
--- a/app/views/user/profile.phtml
+++ b/app/views/user/profile.phtml
@@ -24,6 +24,7 @@
<input type="password" id="passwordPlain" name="passwordPlain" autocomplete="off" pattern=".{7,}" <?php echo cryptAvailable() ? '' : 'disabled="disabled" '; ?>/>
<a class="btn toggle-password" data-toggle="passwordPlain"><?php echo _i('key'); ?></a>
</div>
+ <?php echo _i('help'); ?> <?php echo _t('conf.profile.password_format'); ?>
<noscript><b><?php echo _t('gen.js.should_be_activated'); ?></b></noscript>
</div>
</div>
diff --git a/data/shares.php b/data/shares.php
index 44176f1bf..6e0e9ea0c 100644
--- a/data/shares.php
+++ b/data/shares.php
@@ -19,7 +19,7 @@
return array(
'shaarli' => array(
'url' => '~URL~?post=~LINK~&amp;title=~TITLE~&amp;source=FreshRSS',
- 'transform' => array('urlencode'),
+ 'transform' => array('rawurlencode'),
'help' => 'http://sebsauvage.net/wiki/doku.php?id=php:shaarli',
'form' => 'advanced',
),
@@ -40,31 +40,28 @@ return array(
),
'diaspora' => array(
'url' => '~URL~/bookmarklet?url=~LINK~&amp;title=~TITLE~',
- 'transform' => array('urlencode'),
+ 'transform' => array('rawurlencode'),
'help' => 'https://diasporafoundation.org/',
'form' => 'advanced',
),
'twitter' => array(
'url' => 'https://twitter.com/share?url=~LINK~&amp;text=~TITLE~',
- 'transform' => array('urlencode'),
+ 'transform' => array('rawurlencode'),
'form' => 'simple',
),
'g+' => array(
'url' => 'https://plus.google.com/share?url=~LINK~',
- 'transform' => array('urlencode'),
+ 'transform' => array('rawurlencode'),
'form' => 'simple',
),
'facebook' => array(
'url' => 'https://www.facebook.com/sharer.php?u=~LINK~&amp;t=~TITLE~',
- 'transform' => array('urlencode'),
+ 'transform' => array('rawurlencode'),
'form' => 'simple',
),
'email' => array(
'url' => 'mailto:?subject=~TITLE~&amp;body=~LINK~',
- 'transform' => array(
- 'link' => array('urlencode'),
- 'title' => array(),
- ),
+ 'transform' => array('rawurlencode'),
'form' => 'simple',
),
'print' => array(
diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php
index fe415e22b..ab5bb4fc2 100644
--- a/lib/Minz/Configuration.php
+++ b/lib/Minz/Configuration.php
@@ -16,16 +16,9 @@ class Minz_Configuration {
* @param $config_filename the filename of the configuration
* @param $default_filename a filename containing default values for the configuration
* @param $configuration_setter an optional helper to set values in configuration
- * @throws Minz_ConfigurationNamespaceException if the namespace already exists.
*/
public static function register($namespace, $config_filename, $default_filename = null,
$configuration_setter = null) {
- if (isset(self::$config_list[$namespace])) {
- throw new Minz_ConfigurationNamespaceException(
- $namespace . ' namespace already exists'
- );
- }
-
self::$config_list[$namespace] = new Minz_Configuration(
$namespace, $config_filename, $default_filename, $configuration_setter
);
diff --git a/lib/Minz/Error.php b/lib/Minz/Error.php
index 3eadc6d98..3e4a3e8f3 100644
--- a/lib/Minz/Error.php
+++ b/lib/Minz/Error.php
@@ -23,42 +23,13 @@ class Minz_Error {
$logs = self::processLogs ($logs);
$error_filename = APP_PATH . '/Controllers/errorController.php';
- switch ($code) {
- case 200 :
- header('HTTP/1.1 200 OK');
- break;
- case 403 :
- header('HTTP/1.1 403 Forbidden');
- break;
- case 404 :
- header('HTTP/1.1 404 Not Found');
- break;
- case 500 :
- header('HTTP/1.1 500 Internal Server Error');
- break;
- case 503 :
- header('HTTP/1.1 503 Service Unavailable');
- break;
- default :
- header('HTTP/1.1 500 Internal Server Error');
- }
-
if (file_exists ($error_filename)) {
- $params = array (
- 'code' => $code,
- 'logs' => $logs
- );
+ Minz_Session::_param('error_code', $code);
+ Minz_Session::_param('error_logs', $logs);
- if ($redirect) {
- Minz_Request::forward (array (
- 'c' => 'error'
- ), true);
- } else {
- Minz_Request::forward (array (
- 'c' => 'error',
- 'params' => $params
- ), false);
- }
+ Minz_Request::forward (array (
+ 'c' => 'error'
+ ), $redirect);
} else {
echo '<h1>An error occured</h1>' . "\n";
diff --git a/lib/Minz/ExtensionManager.php b/lib/Minz/ExtensionManager.php
index 7edc7afaa..c5c68a8d4 100644
--- a/lib/Minz/ExtensionManager.php
+++ b/lib/Minz/ExtensionManager.php
@@ -56,6 +56,9 @@ class Minz_ExtensionManager {
foreach ($list_potential_extensions as $ext_dir) {
$ext_pathname = EXTENSIONS_PATH . '/' . $ext_dir;
+ if (!is_dir($ext_pathname)) {
+ continue;
+ }
$metadata_filename = $ext_pathname . '/' . self::$ext_metaname;
// Try to load metadata file.
@@ -111,7 +114,7 @@ class Minz_ExtensionManager {
$entry_point_filename = $info['path'] . '/' . self::$ext_entry_point;
$ext_class_name = $info['entrypoint'] . 'Extension';
- include($entry_point_filename);
+ include_once($entry_point_filename);
// Test if the given extension class exists.
if (!class_exists($ext_class_name)) {
diff --git a/lib/Minz/Session.php b/lib/Minz/Session.php
index cfe8debe9..058685ada 100644
--- a/lib/Minz/Session.php
+++ b/lib/Minz/Session.php
@@ -65,7 +65,9 @@ class Minz_Session {
* @param $l la durée de vie
*/
public static function keepCookie($l) {
- $cookie_dir = empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI'];
+ // Get the script_name (e.g. /p/i/index.php) and keep only the path.
+ $cookie_dir = empty($_SERVER['SCRIPT_NAME']) ? '' : $_SERVER['SCRIPT_NAME'];
+ $cookie_dir = dirname($cookie_dir);
session_set_cookie_params($l, $cookie_dir, '', false, true);
}
diff --git a/lib/Minz/Url.php b/lib/Minz/Url.php
index e9f9a69ba..af555a277 100644
--- a/lib/Minz/Url.php
+++ b/lib/Minz/Url.php
@@ -45,45 +45,45 @@ class Minz_Url {
return $url_string;
}
-
+
/**
* Construit l'URI d'une URL
* @param l'url sous forme de tableau
* @param $encodage pour indiquer comment encoder les & (& ou &amp; pour html)
* @return l'uri sous la forme ?key=value&key2=value2
*/
- private static function printUri ($url, $encodage) {
+ private static function printUri($url, $encodage) {
$uri = '';
$separator = '?';
-
- if($encodage == 'html') {
+
+ if ($encodage === 'html') {
$and = '&amp;';
} else {
$and = '&';
}
-
- if (isset ($url['c'])
- && $url['c'] != Minz_Request::defaultControllerName ()) {
+
+ if (isset($url['c'])
+ && $url['c'] != Minz_Request::defaultControllerName()) {
$uri .= $separator . 'c=' . $url['c'];
$separator = $and;
}
-
- if (isset ($url['a'])
- && $url['a'] != Minz_Request::defaultActionName ()) {
+
+ if (isset($url['a'])
+ && $url['a'] != Minz_Request::defaultActionName()) {
$uri .= $separator . 'a=' . $url['a'];
$separator = $and;
}
-
- if (isset ($url['params'])) {
+
+ if (isset($url['params'])) {
foreach ($url['params'] as $key => $param) {
- $uri .= $separator . $key . '=' . $param;
+ $uri .= $separator . urlencode($key) . '=' . urlencode($param);
$separator = $and;
}
}
-
+
return $uri;
}
-
+
/**
* Vérifie que les éléments du tableau représentant une url soit ok
* @param l'url sous forme de tableau (sinon renverra directement $url)
@@ -91,7 +91,7 @@ class Minz_Url {
*/
public static function checkUrl ($url) {
$url_checked = $url;
-
+
if (is_array ($url)) {
if (!isset ($url['c'])) {
$url_checked['c'] = Minz_Request::defaultControllerName ();
@@ -103,7 +103,7 @@ class Minz_Url {
$url_checked['params'] = array ();
}
}
-
+
return $url_checked;
}
}
diff --git a/lib/SimplePie/SimplePie.php b/lib/SimplePie/SimplePie.php
index dc4bbb6cb..c4872b5be 100644
--- a/lib/SimplePie/SimplePie.php
+++ b/lib/SimplePie/SimplePie.php
@@ -1529,11 +1529,11 @@ class SimplePie
{ //FreshRSS
$md5 = $this->cleanMd5($file->body);
if ($this->data['md5'] === $md5) {
- syslog(LOG_DEBUG, 'SimplePie MD5 cache match for ' . $this->feed_url);
+ // syslog(LOG_DEBUG, 'SimplePie MD5 cache match for ' . $this->feed_url);
$cache->touch();
return true; //Content unchanged even though server did not send a 304
} else {
- syslog(LOG_DEBUG, 'SimplePie MD5 cache no match for ' . $this->feed_url);
+ // syslog(LOG_DEBUG, 'SimplePie MD5 cache no match for ' . $this->feed_url);
$this->data['md5'] = $md5;
}
}
diff --git a/lib/SimplePie/SimplePie/File.php b/lib/SimplePie/SimplePie/File.php
index b1bbe4420..9625af2a9 100644
--- a/lib/SimplePie/SimplePie/File.php
+++ b/lib/SimplePie/SimplePie/File.php
@@ -79,7 +79,7 @@ class SimplePie_File
$this->useragent = $useragent;
if (preg_match('/^http(s)?:\/\//i', $url))
{
- syslog(LOG_INFO, 'SimplePie GET ' . $url); //FreshRSS
+ // syslog(LOG_INFO, 'SimplePie GET ' . $url); //FreshRSS
if ($useragent === null)
{
$useragent = ini_get('user_agent');
diff --git a/lib/lib_opml.php b/lib/lib_opml.php
index f320335bb..02ae5f55c 100644
--- a/lib/lib_opml.php
+++ b/lib/lib_opml.php
@@ -1,11 +1,19 @@
<?php
-/* *
+/**
* lib_opml is a free library to manage OPML format in PHP.
- * It takes in consideration only version 2.0 (http://dev.opml.org/spec2.html).
- * Basically it means "text" attribute for outline elements is required.
*
- * lib_opml requires SimpleXML (http://php.net/manual/en/book.simplexml.php)
+ * By default, it takes in consideration version 2.0 but can be compatible with
+ * OPML 1.0. More information on http://dev.opml.org.
+ * Difference is "text" attribute is optional in version 1.0. It is highly
+ * recommended to use this attribute.
+ *
+ * lib_opml requires SimpleXML (php.net/simplexml) and DOMDocument (php.net/domdocument)
+ *
+ * @author Marien Fressinaud <dev@marienfressinaud.fr>
+ * @link https://github.com/marienfressinaud/lib_opml
+ * @version 0.2
+ * @license public domain
*
* Usages:
* > include('lib_opml.php');
@@ -23,21 +31,44 @@
* > echo $opml_string;
* > print_r($opml_object);
*
+ * You can set $strict argument to false if you want to bypass "text" attribute
+ * requirement.
+ *
* If parsing fails for any reason (e.g. not an XML string, does not match with
* the specifications), a LibOPML_Exception is raised.
*
- * Author: Marien Fressinaud <dev@marienfressinaud.fr>
- * Url: https://github.com/marienfressinaud/lib_opml
- * Version: 0.1
- * Date: 2014-03-29
- * License: public domain
+ * lib_opml array format is described here:
+ * $array = array(
+ * 'head' => array( // 'head' element is optional (but recommended)
+ * 'key' => 'value', // key must be a part of available OPML head elements
+ * ),
+ * 'body' => array( // body is required
+ * array( // this array represents an outline (at least one)
+ * 'text' => 'value', // 'text' element is required if $strict is true
+ * 'key' => 'value', // key and value are what you want (optional)
+ * '@outlines' = array( // @outlines is a special value and represents sub-outlines
+ * array(
+ * [...] // where [...] is a valid outline definition
+ * ),
+ * ),
+ * ),
+ * array( // other outline definitions
+ * [...]
+ * ),
+ * [...],
+ * )
+ * )
*
- * */
+ */
+/**
+ * A simple Exception class which represents any kind of OPML problem.
+ * Message should precise the current problem.
+ */
class LibOPML_Exception extends Exception {}
-// These elements are optional
+// Define the list of available head attributes. All of them are optional.
define('HEAD_ELEMENTS', serialize(array(
'title', 'dateCreated', 'dateModified', 'ownerName', 'ownerEmail',
'ownerId', 'docs', 'expansionState', 'vertScrollState', 'windowTop',
@@ -45,7 +76,16 @@ define('HEAD_ELEMENTS', serialize(array(
)));
-function libopml_parse_outline($outline_xml) {
+/**
+ * Parse an XML object as an outline object and return corresponding array
+ *
+ * @param SimpleXMLElement $outline_xml the XML object we want to parse
+ * @param bool $strict true if "text" attribute is required, false else
+ * @return array corresponding to an outline and following format described above
+ * @throws LibOPML_Exception
+ * @access private
+ */
+function libopml_parse_outline($outline_xml, $strict = true) {
$outline = array();
// An outline may contain any kind of attributes but "text" attribute is
@@ -59,7 +99,7 @@ function libopml_parse_outline($outline_xml) {
}
}
- if (!$text_is_present) {
+ if (!$text_is_present && $strict) {
throw new LibOPML_Exception(
'Outline does not contain any text attribute'
);
@@ -68,7 +108,7 @@ function libopml_parse_outline($outline_xml) {
foreach ($outline_xml->children() as $key => $value) {
// An outline may contain any number of outline children
if ($key === 'outline') {
- $outline['@outlines'][] = libopml_parse_outline($value);
+ $outline['@outlines'][] = libopml_parse_outline($value, $strict);
} else {
throw new LibOPML_Exception(
'Body can contain only outline elements'
@@ -80,7 +120,16 @@ function libopml_parse_outline($outline_xml) {
}
-function libopml_parse_string($xml) {
+/**
+ * Parse a string as a XML one and returns the corresponding array
+ *
+ * @param string $xml is the string we want to parse
+ * @param bool $strict true if "text" attribute is required, false else
+ * @return array corresponding to the XML string and following format described above
+ * @throws LibOPML_Exception
+ * @access public
+ */
+function libopml_parse_string($xml, $strict = true) {
$dom = new DOMDocument();
$dom->recover = true;
$dom->strictErrorChecking = false;
@@ -101,7 +150,6 @@ function libopml_parse_string($xml) {
// First, we get all "head" elements. Head is required but its sub-elements
// are optional.
- // TODO: test head exists!
foreach ($opml->head->children() as $key => $value) {
if (in_array($key, unserialize(HEAD_ELEMENTS), true)) {
$array['head'][$key] = (string)$value;
@@ -115,11 +163,10 @@ function libopml_parse_string($xml) {
// Then, we get body oulines. Body must contain at least one outline
// element.
$at_least_one_outline = false;
- // TODO: test body exists!
foreach ($opml->body->children() as $key => $value) {
if ($key === 'outline') {
$at_least_one_outline = true;
- $array['body'][] = libopml_parse_outline($value);
+ $array['body'][] = libopml_parse_outline($value, $strict);
} else {
throw new LibOPML_Exception(
'Body can contain only outline elements'
@@ -137,7 +184,16 @@ function libopml_parse_string($xml) {
}
-function libopml_parse_file($filename) {
+/**
+ * Parse a string contained into a file as a XML string and returns the corresponding array
+ *
+ * @param string $filename should indicates a valid XML file
+ * @param bool $strict true if "text" attribute is required, false else
+ * @return array corresponding to the file content and following format described above
+ * @throws LibOPML_Exception
+ * @access public
+ */
+function libopml_parse_file($filename, $strict = true) {
$file_content = file_get_contents($filename);
if ($file_content === false) {
@@ -146,11 +202,20 @@ function libopml_parse_file($filename) {
);
}
- return libopml_parse_string($file_content);
+ return libopml_parse_string($file_content, $strict);
}
-function libopml_render_outline($parent_elt, $outline) {
+/**
+ * Create a XML outline object in a parent object.
+ *
+ * @param SimpleXMLElement $parent_elt is the parent object of current outline
+ * @param array $outline array representing an outline object
+ * @param bool $strict true if "text" attribute is required, false else
+ * @throws LibOPML_Exception
+ * @access private
+ */
+function libopml_render_outline($parent_elt, $outline, $strict) {
// Outline MUST be an array!
if (!is_array($outline)) {
throw new LibOPML_Exception(
@@ -165,7 +230,7 @@ function libopml_render_outline($parent_elt, $outline) {
// outline elements.
if ($key === '@outlines' && is_array($value)) {
foreach ($value as $outline_child) {
- libopml_render_outline($outline_elt, $outline_child);
+ libopml_render_outline($outline_elt, $outline_child, $strict);
}
} elseif (is_array($value)) {
throw new LibOPML_Exception(
@@ -181,7 +246,7 @@ function libopml_render_outline($parent_elt, $outline) {
}
}
- if (!$text_is_present) {
+ if (!$text_is_present && $strict) {
throw new LibOPML_Exception(
'You must define at least a text element for all outlines'
);
@@ -189,8 +254,19 @@ function libopml_render_outline($parent_elt, $outline) {
}
-function libopml_render($array, $as_xml_object = false) {
- $opml = new SimpleXMLElement('<opml version="2.0"></opml>');
+/**
+ * Render an array as an OPML string or a XML object.
+ *
+ * @param array $array is the array we want to render and must follow structure defined above
+ * @param bool $as_xml_object false if function must return a string, true for a XML object
+ * @param bool $strict true if "text" attribute is required, false else
+ * @return string|SimpleXMLElement XML string corresponding to $array or XML object
+ * @throws LibOPML_Exception
+ * @access public
+ */
+function libopml_render($array, $as_xml_object = false, $strict = true) {
+ $opml = new SimpleXMLElement('<opml></opml>');
+ $opml->addAttribute('version', $strict ? '2.0' : '1.0');
// Create head element. $array['head'] is optional but head element will
// exist in the final XML object.
@@ -218,7 +294,7 @@ function libopml_render($array, $as_xml_object = false) {
// Create outline elements
$body = $opml->addChild('body');
foreach ($array['body'] as $outline) {
- libopml_render_outline($body, $outline);
+ libopml_render_outline($body, $outline, $strict);
}
// And return the final result
diff --git a/lib/lib_rss.php b/lib/lib_rss.php
index 083e87745..e5fe73041 100644
--- a/lib/lib_rss.php
+++ b/lib/lib_rss.php
@@ -180,7 +180,7 @@ function sanitizeHTML($data, $base = '') {
function get_content_by_parsing ($url, $path) {
require_once (LIB_PATH . '/lib_phpQuery.php');
- syslog(LOG_INFO, 'FreshRSS GET ' . $url);
+ Minz_Log::notice('FreshRSS GET ' . url_remove_credentials($url));
$html = file_get_contents ($url);
if ($html) {
@@ -248,7 +248,7 @@ function listUsers() {
* @return a Minz_Configuration object, null if the configuration cannot be loaded.
*/
function get_user_configuration($username) {
- $namespace = time() . '_user_' . $username;
+ $namespace = 'user_' . $username;
try {
Minz_Configuration::register($namespace,
join_path(USERS_PATH, $username, 'config.php'),
@@ -429,3 +429,13 @@ function array_push_unique(&$array, $value) {
function array_remove(&$array, $value) {
$array = array_diff($array, array($value));
}
+
+
+/**
+ * Sanitize a URL by removing HTTP credentials.
+ * @param $url the URL to sanitize.
+ * @return the same URL without HTTP credentials.
+ */
+function url_remove_credentials($url) {
+ return preg_replace('/[^\/]*:[^:]*@/', '', $url);
+}
diff --git a/p/f.php b/p/f.php
index fefbf9a10..c904e1fcb 100644
--- a/p/f.php
+++ b/p/f.php
@@ -4,6 +4,7 @@ require('../constants.php');
include(LIB_PATH . '/Favicon/Favicon.php');
include(LIB_PATH . '/Favicon/DataAccess.php');
+require(LIB_PATH . '/http-conditional.php');
$favicons_dir = DATA_PATH . '/favicons/';
@@ -46,10 +47,13 @@ function download_favicon($website, $dest) {
function show_default_favicon() {
global $default_favicon;
- header('HTTP/1.1 404 Not Found');
- header('Content-Type: image/ico');
- readfile($default_favicon);
- die();
+ header('Content-Type: image/x-icon');
+ header('Content-Disposition: inline; filename="default_favicon.ico"');
+
+ $default_mtime = @filemtime($default_favicon);
+ if (!httpConditional($default_mtime, 2592000, 2)) {
+ readfile($default_favicon);
+ }
}
@@ -64,19 +68,22 @@ $ico = $favicons_dir . $id . '.ico';
$ico_mtime = @filemtime($ico);
$txt_mtime = @filemtime($txt);
-if (($ico_mtime == false) || ($txt_mtime > $ico_mtime)) {
+
+if ($ico_mtime == false || $txt_mtime > $ico_mtime) {
if ($txt_mtime == false) {
show_default_favicon();
+ return;
}
+ // no ico file or we should download a new one.
$url = file_get_contents($txt);
if (!download_favicon($url, $ico)) {
+ // Download failed, show the default favicon
show_default_favicon();
+ return;
}
}
-require(LIB_PATH . '/http-conditional.php');
-
header('Content-Type: image/x-icon');
header('Content-Disposition: inline; filename="' . $id . '.ico"');
diff --git a/p/scripts/main.js b/p/scripts/main.js
index 9b6524b01..1be75bb12 100644
--- a/p/scripts/main.js
+++ b/p/scripts/main.js
@@ -155,6 +155,9 @@ function mark_read(active, only_not_read) {
faviconNbUnread();
pending_feeds.splice(index_pending, 1);
+ }).fail(function (data) {
+ openNotification(i18n.notif_request_failed, 'bad');
+ pending_feeds.splice(index_pending, 1);
});
}
@@ -210,6 +213,9 @@ function mark_favorite(active) {
}
pending_feeds.splice(index_pending, 1);
+ }).fail(function (data) {
+ openNotification(i18n.notif_request_failed, 'bad');
+ pending_feeds.splice(index_pending, 1);
});
}
@@ -513,7 +519,7 @@ function init_column_categories() {
if ($(this).nextAll('.dropdown-menu').length === 0) {
var feed_id = $(this).closest('.item').attr('id').substr(2),
feed_web = $(this).data('fweb'),
- template = $('#feed_config_template').html().replace(/!!!!!!/g, feed_id).replace('http://example.net/', feed_web);
+ template = $('#feed_config_template').html().replace(/------/g, feed_id).replace('http://example.net/', feed_web);
$(this).attr('href', '#dropdown-' + feed_id).prev('.dropdown-target').attr('id', 'dropdown-' + feed_id).parent().append(template);
}
});
diff --git a/p/themes/BlueLagoon/README.md b/p/themes/BlueLagoon/README.md
index 62afc234b..a83dd6743 100644
--- a/p/themes/BlueLagoon/README.md
+++ b/p/themes/BlueLagoon/README.md
@@ -1,7 +1,7 @@
Blue Lagoon
=======
-**C'est un cocktail (bis)! C'est la version plus "fresh" de [Screwdriver](https://github.com/misterair/Screwdriver). C'est... c'est... un thème pour l'agrégateur de flux RSS [FreshRSS](https://github.com/marienfressinaud/FreshRSS/)**
+**C'est un cocktail (bis)! C'est la version plus "fresh" de [Screwdriver](https://github.com/misterair/Screwdriver). C'est... c'est... un thème pour l'agrégateur de flux RSS [FreshRSS](https://github.com/FreshRSS/FreshRSS/)**
En toute modestie, ce thème tue du Nyan Cat.
diff --git a/p/themes/Dark/dark.css b/p/themes/Dark/dark.css
index dd7502f25..cd2f85ebf 100644
--- a/p/themes/Dark/dark.css
+++ b/p/themes/Dark/dark.css
@@ -879,6 +879,12 @@ a.btn {
border-radius: 3px;
}
+/*=== Slider */
+#slider {
+ background-color: #1c1c1c;
+ border-left: 1px solid #666;
+}
+
/*=== DIVERS */
/*===========*/
.aside.aside_feed .nav-form input,
diff --git a/p/themes/Screwdriver/README.md b/p/themes/Screwdriver/README.md
index 4b10eb90f..385e1dc71 100644
--- a/p/themes/Screwdriver/README.md
+++ b/p/themes/Screwdriver/README.md
@@ -1,7 +1,7 @@
Screwdriver
=======
-**C'est un cocktail! C'est chaud mais "fresh" à la fois. C'est... c'est... un thème pour l'agrégateur de flux RSS<a href="https://github.com/marienfressinaud/FreshRSS/" target="blank">FreshRSS</a>!!**
+**C'est un cocktail! C'est chaud mais "fresh" à la fois. C'est... c'est... un thème pour l'agrégateur de flux RSS<a href="https://github.com/FreshRSS/FreshRSS/" target="blank">FreshRSS</a>!!**
En toute modestie, ce thème tue du chaton.
![screenshot](https://github.com/misterair/Screwdriver/blob/master/screenshot.png)
diff --git a/p/themes/base-theme/README.md b/p/themes/base-theme/README.md
index 6f186e15c..9f7d635ca 100644
--- a/p/themes/base-theme/README.md
+++ b/p/themes/base-theme/README.md
@@ -8,5 +8,5 @@ A base theme for [FreshRSS](http://freshrss.org)
3. Choose your new theme in FreshRSS configuration
4. Enjoy your wonderful theme!
-Don't hesitate to share your theme with us [on Github](https://github.com/marienfressinaud/FreshRSS/issues) :)
+Don't hesitate to share your theme with us [on Github](https://github.com/FreshRSS/FreshRSS/issues) :)
diff --git a/p/themes/base-theme/template.css b/p/themes/base-theme/template.css
index 25f206106..a299a5ddf 100644
--- a/p/themes/base-theme/template.css
+++ b/p/themes/base-theme/template.css
@@ -75,6 +75,7 @@ input {
}
textarea,
input[type="file"],
+input.long,
input.extend:focus {
width: 300px;
}
@@ -485,7 +486,7 @@ a.btn {
text-decoration: none;
}
.flux .item.date {
- width: 145px;
+ width: 155px;
text-align: right;
overflow: hidden;
}
diff --git a/tests/app/Models/CategoryTest.php b/tests/app/Models/CategoryTest.php
new file mode 100644
index 000000000..da439b785
--- /dev/null
+++ b/tests/app/Models/CategoryTest.php
@@ -0,0 +1,32 @@
+<?php
+
+class FreshRSS_CategoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function test__construct_whenNoParameters_createsObjectWithDefaultValues() {
+ $category = new FreshRSS_Category();
+ $this->assertEquals(0, $category->id());
+ $this->assertEquals('', $category->name());
+ }
+
+ /**
+ * @param string $input
+ * @param string $expected
+ * @dataProvider provideValidNames
+ */
+ public function test_name_whenValidValue_storesModifiedValue($input, $expected) {
+ $category = new FreshRSS_Category($input);
+ $this->assertEquals($expected, $category->name());
+ }
+
+ public function provideValidNames() {
+ return array(
+ array('', ''),
+ array('this string does not need trimming', 'this string does not need trimming'),
+ array(' this string needs trimming on left', 'this string needs trimming on left'),
+ array('this string needs trimming on right ', 'this string needs trimming on right'),
+ array(' this string needs trimming on both ends ', 'this string needs trimming on both ends'),
+ array(str_repeat('This string needs to be shortened because its length is way too long. ', 4), str_repeat('This string needs to be shortened because its length is way too long. ', 3) . 'This string needs to be shortened because its'),
+ );
+ }
+
+}
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
new file mode 100644
index 000000000..24340b15c
--- /dev/null
+++ b/tests/bootstrap.php
@@ -0,0 +1,7 @@
+<?php
+
+error_reporting(E_ALL);
+ini_set('display_errors', 1);
+
+require('../constants.php');
+require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader \ No newline at end of file
diff --git a/tests/phpunit.xml b/tests/phpunit.xml
new file mode 100644
index 000000000..f639637ec
--- /dev/null
+++ b/tests/phpunit.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit bootstrap="bootstrap.php">
+ <filter>
+ <whitelist>
+ <directory suffix=".php">../app</directory>
+ </whitelist>
+ </filter>
+ <testsuites>
+ <testsuite name="FreshRSS">
+ <directory>app</directory>
+ </testsuite>
+ </testsuites>
+</phpunit> \ No newline at end of file