aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig7
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml20
-rw-r--r--CHANGELOG.md49
-rw-r--r--CREDITS.md2
-rw-r--r--README.fr.md5
-rw-r--r--README.md7
-rwxr-xr-xapp/Controllers/configureController.php6
-rwxr-xr-xapp/Controllers/entryController.php4
-rw-r--r--app/Controllers/extensionController.php37
-rwxr-xr-xapp/Controllers/feedController.php27
-rw-r--r--app/Controllers/updateController.php4
-rw-r--r--app/Controllers/userController.php4
-rw-r--r--app/FreshRSS.php8
-rw-r--r--app/Models/DatabaseDAO.php41
-rw-r--r--app/Models/DatabaseDAOPGSQL.php37
-rw-r--r--app/Models/DatabaseDAOSQLite.php13
-rw-r--r--app/Models/Entry.php8
-rw-r--r--app/Models/EntryDAO.php22
-rw-r--r--app/Models/EntryDAOPGSQL.php11
-rw-r--r--app/Models/EntryDAOSQLite.php8
-rw-r--r--app/Models/Feed.php43
-rw-r--r--app/Models/LogDAO.php6
-rw-r--r--app/Models/Share.php31
-rwxr-xr-xapp/actualize_script.php23
-rw-r--r--app/i18n/cz/admin.php7
-rw-r--r--app/i18n/cz/gen.php14
-rw-r--r--app/i18n/cz/sub.php4
-rw-r--r--app/i18n/de/admin.php17
-rw-r--r--app/i18n/de/gen.php13
-rw-r--r--app/i18n/de/sub.php4
-rw-r--r--app/i18n/en/admin.php7
-rw-r--r--app/i18n/en/gen.php12
-rw-r--r--app/i18n/en/sub.php4
-rwxr-xr-xapp/i18n/es/admin.php7
-rwxr-xr-xapp/i18n/es/gen.php12
-rwxr-xr-xapp/i18n/es/sub.php4
-rw-r--r--app/i18n/fr/admin.php13
-rw-r--r--app/i18n/fr/gen.php12
-rw-r--r--app/i18n/fr/sub.php4
-rw-r--r--app/i18n/he/admin.php188
-rw-r--r--app/i18n/he/conf.php171
-rw-r--r--app/i18n/he/feedback.php110
-rw-r--r--app/i18n/he/gen.php191
-rw-r--r--app/i18n/he/index.php61
-rw-r--r--app/i18n/he/install.php107
-rw-r--r--app/i18n/he/sub.php76
-rw-r--r--app/i18n/it/admin.php7
-rw-r--r--app/i18n/it/gen.php12
-rw-r--r--app/i18n/it/sub.php4
-rw-r--r--app/i18n/kr/admin.php7
-rw-r--r--app/i18n/kr/gen.php12
-rw-r--r--app/i18n/kr/sub.php4
-rw-r--r--app/i18n/nl/admin.php7
-rw-r--r--app/i18n/nl/gen.php18
-rw-r--r--app/i18n/nl/sub.php14
-rw-r--r--app/i18n/pt-br/admin.php7
-rw-r--r--app/i18n/pt-br/gen.php12
-rw-r--r--app/i18n/pt-br/sub.php9
-rw-r--r--app/i18n/ru/admin.php7
-rw-r--r--app/i18n/ru/gen.php10
-rw-r--r--app/i18n/ru/sub.php4
-rw-r--r--app/i18n/tr/admin.php7
-rw-r--r--app/i18n/tr/gen.php12
-rw-r--r--app/i18n/tr/sub.php4
-rw-r--r--app/i18n/zh-cn/admin.php41
-rw-r--r--app/i18n/zh-cn/conf.php14
-rw-r--r--app/i18n/zh-cn/feedback.php6
-rw-r--r--app/i18n/zh-cn/gen.php16
-rw-r--r--app/i18n/zh-cn/install.php18
-rw-r--r--app/i18n/zh-cn/sub.php28
-rw-r--r--app/layout/nav_menu.phtml6
-rw-r--r--app/views/configure/sharing.phtml6
-rw-r--r--app/views/configure/system.phtml2
-rw-r--r--app/views/extension/index.phtml37
-rw-r--r--app/views/helpers/index/normal/entry_bottom.phtml9
-rw-r--r--app/views/subscription/bookmarklet.phtml4
-rw-r--r--app/views/update/index.phtml16
-rw-r--r--app/views/user/profile.phtml2
-rw-r--r--cli/README.md53
-rw-r--r--cli/_cli.php19
-rw-r--r--cli/_update-or-create-user.php2
-rwxr-xr-xcli/actualize-user.php2
-rw-r--r--cli/check.translation.php106
-rwxr-xr-xcli/create-user.php2
-rwxr-xr-xcli/db-optimize.php20
-rwxr-xr-xcli/delete-user.php2
-rwxr-xr-xcli/do-install.php17
-rwxr-xr-xcli/export-opml-for-user.php2
-rwxr-xr-xcli/export-zip-for-user.php2
-rw-r--r--cli/i18n/I18nCompletionValidator.php49
-rw-r--r--cli/i18n/I18nData.php118
-rw-r--r--cli/i18n/I18nFile.php92
-rw-r--r--cli/i18n/I18nUsageValidator.php47
-rw-r--r--cli/i18n/I18nValidatorInterface.php26
-rw-r--r--cli/i18n/ignore/en.php105
-rw-r--r--cli/i18n/ignore/fr.php55
-rwxr-xr-xcli/import-for-user.php2
-rwxr-xr-xcli/list-users.php2
-rw-r--r--cli/manipulate.translation.php79
-rwxr-xr-xcli/reconfigure.php2
-rwxr-xr-xcli/update-user.php2
-rwxr-xr-xcli/user-info.php7
-rw-r--r--config.default.php2
-rw-r--r--constants.php59
-rw-r--r--data/shares.php25
-rw-r--r--docs/en/admins/01_Index.md8
-rw-r--r--docs/en/admins/02_Installation.md (renamed from docs/en/users/01_Installation.md)141
-rw-r--r--docs/en/admins/03_Updating.md90
-rw-r--r--docs/en/contributing.md7
-rw-r--r--docs/en/developers/01_First_steps.md4
-rw-r--r--docs/en/developers/03_Backend/05_Extensions.md94
-rw-r--r--docs/en/index.md8
-rw-r--r--docs/en/users/02_First_steps.md4
-rw-r--r--docs/en/users/05_Configuration.md16
-rw-r--r--docs/en/users/06_Mobile_access.md52
-rw-r--r--docs/fr/developers/03_Backend/05_Extensions.md4
-rw-r--r--docs/fr/users/01_Installation.md136
-rw-r--r--docs/fr/users/06_Mobile_access.md50
-rw-r--r--lib/Minz/Dispatcher.php3
-rw-r--r--lib/Minz/ExtensionManager.php36
-rw-r--r--lib/Minz/Log.php34
-rw-r--r--lib/favicons.php10
-rw-r--r--lib/lib_rss.php16
-rw-r--r--p/.htaccess9
-rw-r--r--p/api/greader.php51
-rw-r--r--p/api/index.php2
-rw-r--r--p/api/pshb.php42
-rw-r--r--p/ext.php2
-rw-r--r--p/f.php2
-rwxr-xr-xp/i/index.php2
-rw-r--r--p/scripts/main.js11
-rw-r--r--p/themes/BlueLagoon/BlueLagoon.css6
-rw-r--r--p/themes/Dark/dark.css6
-rw-r--r--p/themes/Flat/flat.css6
-rw-r--r--p/themes/Origine/origine.css6
-rw-r--r--p/themes/Pafat/pafat.css8
-rw-r--r--p/themes/Screwdriver/screwdriver.css6
-rw-r--r--p/themes/base-theme/base.css6
-rw-r--r--p/themes/base-theme/template.css10
-rw-r--r--p/themes/fonts/OpenSans.woffbin0 -> 67528 bytes
-rw-r--r--p/themes/fonts/OpenSans.woff2bin0 -> 61980 bytes
-rw-r--r--p/themes/fonts/openSans.woffbin21956 -> 0 bytes
-rw-r--r--tests/bootstrap.php2
144 files changed, 2986 insertions, 627 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..e18761a1a
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,7 @@
+[*]
+end_of_line = lf
+
+[*.php]
+indent_style = tab
+indent_size = 4
+insert_final_newline = true
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..cd2fd5d3a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+constants.local.php
diff --git a/.travis.yml b/.travis.yml
index 5c43e5666..7bfefd8ce 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,16 +14,27 @@ install:
script:
- phpenv rehash
- - phpcs . --standard=phpcs.xml --warning-severity=0 --extensions=php -p
+ - |
+ if [[ $VALIDATE_STANDARD == yes ]]; then
+ phpcs . --standard=phpcs.xml --warning-severity=0 --extensions=php -p
+ fi
+ - |
+ if [[ $CHECK_TRANSLATION == yes ]]; then
+ php cli/check.translation.php -r
+ fi
+
+env:
+ - CHECK_TRANSLATION=no VALIDATE_STANDARD=yes
-env: # important! otherwise no job will be allowed to fail
matrix:
- # PHP 5.3 only runs on Ubuntu 12.04 (precise), not 14.04 (trusty)
+ fast_finish: true
include:
- php: "5.3"
dist: precise
- fast_finish: true
+ - php: "7.1"
+ env: CHECK_TRANSLATION=yes VALIDATE_STANDARD=no
allow_failures:
+ # PHP 5.3 only runs on Ubuntu 12.04 (precise), not 14.04 (trusty)
- php: "5.3"
dist: precise
- php: "5.4"
@@ -32,3 +43,4 @@ matrix:
- php: "7.0"
- php: hhvm
- php: nightly
+ - env: CHECK_TRANSLATION=yes VALIDATE_STANDARD=no
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9dbdbe960..77252fb78 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,47 @@
-# Changelog
+# FreshRSS changelog
+
+## 2017-12-17 FreshRSS 1.9.0
+
+* Features
+ * Share with Mastodon [#1521](https://github.com/FreshRSS/FreshRSS/issues/1521)
+* UI
+ * Add more Unicode glyphs in the Open Sans font [#1032](https://github.com/FreshRSS/FreshRSS/pull/1032)
+ * Show URL to add subscriptions from third-party tools [#1247](https://github.com/FreshRSS/FreshRSS/issues/1247)
+ * Improved message when checking for new versions [#1586](https://github.com/FreshRSS/FreshRSS/issues/1586)
+* SimplePie
+ * Remove "SimplePie" name from HTTP User-Agent string [#1656](https://github.com/FreshRSS/FreshRSS/pull/1656)
+* Bug fixing
+ * Work-around for PHP 5.6.0- `CURLOPT_FOLLOWLOCATION` `open_basedir` bug in favicons and PubSubHubbub [#1655](https://github.com/FreshRSS/FreshRSS/issues/1655)
+ * Fix PDO PostgreSQL detection [#1690](https://github.com/FreshRSS/FreshRSS/issues/1690)
+ * Fix punycode warning in PHP 7.2 [#1699](https://github.com/FreshRSS/FreshRSS/issues/1699)
+ * Fix crash when adding a new category while adding a new feed [#1731](https://github.com/FreshRSS/FreshRSS/pull/1731)
+ * Fix ExtensionManager exception handling [#1724](https://github.com/FreshRSS/FreshRSS/pull/1724)
+* CLI
+ * New command `./cli/db-optimize.php` for database optimisation [#1583](https://github.com/FreshRSS/FreshRSS/issues/1583)
+ * Check PHP requirements before running `actualize_script.php` (cron for refreshing feeds) [#1711](https://github.com/FreshRSS/FreshRSS/pull/1711)
+* SQL
+ * Perform `VACUUM` on SQLite and PostgreSQL databases when optimisation is requested [#918](https://github.com/FreshRSS/FreshRSS/issues/918)
+* API
+ * Breaking change / compatibility fix (EasyRSS): Provide `link` to articles without HTML-encoding [#1683](https://github.com/FreshRSS/FreshRSS/issues/1683)
+* Extensions
+ * Breaking change: uppercase `./Controllers/` directory [#1729](https://github.com/FreshRSS/FreshRSS/pull/1729)
+ * Show existing extensions in admin panel [#1708](https://github.com/FreshRSS/FreshRSS/pull/1708)
+ * New function `$entry->_hash($hex)` for extensions that change the content of entries [#1707](https://github.com/FreshRSS/FreshRSS/pull/1707)
+* I18n
+ * Hebrew [#1716](https://github.com/FreshRSS/FreshRSS/pull/1716)
+ * Improved German [#1698](https://github.com/FreshRSS/FreshRSS/pull/1698)
+* Misc.
+ * Customisable `constants.local.php` [#1725](https://github.com/FreshRSS/FreshRSS/pull/1725)
+ * Basic mechanism to limit the size of the logs [#1712](https://github.com/FreshRSS/FreshRSS/pull/1712)
+ * Translation validation tool [#1653](https://github.com/FreshRSS/FreshRSS/pull/1653)
+ * Translation manipulation tool [#1658](https://github.com/FreshRSS/FreshRSS/pull/1658)
+ * Improved documentation [#1697](https://github.com/FreshRSS/FreshRSS/pull/1697), [#1704](https://github.com/FreshRSS/FreshRSS/pull/1704)
+ * New `.editorconfig` file [#1732](https://github.com/FreshRSS/FreshRSS/pull/1732)
+
## 2017-10-01 FreshRSS 1.8.0
-* Compatibility:
+* Compatibility
* Minimal PHP version increased to PHP 5.3.8+ to fix sanitize bug [#1604](https://github.com/FreshRSS/FreshRSS/issues/1604)
* Add support for PHP 7.1 in the API [#1584](https://github.com/FreshRSS/FreshRSS/issues/1584), [#1594](https://github.com/FreshRSS/FreshRSS/pull/1594)
* UI
@@ -13,7 +52,7 @@
* Fix share menu on small screens [#1645](https://github.com/FreshRSS/FreshRSS/pull/1645)
* Go back to previous view when collapsing article [#1177](https://github.com/FreshRSS/FreshRSS/issues/1177)
* CLI
- * New command `./cli/update-user` to update user settings [#1600](https://github.com/FreshRSS/FreshRSS/issues/1600)
+ * New command `./cli/update-user.php` to update user settings [#1600](https://github.com/FreshRSS/FreshRSS/issues/1600)
* I18n
* Korean [#1578](https://github.com/FreshRSS/FreshRSS/pull/1578)
* Portuguese (Brazilian) [#1648](https://github.com/FreshRSS/FreshRSS/pull/1648)
@@ -30,7 +69,7 @@
## 2017-06-03 FreshRSS 1.7.0
-* Features:
+* Features
* Deferred insertion of new articles, for better chronological order [#530](https://github.com/FreshRSS/FreshRSS/issues/530)
* Better search:
* Possibility to use multiple `intitle:`, `inurl:`, `author:` [#1478](https://github.com/FreshRSS/FreshRSS/pull/1478)
@@ -38,7 +77,7 @@
* Examples: `!intitle:unwanted`, `-intitle:unwanted`, `-inurl:unwanted`, `-author:unwanted`, `-#unwanted`, `-unwanted`
* Allow double-quotes, such as `author:"some name"`, in addition to single-quotes such as `author:'some name'` [#1478](https://github.com/FreshRSS/FreshRSS/pull/1478)
* Multi-user tokens (to access RSS outputs of any user) [#1390](https://github.com/FreshRSS/FreshRSS/issues/1390)
-* Compatibility:
+* Compatibility
* Add support for PHP 7.1 [#1471](https://github.com/FreshRSS/FreshRSS/issues/1471)
* PostgreSQL is not experimental anymore [#1476](https://github.com/FreshRSS/FreshRSS/pull/1476)
* Bug fixing
diff --git a/CREDITS.md b/CREDITS.md
index cbbef73d6..afe4b13b2 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -13,6 +13,7 @@ People are sorted by name so please keep this order.
* [Amaury Carrade](https://github.com/AmauryCarrade): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=AmauryCarrade), [Web](https://amaury.carrade.eu/)
* [Anton Smirnov](https://github.com/sandfoxme): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:sandfoxme), [Web](http://sandfox.me/)
* [ASMfreaK](https://github.com/ASMfreaK): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=ASMfreaK)
+* [Craig Andrews](https://github.com/candrews): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:candrews), [Web](http://candrews.integralblue.com/)
* [Crupuk](https://github.com/Crupuk): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:Crupuk)
* [Damstre](https://github.com/Damstre): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:Damstre)
* [danc](https://github.com/danc): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=danc), [Web](http://tintouli.free.fr/)
@@ -38,6 +39,7 @@ People are sorted by name so please keep this order.
* [MSZ](https://github.com/mszkb): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=mszkb)
* [Nicolas Elie](https://github.com/nicolaselie): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=nicolaselie)
* [Nicolas Lœuillet](https://github.com/nicosomb): [contributions](https://github.com/FreshRSS/documentation/commits?author=nicosomb), [Web](http://www.loeuillet.org/)
+* [Olivier Dossmann](https://github.com/blankoworld): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=blankoworld), [Web](https://olivier.dossmann.net)
* [plopoyop](https://github.com/plopoyop): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=plopoyop)
* [Paulius Šukys](https://github.com/psukys): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:psukys), [Web](http://sukys.eu)
* [purexo](https://github.com/purexo): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:purexo), [Web](https://purexo.mom/)
diff --git a/README.fr.md b/README.fr.md
index 8f19b280a..797d43504 100644
--- a/README.fr.md
+++ b/README.fr.md
@@ -44,7 +44,7 @@ Nous sommes une communauté amicale.
# Documentation
* https://freshrss.github.io/FreshRSS/fr/
-# Installation
+# [Installation](https://freshrss.github.io/FreshRSS/fr/users/01_Installation.html)
1. Récupérez l’application FreshRSS via la commande git ou [en téléchargeant l’archive](../releases)
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/`
@@ -52,6 +52,7 @@ Nous sommes une communauté amicale.
* ou utilisez [l’interface en ligne de commande](./cli/README.md)
5. Tout devrait fonctionner :) En cas de problème, n’hésitez pas à [nous contacter](https://github.com/FreshRSS/FreshRSS/issues).
6. Des paramètres de configuration avancée peuvent être vues dans [config.default.php](./config.default.php) et modifiées dans `data/config.php`.
+7. Avec Apache, activer [`AllowEncodedSlashes`](http://httpd.apache.org/docs/trunk/mod/core.html#allowencodedslashes) pour une meilleure compatibilité avec les clients mobiles.
## Installation automatisée
* [![DP deploy](https://raw.githubusercontent.com/DFabric/DPlatform-ShellCore/gh-pages/img/deploy.png)](https://dfabric.github.io/DPlatform-ShellCore)
@@ -171,7 +172,7 @@ Voir le [dépôt dédié à ces extensions](https://github.com/FreshRSS/Extensio
* [password_compat](https://github.com/ircmaxell/password_compat)
-# Clients compatibles
+# [Clients compatibles](https://freshrss.github.io/FreshRSS/en/users/06_Mobile_access.html)
Tout client supportant une API de type Google Reader. Sélection :
* Android
diff --git a/README.md b/README.md
index 14ca65a51..1cf66a688 100644
--- a/README.md
+++ b/README.md
@@ -46,7 +46,7 @@ We are a friendly community.
# Documentation
* https://freshrss.github.io/FreshRSS/en/
-# Installation
+# [Installation](https://freshrss.github.io/FreshRSS/en/users/01_Installation.html)
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
@@ -54,6 +54,9 @@ We are a friendly community.
* or use the [Command-Line Interface](./cli/README.md)
5. Everything should be working :) If you encounter any problem, feel free [contact us](https://github.com/FreshRSS/FreshRSS/issues).
6. Advanced configuration settings can be seen in [config.default.php](./config.default.php) and modified in `data/config.php`.
+7. When using Apache, enable [`AllowEncodedSlashes`](http://httpd.apache.org/docs/trunk/mod/core.html#allowencodedslashes) for better compatibility with mobile clients.
+
+More information about installation and server configuration can be found in [our documentation](https://freshrss.github.io/FreshRSS/en/admins/02_Installation.html).
## Automated install
* [![Install on Cloudron](https://cloudron.io/img/button.svg)](https://cloudron.io/button.html?app=org.freshrss.cloudronapp)
@@ -175,7 +178,7 @@ See the [repository dedicated to those extensions](https://github.com/FreshRSS/E
* [password_compat](https://github.com/ircmaxell/password_compat)
-# Compatible clients
+# [Compatible clients](https://freshrss.github.io/FreshRSS/en/users/06_Mobile_access.html)
Any client supporting a Google Reader-like API. Selection:
* Android
diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php
index 155221d19..9d2ee450c 100755
--- a/app/Controllers/configureController.php
+++ b/app/Controllers/configureController.php
@@ -225,10 +225,12 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
$entryDAO = FreshRSS_Factory::createEntryDao();
$this->view->nb_total = $entryDAO->count();
- $this->view->size_user = $entryDAO->size();
+
+ $databaseDAO = FreshRSS_Factory::createDatabaseDAO();
+ $this->view->size_user = $databaseDAO->size();
if (FreshRSS_Auth::hasAccess('admin')) {
- $this->view->size_total = $entryDAO->size(true);
+ $this->view->size_total = $databaseDAO->size(true);
}
}
diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php
index c40588105..bd8b65b2b 100755
--- a/app/Controllers/entryController.php
+++ b/app/Controllers/entryController.php
@@ -147,8 +147,8 @@ class FreshRSS_entry_Controller extends Minz_ActionController {
@set_time_limit(300);
- $entryDAO = FreshRSS_Factory::createEntryDao();
- $entryDAO->optimizeTable();
+ $databaseDAO = FreshRSS_Factory::createDatabaseDAO();
+ $databaseDAO->optimize();
$feedDAO = FreshRSS_Factory::createFeedDao();
$feedDAO->updateCachedValues();
diff --git a/app/Controllers/extensionController.php b/app/Controllers/extensionController.php
index b6d2d3fe4..bb846e921 100644
--- a/app/Controllers/extensionController.php
+++ b/app/Controllers/extensionController.php
@@ -25,10 +25,47 @@ class FreshRSS_extension_Controller extends Minz_ActionController {
'user' => array(),
);
+ $this->view->extensions_installed = array();
+
$extensions = Minz_ExtensionManager::listExtensions();
foreach ($extensions as $ext) {
$this->view->extension_list[$ext->getType()][] = $ext;
+ $this->view->extensions_installed[$ext->getEntrypoint()] = $ext->getVersion();
+ }
+
+ $availableExtensions = $this->getAvailableExtensionList();
+ $this->view->available_extensions = $availableExtensions;
+ }
+
+ /**
+ * fetch extension list from GitHub
+ */
+ protected function getAvailableExtensionList() {
+ $extensionListUrl = 'https://raw.githubusercontent.com/FreshRSS/Extensions/master/extensions.json';
+ $json = file_get_contents($extensionListUrl);
+
+ // we ran into problems, simply ignore them
+ if ($json === false) {
+ Minz_Log::error('Could not fetch available extension from GitHub');
+ return array();
+ }
+
+ // fetch the list as an array
+ $list = json_decode($json, true);
+ if (empty($list)) {
+ Minz_Log::warning('Failed to convert extension file list');
+ return array();
}
+
+ // we could use that for comparing and caching later
+ $version = $list['version'];
+
+ // By now, all the needed data is kept in the main extension file.
+ // In the future we could fetch detail information from the extensions metadata.json, but I tend to stick with
+ // the current implementation for now, unless it becomes too much effort maintain the extension list manually
+ $extensions = $list['extensions'];
+
+ return $extensions;
}
/**
diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php
index 45cba9e98..fff20f798 100755
--- a/app/Controllers/feedController.php
+++ b/app/Controllers/feedController.php
@@ -26,6 +26,18 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
}
}
+ /**
+ * @param $url
+ * @param string $title
+ * @param int $cat_id
+ * @param string $new_cat_name
+ * @param string $http_auth
+ * @return FreshRSS_Feed|the
+ * @throws FreshRSS_AlreadySubscribed_Exception
+ * @throws FreshRSS_FeedNotAdded_Exception
+ * @throws FreshRSS_Feed_Exception
+ * @throws Minz_FileNotExistException
+ */
public static function addFeed($url, $title = '', $cat_id = 0, $new_cat_name = '', $http_auth = '') {
FreshRSS_UserDAO::touch();
@set_time_limit(300);
@@ -33,12 +45,13 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
$catDAO = new FreshRSS_CategoryDAO();
$cat = null;
+ if ($new_cat_name != '') {
+ $new_cat_id = $catDAO->addCategory(array('name' => $new_cat_name));
+ $cat_id = $new_cat_id > 0 ? $new_cat_id : $cat_id;
+ }
if ($cat_id > 0) {
$cat = $catDAO->searchById($cat_id);
}
- if ($cat == null && $new_cat_name != '') {
- $cat = $catDAO->addCategory(array('name' => $new_cat_name));
- }
if ($cat == null) {
$catDAO->checkDefault();
}
@@ -54,7 +67,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
throw new FreshRSS_AlreadySubscribed_Exception($url, $feed->name());
}
- // Call the extension hook
+ /** @var FreshRSS_Feed $feed */
$feed = Minz_ExtensionManager::callHook('feed_before_insert', $feed);
if ($feed === null) {
throw new FreshRSS_FeedNotAdded_Exception($url, $feed->name());
@@ -136,7 +149,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
// User want to create a new category, new_category parameter
// must exist
$new_cat = Minz_Request::param('new_category');
- $new_cat_name = isset($new_cat['name']) ? $new_cat['name'] : '';
+ $new_cat_name = isset($new_cat['name']) ? trim($new_cat['name']) : '';
}
// HTTP information are useful if feed is protected behind a
@@ -263,7 +276,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
if ((!$simplePiePush) && (!$feed_id) && $pubSubHubbubEnabled && ($feed->lastUpdate() > $pshbMinAge)) {
//$text = 'Skip pull of feed using PubSubHubbub: ' . $url;
//Minz_Log::debug($text);
- //file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND);
+ //Minz_Log::debug($text, PSHB_LOG);
continue; //When PubSubHubbub is used, do not pull refresh so often
}
@@ -371,7 +384,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
if ($pubSubHubbubEnabled && !$simplePiePush) { //We use push, but have discovered an article by pull!
$text = 'An article was discovered by pull although we use PubSubHubbub!: Feed ' . $url . ' GUID ' . $entry->guid();
- file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND);
+ Minz_Log::warning($text, PSHB_LOG);
Minz_Log::warning($text);
$pubSubHubbubEnabled = false;
$feed->pubSubHubbubError(true);
diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php
index 7a8a3d6c0..c67b358bb 100644
--- a/app/Controllers/updateController.php
+++ b/app/Controllers/updateController.php
@@ -102,7 +102,7 @@ class FreshRSS_update_Controller extends Minz_ActionController {
$version = 'git';
} else {
$this->view->message = array(
- 'status' => 'bad',
+ 'status' => 'latest',
'title' => _t('gen.short.damn'),
'body' => _t('feedback.update.none')
);
@@ -138,7 +138,7 @@ class FreshRSS_update_Controller extends Minz_ActionController {
$status = $res_array[0];
if (strpos($status, 'UPDATE') !== 0) {
$this->view->message = array(
- 'status' => 'bad',
+ 'status' => 'latest',
'title' => _t('gen.short.damn'),
'body' => _t('feedback.update.none')
);
diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php
index a58501186..2a1d43d9e 100644
--- a/app/Controllers/userController.php
+++ b/app/Controllers/userController.php
@@ -120,7 +120,9 @@ class FreshRSS_user_Controller extends Minz_ActionController {
// Get information about the current user.
$entryDAO = FreshRSS_Factory::createEntryDao($this->view->current_user);
$this->view->nb_articles = $entryDAO->count();
- $this->view->size_user = $entryDAO->size();
+
+ $databaseDAO = FreshRSS_Factory::createDatabaseDAO();
+ $this->view->size_user = $databaseDAO->size();
}
public static function createUser($new_user_name, $passwordPlain, $apiPasswordPlain, $userConfig = array(), $insertDefaultFeeds = true) {
diff --git a/app/FreshRSS.php b/app/FreshRSS.php
index 90d6fae06..f53c85bfb 100644
--- a/app/FreshRSS.php
+++ b/app/FreshRSS.php
@@ -111,7 +111,13 @@ class FreshRSS extends Minz_FrontController {
public static function preLayout() {
switch (Minz_Request::controllerName()) {
case 'index':
- header("Content-Security-Policy: default-src 'self'; child-src *; frame-src *; img-src * data:; media-src *");
+ $urlToAuthorize = array_filter(array_map(function ($a) {
+ if (isset($a['method']) && $a['method'] === 'POST') {
+ return $a['url'];
+ }
+ }, FreshRSS_Context::$user_conf->sharing));
+ $connectSrc = count($urlToAuthorize) ? sprintf("; connect-src 'self' %s", implode(' ', $urlToAuthorize)) : '';
+ header(sprintf("Content-Security-Policy: default-src 'self'; child-src *; frame-src *; img-src * data:; media-src *%s", $connectSrc));
break;
case 'stats':
header("Content-Security-Policy: default-src 'self'; style-src 'self' 'unsafe-inline'");
diff --git a/app/Models/DatabaseDAO.php b/app/Models/DatabaseDAO.php
index 6ba5bca3e..f5469f2b7 100644
--- a/app/Models/DatabaseDAO.php
+++ b/app/Models/DatabaseDAO.php
@@ -80,4 +80,45 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo {
return $list;
}
+
+ public function size($all = false) {
+ $db = FreshRSS_Context::$system_conf->db;
+ $sql = 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema=?'; //MySQL
+ $values = array($db['base']);
+ if (!$all) {
+ $sql .= ' AND table_name LIKE ?';
+ $values[] = $this->prefix . '%';
+ }
+ $stm = $this->bd->prepare($sql);
+ $stm->execute($values);
+ $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
+ return $res[0];
+ }
+
+ public function optimize() {
+ $ok = true;
+
+ $sql = 'OPTIMIZE TABLE `' . $this->prefix . 'entry`'; //MySQL
+ $stm = $this->bd->prepare($sql);
+ $ok &= $stm != false;
+ if ($stm) {
+ $ok &= $stm->execute();
+ }
+
+ $sql = 'OPTIMIZE TABLE `' . $this->prefix . 'feed`'; //MySQL
+ $stm = $this->bd->prepare($sql);
+ $ok &= $stm != false;
+ if ($stm) {
+ $ok &= $stm->execute();
+ }
+
+ $sql = 'OPTIMIZE TABLE `' . $this->prefix . 'category`'; //MySQL
+ $stm = $this->bd->prepare($sql);
+ $ok &= $stm != false;
+ if ($stm) {
+ $ok &= $stm->execute();
+ }
+
+ return $ok;
+ }
}
diff --git a/app/Models/DatabaseDAOPGSQL.php b/app/Models/DatabaseDAOPGSQL.php
index 2a18db970..1b3f7408d 100644
--- a/app/Models/DatabaseDAOPGSQL.php
+++ b/app/Models/DatabaseDAOPGSQL.php
@@ -40,4 +40,41 @@ class FreshRSS_DatabaseDAOPGSQL extends FreshRSS_DatabaseDAO {
'default' => $dao['default'],
);
}
+
+ public function size($all = true) {
+ $db = FreshRSS_Context::$system_conf->db;
+ $sql = 'SELECT pg_size_pretty(pg_database_size(?))';
+ $values = array($db['base']);
+ $stm = $this->bd->prepare($sql);
+ $stm->execute($values);
+ $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
+ return $res[0];
+ }
+
+ public function optimize() {
+ $ok = true;
+
+ $sql = 'VACUUM `' . $this->prefix . 'entry`';
+ $stm = $this->bd->prepare($sql);
+ $ok &= $stm != false;
+ if ($stm) {
+ $ok &= $stm->execute();
+ }
+
+ $sql = 'VACUUM `' . $this->prefix . 'feed`';
+ $stm = $this->bd->prepare($sql);
+ $ok &= $stm != false;
+ if ($stm) {
+ $ok &= $stm->execute();
+ }
+
+ $sql = 'VACUUM `' . $this->prefix . 'category`';
+ $stm = $this->bd->prepare($sql);
+ $ok &= $stm != false;
+ if ($stm) {
+ $ok &= $stm->execute();
+ }
+
+ return $ok;
+ }
}
diff --git a/app/Models/DatabaseDAOSQLite.php b/app/Models/DatabaseDAOSQLite.php
index 2e1df132e..d3aedb3c0 100644
--- a/app/Models/DatabaseDAOSQLite.php
+++ b/app/Models/DatabaseDAOSQLite.php
@@ -45,4 +45,17 @@ class FreshRSS_DatabaseDAOSQLite extends FreshRSS_DatabaseDAO {
'default' => $dao['dflt_value'],
);
}
+
+ public function size($all = false) {
+ return @filesize(join_path(DATA_PATH, 'users', $this->current_user, 'db.sqlite'));
+ }
+
+ public function optimize() {
+ $sql = 'VACUUM';
+ $stm = $this->bd->prepare($sql);
+ if ($stm) {
+ return $stm->execute();
+ }
+ return false;
+ }
}
diff --git a/app/Models/Entry.php b/app/Models/Entry.php
index df3d59bea..0ad3781e5 100644
--- a/app/Models/Entry.php
+++ b/app/Models/Entry.php
@@ -97,6 +97,14 @@ class FreshRSS_Entry extends Minz_Model {
return $this->hash;
}
+ public function _hash($value) {
+ $value = trim($value);
+ if (ctype_xdigit($value)) {
+ $this->hash = substr($value, 0, 32);
+ }
+ return $this->hash;
+ }
+
public function _id($value) {
$this->id = $value;
}
diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php
index bebafe500..e8b6dcdae 100644
--- a/app/Models/EntryDAO.php
+++ b/app/Models/EntryDAO.php
@@ -885,28 +885,6 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
return array('all' => $all, 'unread' => $unread, 'read' => $all - $unread);
}
- public function optimizeTable() {
- $sql = 'OPTIMIZE TABLE `' . $this->prefix . 'entry`'; //MySQL
- $stm = $this->bd->prepare($sql);
- if ($stm) {
- return $stm->execute();
- }
- }
-
- public function size($all = false) {
- $db = FreshRSS_Context::$system_conf->db;
- $sql = 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema=?'; //MySQL
- $values = array($db['base']);
- if (!$all) {
- $sql .= ' AND table_name LIKE ?';
- $values[] = $this->prefix . '%';
- }
- $stm = $this->bd->prepare($sql);
- $stm->execute($values);
- $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
- return $res[0];
- }
-
public static function daoToEntry($dao) {
$entry = new FreshRSS_Entry(
$dao['id_feed'],
diff --git a/app/Models/EntryDAOPGSQL.php b/app/Models/EntryDAOPGSQL.php
index 405774abf..f09fe8e75 100644
--- a/app/Models/EntryDAOPGSQL.php
+++ b/app/Models/EntryDAOPGSQL.php
@@ -46,15 +46,4 @@ END $$;';
}
return $result;
}
-
- public function size($all = true) {
- $db = FreshRSS_Context::$system_conf->db;
- $sql = 'SELECT pg_size_pretty(pg_database_size(?))';
- $values = array($db['base']);
- $stm = $this->bd->prepare($sql);
- $stm->execute($values);
- $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
- return $res[0];
- }
-
}
diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php
index 8dad54322..0f57dc1ba 100644
--- a/app/Models/EntryDAOSQLite.php
+++ b/app/Models/EntryDAOSQLite.php
@@ -261,12 +261,4 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO {
}
return $affected;
}
-
- public function optimizeTable() {
- //TODO: Search for an equivalent in SQLite
- }
-
- public function size($all = false) {
- return @filesize(join_path(DATA_PATH, 'users', $this->current_user, 'db.sqlite'));
- }
}
diff --git a/app/Models/Feed.php b/app/Models/Feed.php
index d8fe03197..560f7415d 100644
--- a/app/Models/Feed.php
+++ b/app/Models/Feed.php
@@ -403,8 +403,7 @@ class FreshRSS_Feed extends Minz_Model {
if (!isset($hubJson['error']) || $hubJson['error'] !== (bool)$error) {
$hubJson['error'] = (bool)$error;
file_put_contents($hubFilename, json_encode($hubJson));
- file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t"
- . 'Set error to ' . ($error ? 1 : 0) . ' for ' . $url . "\n", FILE_APPEND);
+ Minz_Log::warning('Set error to ' . ($error ? 1 : 0) . ' for ' . $url, PSHB_LOG);
}
return false;
}
@@ -419,7 +418,7 @@ class FreshRSS_Feed extends Minz_Model {
if (!$hubJson || empty($hubJson['key']) || !ctype_xdigit($hubJson['key'])) {
$text = 'Invalid JSON for PubSubHubbub: ' . $this->url;
Minz_Log::warning($text);
- file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND);
+ Minz_Log::warning($text, PSHB_LOG);
return false;
}
if ((!empty($hubJson['lease_end'])) && ($hubJson['lease_end'] < (time() + (3600 * 23)))) { //TODO: Make a better policy
@@ -427,7 +426,7 @@ class FreshRSS_Feed extends Minz_Model {
. date('c', empty($hubJson['lease_end']) ? time() : $hubJson['lease_end'])
. ' and needs renewal: ' . $this->url;
Minz_Log::warning($text);
- file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND);
+ Minz_Log::warning($text, PSHB_LOG);
$key = $hubJson['key']; //To renew our lease
} elseif (((!empty($hubJson['error'])) || empty($hubJson['lease_end'])) &&
(empty($hubJson['lease_start']) || $hubJson['lease_start'] < time() - (3600 * 23))) { //Do not renew too often
@@ -445,7 +444,7 @@ class FreshRSS_Feed extends Minz_Model {
file_put_contents(PSHB_PATH . '/keys/' . $key . '.txt', base64url_encode($this->selfUrl));
$text = 'PubSubHubbub prepared for ' . $this->url;
Minz_Log::debug($text);
- file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND);
+ Minz_Log::debug($text, PSHB_LOG);
}
$currentUser = Minz_Session::param('currentUser');
if (FreshRSS_user_Controller::checkUsername($currentUser) && !file_exists($path . '/' . $currentUser . '.txt')) {
@@ -481,24 +480,28 @@ class FreshRSS_Feed extends Minz_Model {
}
$ch = curl_init();
curl_setopt_array($ch, array(
- CURLOPT_URL => $hubJson['hub'],
- CURLOPT_FOLLOWLOCATION => true,
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_USERAGENT => 'FreshRSS/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ')',
- CURLOPT_POSTFIELDS => http_build_query(array(
- 'hub.verify' => 'sync',
- 'hub.mode' => $state ? 'subscribe' : 'unsubscribe',
- 'hub.topic' => $url,
- 'hub.callback' => $callbackUrl,
- ))
- )
- );
+ CURLOPT_URL => $hubJson['hub'],
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_POSTFIELDS => http_build_query(array(
+ 'hub.verify' => 'sync',
+ 'hub.mode' => $state ? 'subscribe' : 'unsubscribe',
+ 'hub.topic' => $url,
+ 'hub.callback' => $callbackUrl,
+ )),
+ CURLOPT_USERAGENT => FRESHRSS_USERAGENT,
+ CURLOPT_MAXREDIRS => 10,
+ ));
+ if (version_compare(PHP_VERSION, '5.6.0') >= 0 || ini_get('open_basedir') == '') {
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); //Keep option separated for open_basedir PHP bug 65646
+ }
+ if (defined('CURLOPT_ENCODING')) {
+ curl_setopt($ch, CURLOPT_ENCODING, ''); //Enable all encodings
+ }
$response = curl_exec($ch);
$info = curl_getinfo($ch);
- file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" .
- 'PubSubHubbub ' . ($state ? 'subscribe' : 'unsubscribe') . ' to ' . $url .
- ' with callback ' . $callbackUrl . ': ' . $info['http_code'] . ' ' . $response . "\n", FILE_APPEND);
+ Minz_Log::warning('PubSubHubbub ' . ($state ? 'subscribe' : 'unsubscribe') . ' to ' . $url .
+ ' with callback ' . $callbackUrl . ': ' . $info['http_code'] . ' ' . $response, PSHB_LOG);
if (substr($info['http_code'], 0, 1) == '2') {
return true;
diff --git a/app/Models/LogDAO.php b/app/Models/LogDAO.php
index ab258cd58..5bce466d5 100644
--- a/app/Models/LogDAO.php
+++ b/app/Models/LogDAO.php
@@ -22,9 +22,9 @@ class FreshRSS_LogDAO {
public static function truncate() {
file_put_contents(join_path(DATA_PATH, 'users', Minz_Session::param('currentUser', '_'), 'log.txt'), '');
if (FreshRSS_Auth::hasAccess('admin')) {
- file_put_contents(join_path(DATA_PATH, 'users', '_', 'log.txt'), '');
- file_put_contents(join_path(DATA_PATH, 'users', '_', 'log_api.txt'), '');
- file_put_contents(join_path(DATA_PATH, 'users', '_', 'log_pshb.txt'), '');
+ file_put_contents(ADMIN_LOG, '');
+ file_put_contents(API_LOG, '');
+ file_put_contents(PSHB_LOG, '');
}
}
}
diff --git a/app/Models/Share.php b/app/Models/Share.php
index 86b1b9ed9..7378b30df 100644
--- a/app/Models/Share.php
+++ b/app/Models/Share.php
@@ -21,9 +21,11 @@ class FreshRSS_Share {
}
$help_url = isset($share_options['help']) ? $share_options['help'] : '';
+ $field = isset($share_options['field']) ? $share_options['field'] : null;
self::$list_sharing[$type] = new FreshRSS_Share(
$type, $share_options['url'], $share_options['transform'],
- $share_options['form'], $help_url
+ $share_options['form'], $help_url, $share_options['method'],
+ $field
);
}
@@ -76,6 +78,8 @@ class FreshRSS_Share {
private $base_url = null;
private $title = null;
private $link = null;
+ private $method = 'GET';
+ private $field;
/**
* Create a FreshRSS_Share object.
@@ -86,9 +90,10 @@ class FreshRSS_Share {
* is typically for a centralized service while "advanced" is for
* decentralized ones.
* @param $help_url is an optional url to give help on this option.
+ * @param $method defines the sharing method (GET or POST)
*/
private function __construct($type, $url_transform, $transform,
- $form_type, $help_url = '') {
+ $form_type, $help_url, $method, $field) {
$this->type = $type;
$this->name = _t('gen.share.' . $type);
$this->url_transform = $url_transform;
@@ -103,6 +108,11 @@ class FreshRSS_Share {
$form_type = 'simple';
}
$this->form_type = $form_type;
+ if (!in_array($method, array('GET', 'POST'))) {
+ $method = 'GET';
+ }
+ $this->method = $method;
+ $this->field = $field;
}
/**
@@ -116,6 +126,8 @@ class FreshRSS_Share {
'url' => 'base_url',
'title' => 'title',
'link' => 'link',
+ 'method' => 'method',
+ 'field' => 'field',
);
foreach ($options as $key => $value) {
@@ -133,6 +145,21 @@ class FreshRSS_Share {
}
/**
+ * Return the current method of the share option.
+ */
+ public function method() {
+ return $this->method;
+ }
+
+ /**
+ * Return the current field of the share option. It's null for shares
+ * using the GET method.
+ */
+ public function field() {
+ return $this->field;
+ }
+
+ /**
* Return the current form type of the share option.
*/
public function formType() {
diff --git a/app/actualize_script.php b/app/actualize_script.php
index deaa1bf7c..6f48220a6 100755
--- a/app/actualize_script.php
+++ b/app/actualize_script.php
@@ -1,6 +1,5 @@
<?php
-require(dirname(__FILE__) . '/../constants.php');
-require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader
+require(__DIR__ . '/../cli/_cli.php');
session_cache_limiter('');
ob_implicit_flush(false);
@@ -12,7 +11,6 @@ if (defined('STDOUT')) {
fwrite(STDOUT, 'Starting feed actualization at ' . $begin_date->format('c') . "\n"); //Unbuffered
}
-
// Set the header params ($_GET) to call the FRSS application.
$_GET['c'] = 'feed';
$_GET['a'] = 'actualize';
@@ -20,15 +18,13 @@ $_GET['ajax'] = 1;
$_GET['force'] = true;
$_SERVER['HTTP_HOST'] = '';
-
-$log_file = join_path(USERS_PATH, '_', 'log.txt');
-
-
$app = new FreshRSS();
$system_conf = Minz_Configuration::get('system');
$system_conf->auth_type = 'none'; // avoid necessity to be logged in (not saved!)
-FreshRSS_Context::$isCli = true;
+
+// make sure the PHP setup of the CLI environment is compatible with FreshRSS as well
+performRequirementCheck($system_conf->db['type']);
// Create the list of users to actualize.
// Users are processed in a random order but always start with admin
@@ -39,19 +35,18 @@ if ($system_conf->default_user !== '') {
$users = array_unique($users);
}
-
$limits = $system_conf->limits;
$min_last_activity = time() - $limits['max_inactivity'];
foreach ($users as $user) {
if (($user !== $system_conf->default_user) &&
(FreshRSS_UserDAO::mtime($user) < $min_last_activity)) {
- Minz_Log::notice('FreshRSS skip inactive user ' . $user, $log_file);
+ Minz_Log::notice('FreshRSS skip inactive user ' . $user, ADMIN_LOG);
if (defined('STDOUT')) {
fwrite(STDOUT, 'FreshRSS skip inactive user ' . $user . "\n"); //Unbuffered
}
continue;
}
- Minz_Log::notice('FreshRSS actualize ' . $user, $log_file);
+ Minz_Log::notice('FreshRSS actualize ' . $user, ADMIN_LOG);
if (defined('STDOUT')) {
fwrite(STDOUT, 'Actualize ' . $user . "...\n"); //Unbuffered
}
@@ -66,16 +61,14 @@ foreach ($users as $user) {
if (!invalidateHttpCache()) {
- Minz_Log::notice('FreshRSS write access problem in ' . join_path(USERS_PATH, $user, 'log.txt'),
- $log_file);
+ Minz_Log::warning('FreshRSS write access problem in ' . join_path(USERS_PATH, $user, 'log.txt'), ADMIN_LOG);
if (defined('STDERR')) {
fwrite(STDERR, 'Write access problem in ' . join_path(USERS_PATH, $user, 'log.txt') . "\n");
}
}
}
-
-Minz_Log::notice('FreshRSS actualize done.', $log_file);
+Minz_Log::notice('FreshRSS actualize done.', ADMIN_LOG);
if (defined('STDOUT')) {
fwrite(STDOUT, 'Done.' . "\n");
$end_date = date_create('now');
diff --git a/app/i18n/cz/admin.php b/app/i18n/cz/admin.php
index 63cee3cca..dbfebd4c9 100644
--- a/app/i18n/cz/admin.php
+++ b/app/i18n/cz/admin.php
@@ -155,6 +155,13 @@ return array(
'help' => '0 znamená žádná omezení účtu',
'number' => 'Maximální počet účtů',
),
+ 'community' => 'Available community extensions', // @todo translate
+ 'name' => 'Name', // @todo translate
+ 'version' => 'Version', // @todo translate
+ 'description' => 'Description', // @todo translate
+ 'author' => 'Author', // @todo translate
+ 'latest' => 'Installed', // @todo translate
+ 'update' => 'Update available', // @todo translate
),
'update' => array(
'_' => 'Aktualizace systému',
diff --git a/app/i18n/cz/gen.php b/app/i18n/cz/gen.php
index a9c7dc875..fcddf452c 100644
--- a/app/i18n/cz/gen.php
+++ b/app/i18n/cz/gen.php
@@ -13,8 +13,8 @@ return array(
'filter' => 'Filtrovat',
'import' => 'Import',
'manage' => 'Spravovat',
- 'mark_read' => 'Označit jako přečtené',
'mark_favorite' => 'Označit jako oblíbené',
+ 'mark_read' => 'Označit jako přečtené',
'remove' => 'Odstranit',
'see_website' => 'Navštívit WWW stránku',
'submit' => 'Odeslat',
@@ -79,8 +79,8 @@ return array(
'last_year' => 'Minulý rok',
'mar' => 'bře',
'march' => 'Bře',
- 'may_' => 'Kvě',
'may' => 'Květen',
+ 'may_' => 'Kvě',
'mon' => 'Po',
'month' => 'měsíce',
'nov' => 'lis',
@@ -119,6 +119,7 @@ return array(
'en' => 'English',
'es' => 'Español',
'fr' => 'Français',
+ 'he' => 'עברית',
'it' => 'Italiano',
'kr' => '한국어',
'nl' => 'Nederlands',
@@ -143,7 +144,7 @@ return array(
'sharing' => 'Sdílení',
'shortcuts' => 'Zkratky',
'stats' => 'Statistika',
- 'system' => 'System configuration',// @todo translate
+ 'system' => 'System configuration', // @todo translate
'update' => 'Aktualizace',
'user_management' => 'Správa uživatelů',
'user_profile' => 'Profil',
@@ -158,20 +159,21 @@ return array(
'previous' => 'Předchozí',
),
'share' => array(
+ 'Known' => 'Known based sites',
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Email',
'facebook' => 'Facebook',
'g+' => 'Google+',
+ 'gnusocial' => 'GNU social',
+ 'jdh' => 'Journal du hacker',
+ 'mastodon' => 'Mastodon',
'movim' => 'Movim',
'print' => 'Tisk',
'shaarli' => 'Shaarli',
'twitter' => 'Twitter',
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
- 'jdh' => 'Journal du hacker',
- 'Known' => 'Known based sites',
- 'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => 'Upozornění!',
diff --git a/app/i18n/cz/sub.php b/app/i18n/cz/sub.php
index 09b23d222..807c249d3 100644
--- a/app/i18n/cz/sub.php
+++ b/app/i18n/cz/sub.php
@@ -1,6 +1,10 @@
<?php
return array(
+ 'api' => array(
+ 'documentation' => 'Copy the following URL to use it within an external tool.',// TODO
+ 'title' => 'API',// TODO
+ ),
'bookmarklet' => array(
'documentation' => 'Drag this button to your bookmarks toolbar or right-click it and choose "Bookmark This Link". Then click "Subscribe" button in any page you want to subscribe to.',// TODO
'label' => 'Subscribe',// TODO
diff --git a/app/i18n/de/admin.php b/app/i18n/de/admin.php
index 8fc43a7bb..bb2c9352d 100644
--- a/app/i18n/de/admin.php
+++ b/app/i18n/de/admin.php
@@ -112,6 +112,13 @@ return array(
),
'title' => 'Erweiterungen',
'user' => 'Benutzer-Erweiterungen',
+ 'community' => 'Verfügbare Community Erweiterungen',
+ 'name' => 'Name',
+ 'version' => 'Version',
+ 'description' => 'Beschreibungen',
+ 'author' => 'Autor',
+ 'latest' => 'Installiert',
+ 'update' => 'Update verfügbar',
),
'stats' => array(
'_' => 'Statistiken',
@@ -146,11 +153,11 @@ return array(
'top_feed' => 'Top 10-Feeds',
),
'system' => array(
- '_' => 'System configuration', // @todo translate
- 'auto-update-url' => 'Auto-update server URL', // @todo translate
- 'instance-name' => 'Instance name', // @todo translate
- 'max-categories' => 'Categories per user limit', // @todo translate
- 'max-feeds' => 'Feeds per user limit', // @todo translate
+ '_' => 'Systemeinstellungen',
+ 'auto-update-url' => 'Auto-update URL',
+ 'instance-name' => 'Dein Reader Name',
+ 'max-categories' => 'Anzahl erlaubter Kategorien pro Benutzer',
+ 'max-feeds' => 'Anzahl erlaubter Feeds pro Benutzer',
'registration' => array(
'help' => '0 meint, dass es kein Account Limit gibt',
'number' => 'Maximale Anzahl von Accounts',
diff --git a/app/i18n/de/gen.php b/app/i18n/de/gen.php
index 43d0a2c05..bed49a4a4 100644
--- a/app/i18n/de/gen.php
+++ b/app/i18n/de/gen.php
@@ -13,8 +13,8 @@ return array(
'filter' => 'Filtern',
'import' => 'Importieren',
'manage' => 'Verwalten',
- 'mark_read' => 'Als gelesen markieren',
'mark_favorite' => 'Als Favorit markieren',
+ 'mark_read' => 'Als gelesen markieren',
'remove' => 'Entfernen',
'see_website' => 'Webseite ansehen',
'submit' => 'Abschicken',
@@ -79,8 +79,8 @@ return array(
'last_year' => 'Letztes Jahr',
'mar' => 'Mär',
'march' => 'März',
- 'may_' => 'Mai',
'may' => 'Mai',
+ 'may_' => 'Mai',
'mon' => 'Mo',
'month' => 'Monat(en)',
'nov' => 'Nov',
@@ -119,6 +119,7 @@ return array(
'en' => 'English',
'es' => 'Español',
'fr' => 'Français',
+ 'he' => 'עברית',
'it' => 'Italiano',
'kr' => '한국어',
'nl' => 'Nederlands',
@@ -143,7 +144,7 @@ return array(
'sharing' => 'Teilen',
'shortcuts' => 'Tastaturkürzel',
'stats' => 'Statistiken',
- 'system' => 'System configuration',// @todo translate
+ 'system' => 'Systemeinstellungen',
'update' => 'Aktualisieren',
'user_management' => 'Benutzer verwalten',
'user_profile' => 'Profil',
@@ -163,15 +164,15 @@ return array(
'email' => 'E-Mail',
'facebook' => 'Facebook',
'g+' => 'Google+',
+ 'gnusocial' => 'GNU social',
+ 'jdh' => 'Journal du hacker',
+ 'mastodon' => 'Mastodon',
'movim' => 'Movim',
'print' => 'Drucken',
'shaarli' => 'Shaarli',
'twitter' => 'Twitter',
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
- 'jdh' => 'Journal du hacker',
- 'Known' => 'Known based sites',
- 'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => 'Achtung!',
diff --git a/app/i18n/de/sub.php b/app/i18n/de/sub.php
index c16d09db1..4ffef4302 100644
--- a/app/i18n/de/sub.php
+++ b/app/i18n/de/sub.php
@@ -1,6 +1,10 @@
<?php
return array(
+ 'api' => array(
+ 'documentation' => 'Copy the following URL to use it within an external tool.',// TODO
+ 'title' => 'API',// TODO
+ ),
'bookmarklet' => array(
'documentation' => 'Drag this button to your bookmarks toolbar or right-click it and choose "Bookmark This Link". Then click "Subscribe" button in any page you want to subscribe to.',// TODO
'label' => 'Subscribe',// TODO
diff --git a/app/i18n/en/admin.php b/app/i18n/en/admin.php
index 707627782..187b66a8c 100644
--- a/app/i18n/en/admin.php
+++ b/app/i18n/en/admin.php
@@ -112,6 +112,13 @@ return array(
),
'title' => 'Extensions',
'user' => 'User extensions',
+ 'community' => 'Available community extensions',
+ 'name' => 'Name',
+ 'version' => 'Version',
+ 'description' => 'Description',
+ 'author' => 'Author',
+ 'latest' => 'Installed',
+ 'update' => 'Update available',
),
'stats' => array(
'_' => 'Statistics',
diff --git a/app/i18n/en/gen.php b/app/i18n/en/gen.php
index 095eb17d3..ac48e3486 100644
--- a/app/i18n/en/gen.php
+++ b/app/i18n/en/gen.php
@@ -13,8 +13,8 @@ return array(
'filter' => 'Filter',
'import' => 'Import',
'manage' => 'Manage',
- 'mark_read' => 'Mark as read',
'mark_favorite' => 'Mark as favourite',
+ 'mark_read' => 'Mark as read',
'remove' => 'Remove',
'see_website' => 'See website',
'submit' => 'Submit',
@@ -79,8 +79,8 @@ return array(
'last_year' => 'Last year',
'mar' => 'Mar.',
'march' => 'March',
- 'may_' => 'May',
'may' => 'May',
+ 'may_' => 'May',
'mon' => 'Mon',
'month' => 'months',
'nov' => 'Nov.',
@@ -119,6 +119,7 @@ return array(
'en' => 'English',
'es' => 'Español',
'fr' => 'Français',
+ 'he' => 'עברית',
'it' => 'Italiano',
'kr' => '한국어',
'nl' => 'Nederlands',
@@ -163,15 +164,16 @@ return array(
'email' => 'Email',
'facebook' => 'Facebook',
'g+' => 'Google+',
+ 'gnusocial' => 'GNU social',
+ 'jdh' => 'Journal du hacker',
+ 'Known' => 'Known based sites',
+ 'mastodon' => 'Mastodon',
'movim' => 'Movim',
'print' => 'Print',
'shaarli' => 'Shaarli',
'twitter' => 'Twitter',
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
- 'jdh' => 'Journal du hacker',
- 'Known' => 'Known based sites',
- 'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => 'Warning!',
diff --git a/app/i18n/en/sub.php b/app/i18n/en/sub.php
index ca696280a..47b15ae7a 100644
--- a/app/i18n/en/sub.php
+++ b/app/i18n/en/sub.php
@@ -1,6 +1,10 @@
<?php
return array(
+ 'api' => array(
+ 'documentation' => 'Copy the following URL to use it within an external tool.',
+ 'title' => 'API',
+ ),
'bookmarklet' => array(
'documentation' => 'Drag this button to your bookmarks toolbar or right-click it and choose "Bookmark This Link". Then click "Subscribe" button in any page you want to subscribe to.',
'label' => 'Subscribe',
diff --git a/app/i18n/es/admin.php b/app/i18n/es/admin.php
index c9d9368eb..93b1e6e5c 100755
--- a/app/i18n/es/admin.php
+++ b/app/i18n/es/admin.php
@@ -112,6 +112,13 @@ return array(
),
'title' => 'Extensiones',
'user' => 'Extensiones de usuario',
+ 'community' => 'Available community extensions', // @todo translate
+ 'name' => 'Name', // @todo translate
+ 'version' => 'Version', // @todo translate
+ 'description' => 'Description', // @todo translate
+ 'author' => 'Author', // @todo translate
+ 'latest' => 'Installed', // @todo translate
+ 'update' => 'Update available', // @todo translate
),
'stats' => array(
'_' => 'Estadísticas',
diff --git a/app/i18n/es/gen.php b/app/i18n/es/gen.php
index 68fdaf429..32fbcdaee 100755
--- a/app/i18n/es/gen.php
+++ b/app/i18n/es/gen.php
@@ -13,8 +13,8 @@ return array(
'filter' => 'Filtrar',
'import' => 'Importar',
'manage' => 'Administrar',
- 'mark_read' => 'Marcar como leído',
'mark_favorite' => 'Marcar como favorita',
+ 'mark_read' => 'Marcar como leído',
'remove' => 'Borrar',
'see_website' => 'Ver web',
'submit' => 'Enviar',
@@ -79,8 +79,8 @@ return array(
'last_year' => 'Año pasado',
'mar' => 'mar',
'march' => 'marzo',
- 'may_' => 'may',
'may' => 'mayo',
+ 'may_' => 'may',
'mon' => 'Lun',
'month' => 'meses',
'nov' => 'nov',
@@ -119,6 +119,7 @@ return array(
'en' => 'English',
'es' => 'Español',
'fr' => 'Français',
+ 'he' => 'עברית',
'it' => 'Italiano',
'kr' => '한국어',
'nl' => 'Nederlands',
@@ -158,20 +159,21 @@ return array(
'previous' => 'Anterior',
),
'share' => array(
+ 'Known' => 'Known based sites',
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Email',
'facebook' => 'Facebook',
'g+' => 'Google+',
+ 'gnusocial' => 'GNU social',
+ 'jdh' => 'Journal du hacker',
+ 'mastodon' => 'Mastodon',
'movim' => 'Movim',
'print' => 'Print',
'shaarli' => 'Shaarli',
'twitter' => 'Twitter',
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
- 'jdh' => 'Journal du hacker',
- 'Known' => 'Known based sites',
- 'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => '¡Aviso!',
diff --git a/app/i18n/es/sub.php b/app/i18n/es/sub.php
index c18f0b786..72eb06eb7 100755
--- a/app/i18n/es/sub.php
+++ b/app/i18n/es/sub.php
@@ -1,6 +1,10 @@
<?php
return array(
+ 'api' => array(
+ 'documentation' => 'Copy the following URL to use it within an external tool.',// TODO
+ 'title' => 'API',// TODO
+ ),
'category' => array(
'_' => 'Categoría',
'add' => 'Añadir a la categoría',
diff --git a/app/i18n/fr/admin.php b/app/i18n/fr/admin.php
index 9a13ecc21..b2bc48209 100644
--- a/app/i18n/fr/admin.php
+++ b/app/i18n/fr/admin.php
@@ -103,15 +103,22 @@ return array(
),
'extensions' => array(
'disabled' => 'Désactivée',
- 'empty_list' => 'Il n’y a aucune extension installée.',
+ 'empty_list' => 'Aucune extension installée',
'enabled' => 'Activée',
- 'no_configure_view' => 'Cette extension ne peut pas être configurée.',
+ 'no_configure_view' => 'Cette extension n’a pas à être configurée',
'system' => array(
'_' => 'Extensions système',
- 'no_rights' => 'Extension système (vous n’avez aucun droit dessus)',
+ 'no_rights' => 'Extensions système (contrôlées par l’administrateur)',
),
'title' => 'Extensions',
'user' => 'Extensions utilisateur',
+ 'community' => 'Extensions utilisateur disponibles',
+ 'name' => 'Nom',
+ 'version' => 'Version',
+ 'description' => 'Description',
+ 'author' => 'Auteur',
+ 'latest' => 'Installée',
+ 'update' => 'Mise à jour disponible',
),
'stats' => array(
'_' => 'Statistiques',
diff --git a/app/i18n/fr/gen.php b/app/i18n/fr/gen.php
index 16935c3c4..61a24602a 100644
--- a/app/i18n/fr/gen.php
+++ b/app/i18n/fr/gen.php
@@ -13,8 +13,8 @@ return array(
'filter' => 'Filtrer',
'import' => 'Importer',
'manage' => 'Gérer',
- 'mark_read' => 'Marquer comme lu',
'mark_favorite' => 'Mettre en favori',
+ 'mark_read' => 'Marquer comme lu',
'remove' => 'Supprimer',
'see_website' => 'Voir le site',
'submit' => 'Valider',
@@ -79,8 +79,8 @@ return array(
'last_year' => 'Depuis l’année dernière',
'mar' => 'mars',
'march' => 'mars',
- 'may_' => 'mai',
'may' => 'mai',
+ 'may_' => 'mai',
'mon' => 'lun.',
'month' => 'mois',
'nov' => 'nov.',
@@ -119,6 +119,7 @@ return array(
'en' => 'English',
'es' => 'Español',
'fr' => 'Français',
+ 'he' => 'עברית',
'it' => 'Italiano',
'kr' => '한국어',
'nl' => 'Nederlands',
@@ -158,20 +159,21 @@ return array(
'previous' => 'Précédent',
),
'share' => array(
+ 'Known' => 'Sites basés sur Known',
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Courriel',
'facebook' => 'Facebook',
'g+' => 'Google+',
+ 'gnusocial' => 'GNU social',
+ 'jdh' => 'Journal du hacker',
+ 'mastodon' => 'Mastodon',
'movim' => 'Movim',
'print' => 'Imprimer',
'shaarli' => 'Shaarli',
'twitter' => 'Twitter',
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
- 'jdh' => 'Journal du hacker',
- 'Known' => 'Sites basés sur Known',
- 'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => 'Attention !',
diff --git a/app/i18n/fr/sub.php b/app/i18n/fr/sub.php
index 9135b2b7c..607863c8f 100644
--- a/app/i18n/fr/sub.php
+++ b/app/i18n/fr/sub.php
@@ -1,6 +1,10 @@
<?php
return array(
+ 'api' => array(
+ 'documentation' => 'Copier l’URL suivante dans l’outil qui utilisera l’API.',
+ 'title' => 'API',
+ ),
'bookmarklet' => array(
'documentation' => 'Glisser ce bouton dans la barre des favoris ou cliquer droit dessus et choisir "Enregistrer ce lien". Ensuite, cliquer sur le bouton "S’abonner" sur les pages auxquelles vous voulez vous abonner.',
'label' => 'S’abonner',
diff --git a/app/i18n/he/admin.php b/app/i18n/he/admin.php
new file mode 100644
index 000000000..b7c83c828
--- /dev/null
+++ b/app/i18n/he/admin.php
@@ -0,0 +1,188 @@
+<?php
+
+return array(
+ 'auth' => array(
+ 'allow_anonymous' => 'הרשאה לאנונימיים לקרוא את מאמרי משתמש ברירת המחדל (%s)',
+ 'allow_anonymous_refresh' => 'הרשאה לאנונימיים לרענן את רשימת המאמרים',
+ 'api_enabled' => 'הרשאת גישה ל <abbr>API</abbr> <small>(נדרש ליישומים סלולריים)</small>',
+ 'form' => 'טופס אינטרנטי (מסורתי, דורש JavaScript)',
+ 'http' => 'HTTP (למשתמשים מתקדמים עם HTTPS)',
+ 'none' => 'ללא (מסוכן)',
+ 'title' => 'Authentication', // @todo
+ 'title_reset' => 'איפוס אימות',
+ 'token' => 'מחרוזת אימות',
+ 'token_help' => 'Allows to access RSS output of the default user without authentication:', // @todo
+ 'type' => 'שיטת אימות',
+ 'unsafe_autologin' => 'הרשאה להתחברות אוטומטית בפורמט: ',
+ ),
+ 'check_install' => array(
+ 'cache' => array(
+ 'nok' => 'יש לבדוק את ההרשאות בתיקייה <em>%s</em>. שרת הHTTP חייב להיות בעל הרשאות כתיבה.',
+ 'ok' => 'ההרשאות בתיקיית המטמון תקינות',
+ ),
+ 'categories' => array(
+ 'nok' => 'Category table is bad configured.', // @todo
+ 'ok' => 'Category table is ok.', // @todo
+ ),
+ 'connection' => array(
+ 'nok' => 'Connection to the database cannot being established.', // @todo
+ 'ok' => 'Connection to the database is ok.', // @todo
+ ),
+ 'ctype' => array(
+ 'nok' => 'הספרייה הנדרשת ל character type checking (php-ctype) אינה מותקנת',
+ 'ok' => 'הספרייה הנדרשת ל character type checking (ctype) מותקנת',
+ ),
+ 'curl' => array(
+ 'nok' => 'בURL לא מותקן (php5-curl package)',
+ 'ok' => 'You have cURL extension.', // @todo
+ ),
+ 'data' => array(
+ 'nok' => 'יש לבדוק את ההרשאות בתיקייה <em>%s</em>. שרת הHTTP חייב להיות בעל הרשאות כתיבה.',
+ 'ok' => 'ההרשאות בתיקיית הדאטא תקינות',
+ ),
+ 'database' => 'Database installation', // @todo
+ 'dom' => array(
+ 'nok' => 'הספרייה הנדרשת לסיור ב DOM אינה מותקנת (php-xml package)',
+ 'ok' => 'הספרייה הנדרשת לסיור ב DOM מותקנת',
+ ),
+ 'entries' => array(
+ 'nok' => 'Entry table is bad configured.', // @todo
+ 'ok' => 'Entry table is ok.', // @todo
+ ),
+ 'favicons' => array(
+ 'nok' => 'Check permissions on <em>./data/favicons</em> directory. HTTP server must have rights to write into', // @todo
+ 'ok' => 'ההרשאות בתיקיית הfavicons תקינות',
+ ),
+ 'feeds' => array(
+ 'nok' => 'Feed table is bad configured.', // @todo
+ 'ok' => 'Feed table is ok.', // @todo
+ ),
+ 'fileinfo' => array(
+ 'nok' => 'Cannot find the PHP fileinfo library (fileinfo package).', // @todo
+ 'ok' => 'You have the fileinfo library.', // @todo
+ ),
+ 'files' => 'File installation', // @todo
+ 'json' => array(
+ 'nok' => 'You lack JSON (php5-json package).', // @todo
+ 'ok' => 'You have JSON extension.', // @todo
+ ),
+ 'minz' => array(
+ 'nok' => 'You lack the Minz framework.', // @todo
+ 'ok' => 'יש לכם את תשתית Minz',
+ ),
+ 'pcre' => array(
+ 'nok' => 'הספרייה הנדרשת לביטויים רגולריים אינה מותקנת (php-pcre)',
+ 'ok' => 'הספרייה הנדרשת לביטויים רגולריים מותקנת (PCRE)',
+ ),
+ 'pdo' => array(
+ 'nok' => 'PDO אינו מותקן או שאחד ממנהלי ההתקנים שלו חסר (pdo_mysql, pdo_sqlite)',
+ 'ok' => 'PDO מותקן ולפחות אחד ממנהלי ההתקן הנתמכים מותקן (pdo_mysql, pdo_sqlite)',
+ ),
+ 'php' => array(
+ '_' => 'PHP installation', // @todo
+ 'nok' => 'גירסת PHP שלכם היא %s אך FreshRSS דורש לפחות את גירסה %s',
+ 'ok' => 'גירסת PHP שלכם היא %s, שתואמת ל FreshRSS',
+ ),
+ 'tables' => array(
+ 'nok' => 'There is one or more lacking tables in the database.', // @todo
+ 'ok' => 'Tables are existing in the database.', // @todo
+ ),
+ 'title' => 'Installation checking', // @todo
+ 'tokens' => array(
+ 'nok' => 'Check permissions on <em>./data/tokens</em> directory. HTTP server must have rights to write into', // @todo
+ 'ok' => 'Permissions on tokens directory are good.', // @todo
+ ),
+ 'users' => array(
+ 'nok' => 'Check permissions on <em>./data/users</em> directory. HTTP server must have rights to write into', // @todo
+ 'ok' => 'Permissions on users directory are good.', // @todo
+ ),
+ 'zip' => array(
+ 'nok' => 'You lack ZIP extension (php5-zip package).', // @todo
+ 'ok' => 'You have ZIP extension.', // @todo
+ ),
+ ),
+ 'extensions' => array(
+ 'disabled' => 'Disabled', // @todo
+ 'empty_list' => 'There is no installed extension', // @todo
+ 'enabled' => 'Enabled', // @todo
+ 'no_configure_view' => 'This extension cannot be configured.', // @todo
+ 'system' => array(
+ '_' => 'System extensions', // @todo
+ 'no_rights' => 'System extension (you have no rights on it)', // @todo
+ ),
+ 'title' => 'Extensions', // @todo
+ 'user' => 'User extensions', // @todo
+ 'community' => 'Available community extensions', // @todo
+ 'name' => 'Name', // @todo
+ 'version' => 'Version', // @todo
+ 'description' => 'Description', // @todo
+ 'author' => 'Author', // @todo
+ 'latest' => 'Installed', // @todo
+ 'update' => 'Update available', // @todo
+ ),
+ 'stats' => array(
+ '_' => 'סטטיסטיקות',
+ 'all_feeds' => 'כל ההזנות',
+ 'category' => 'קטגוריה',
+ 'entry_count' => 'סכום המאמרים',
+ 'entry_per_category' => 'מאמרים על פי קטגוריה',
+ 'entry_per_day' => 'מספר מאמרים ליום (30 ימים אחרונים)',
+ 'entry_per_day_of_week' => 'Per day of week (average: %.2f messages)', // @todo
+ 'entry_per_hour' => 'Per hour (average: %.2f messages)', // @todo
+ 'entry_per_month' => 'Per month (average: %.2f messages)', // @todo
+ 'entry_repartition' => 'חלוקת המאמרים',
+ 'feed' => 'הזנה',
+ 'feed_per_category' => 'הזנות על פי קטגוריה',
+ 'idle' => 'הזנות שלא עודכנו',
+ 'main' => 'סטטיסטיקות ראשיות',
+ 'main_stream' => 'הזנה ראשית',
+ 'menu' => array(
+ 'idle' => 'הזנות שלא עודכנו',
+ 'main' => 'סטטיסטיקות ראשיות',
+ 'repartition' => 'חלוקת המאמרים',
+ ),
+ 'no_idle' => 'אין הזנות מובטלות!',
+ 'number_entries' => '%d מאמרים',
+ 'percent_of_total' => '%% מסך הכל',
+ 'repartition' => 'חלוקת המאמרים',
+ 'status_favorites' => 'מועדפים',
+ 'status_read' => 'נקרא',
+ 'status_total' => 'סך הכל',
+ 'status_unread' => 'לא נקרא',
+ 'title' => 'סטטיסטיקות',
+ 'top_feed' => 'עשרת ההזנות המובילות',
+ ),
+ 'system' => array(
+ '_' => 'System configuration', // @todo
+ 'auto-update-url' => 'Auto-update server URL', // @todo
+ 'instance-name' => 'Instance name', // @todo
+ 'max-categories' => 'Categories per user limit', // @todo
+ 'max-feeds' => 'Feeds per user limit', // @todo
+ 'registration' => array(
+ 'help' => '0 means that there is no account limit', // @todo
+ 'number' => 'Max number of accounts', // @todo
+ ),
+ ),
+ 'update' => array(
+ '_' => 'מערכת העדכון',
+ 'apply' => 'החלת העדכון',
+ 'check' => 'בדיקת עדכונים חדשים',
+ 'current_version' => 'Your current version of FreshRSS is the %s.', // @todo
+ 'last' => 'תאריך בדיקה אחרון: %s',
+ 'none' => 'אין עדכון להחלה',
+ 'title' => 'מערכת העדכון',
+ ),
+ 'user' => array(
+ 'articles_and_size' => '%s articles (%s)', // @todo
+ 'create' => 'יצירת משתמש חדש',
+ 'language' => 'שפה',
+ 'number' => 'There is %d account created', // @todo
+ 'numbers' => 'There are %d accounts created', // @todo
+ 'password_form' => 'סיסמה<br /><small>(לשימוש בטפוס ההרשמה)</small>',
+ 'password_format' => 'At least 7 characters', // @todo
+ 'title' => 'Manage users', // @todo
+ 'user_list' => 'רשימת משתמשים',
+ 'username' => 'שם משתמש',
+ 'users' => 'משתמשים',
+ ),
+);
diff --git a/app/i18n/he/conf.php b/app/i18n/he/conf.php
new file mode 100644
index 000000000..ba9985d45
--- /dev/null
+++ b/app/i18n/he/conf.php
@@ -0,0 +1,171 @@
+<?php
+
+return array(
+ 'archiving' => array(
+ '_' => 'ארכוב',
+ 'advanced' => 'מתקדם',
+ 'delete_after' => 'מחיקת מאמרים לאחר',
+ 'help' => 'אפשרויות נוספות זמינות בזרמים ספציפיים',
+ 'keep_history_by_feed' => 'Minimum number of articles to keep by feed', // @todo
+ 'optimize' => 'מיטוב בסיס הנתונים',
+ 'optimize_help' => 'ביצוע לעיתים קרובות על מנת למטב את בסיס הנתונים',
+ 'purge_now' => 'ניקוי עכשיו',
+ 'title' => 'ארכוב',
+ 'ttl' => 'אין לרענן אוטומטית יותר מ',
+ ),
+ 'display' => array(
+ '_' => 'תצוגה',
+ 'icon' => array(
+ 'bottom_line' => 'שורה תחתונה',
+ 'entry' => 'סמלילי מאמרים',
+ 'publication_date' => 'תאריך הפרסום',
+ 'related_tags' => 'תגיות קשורות',
+ 'sharing' => 'שיתוף',
+ 'top_line' => 'שורה עליונה',
+ ),
+ 'language' => 'שפה',
+ 'notif_html5' => array(
+ 'seconds' => 'שניות (0 משמעותה ללא פג תוקף)',
+ 'timeout' => 'HTML5 התראה פג תוקף',
+ ),
+ 'theme' => 'ערכת נושא',
+ 'title' => 'תצוגה',
+ 'width' => array(
+ 'content' => 'רוחב התוכן',
+ 'large' => 'גדול',
+ 'medium' => 'בינוני',
+ 'no_limit' => 'ללא הגבלה',
+ 'thin' => 'צר',
+ ),
+ ),
+ 'query' => array(
+ '_' => 'שאילתות',
+ 'deprecated' => 'שאילתה זו אינה בתוקף יותר, הפיד או הקטגוריה לייחוס נמחקו.',
+ 'filter' => 'מסננים בשימוש:',
+ 'get_all' => 'הצגת כל המאמרים',
+ 'get_category' => 'הצגת קטגוריה "%s"',
+ 'get_favorite' => 'הצגת מאמרים מועדפים',
+ 'get_feed' => 'הצגת הזנה %s',
+ 'no_filter' => 'ללא סינון',
+ 'none' => 'אף שאילתה לא נוצרה עדיין.',
+ 'number' => 'שאילתה מספר °%d',
+ 'order_asc' => 'הצגת מאמרים ישנים בראש',
+ 'order_desc' => 'הצגת מאמרים חדשים בראש',
+ 'search' => 'חיפוש "%s"',
+ 'state_0' => 'הצגת כל המאמרים',
+ 'state_1' => 'הצגת מאמרים שנקראו',
+ 'state_2' => 'הצגת מאמרים שלא נקראו',
+ 'state_3' => 'הצגת כל המאמרים',
+ 'state_4' => 'הצגת מאמרים מועדפים',
+ 'state_5' => 'הצגת מאמרים מועדפים שנקראו',
+ 'state_6' => 'הצגת מאמרים מועדפים שלא נקראו',
+ 'state_7' => 'הצגת מאמרים מועדפים',
+ 'state_8' => 'הצגת מאמרים שאינם מועדפים',
+ 'state_9' => 'הצגת מאמרים שנקראו ואינם מועדפים',
+ 'state_10' => 'הצגת מאמרים שלא נקראו ואינם מועדפים',
+ 'state_11' => 'הצגת מאמרים לא מועדפים',
+ 'state_12' => 'הצגת כל המאמרים',
+ 'state_13' => 'הצגת מאמרים שנקראו',
+ 'state_14' => 'הצגת מאמרים שלא נקראו',
+ 'state_15' => 'הצגת כל המאמרים',
+ 'title' => 'שאילתות',
+ ),
+ 'profile' => array(
+ '_' => 'Profile management', // @todo
+ 'email_persona' => 'כתובת דואר אלקטרוני להרשמה<br /><small>(לצורך <a href="https://persona.org/" rel="external">מוזילה פרסונה</a>)</small>',
+ 'password_api' => 'סיסמת API<br /><small>(לדוגמה ליישומים סלולריים)</small>',
+ 'password_form' => 'סיסמה<br /><small>(לשימוש בטפוס ההרשמה)</small>',
+ 'password_format' => 'At least 7 characters', // @todo
+ 'title' => 'Profile', // @todo
+ ),
+ 'reading' => array(
+ '_' => 'קריאה',
+ 'after_onread' => 'לאחר “סימון הכל כנקרא”,',
+ 'articles_per_page' => 'מספר המאמרים בעמוד',
+ 'auto_load_more' => 'טעינת המאמר הבא סוף העמוד',
+ 'auto_remove_article' => 'Hide articles after reading', // @todo
+ 'mark_updated_article_unread' => 'Mark updated articles as unread', // @todo
+ 'confirm_enabled' => 'הצגת דו-שיח לאישור “סימון הכל כנקרא” ',
+ 'display_articles_unfolded' => 'הצגת מאמרים בשלמותם כברירת מחדל',
+ 'display_categories_unfolded' => 'הצגת קטגוריות מקופלות כברירת מחדל',
+ 'hide_read_feeds' => 'הסתרת קטגוריות &amp; הזנות ללא מאמרים שלא נקראו (לא עובד יחד עם “הצגת כל המאמרים”)',
+ 'img_with_lazyload' => 'שימוש ב "טעינה עצלה" על מנת לטעון תמונות',
+ 'sides_close_article' => 'Clicking outside of article text area closes the article', // @todo
+ 'jump_next' => 'קפיצה לפריט הבא שלא נקרא (הזנה או קטגוריה)',
+ 'number_divided_when_reader' => 'חלוקה ב2 במצב קריאה.',
+ 'read' => array(
+ 'article_open_on_website' => 'כאשר מאמר נפתח באתר המקורי',
+ 'article_viewed' => 'כאשר מאמר נצפה',
+ 'scroll' => 'כאשר גוללים',
+ 'upon_reception' => 'כאשר המאמר מתקבל',
+ 'when' => 'סימון מאמרים כנקראו…',
+ ),
+ 'show' => array(
+ '_' => 'מאמרים להצגה',
+ 'adaptive' => 'תצוגה מתעדכנת',
+ 'all_articles' => 'הצגת כל המאמרים',
+ 'unread' => 'הצגת מאמרים שלא נקראו בלבד',
+ ),
+ 'sort' => array(
+ '_' => 'סדר המיון',
+ 'newer_first' => 'חדשים בראש',
+ 'older_first' => 'ישנים יותר בראש',
+ ),
+ 'sticky_post' => 'הצמדת המאמר לחלק העליון כאשר הוא פתוח',
+ 'title' => 'קריאה',
+ 'view' => array(
+ 'default' => 'תצוגת ברירת המחדל',
+ 'global' => 'תצוגה גלובלית',
+ 'normal' => 'תצוגה רגילה',
+ 'reader' => 'תצוגת קריאה',
+ ),
+ ),
+ 'sharing' => array(
+ '_' => 'שיתוף',
+ 'blogotext' => 'Blogotext',
+ 'diaspora' => 'Diaspora*',
+ 'email' => 'דואר אלקטרוני',
+ 'facebook' => 'Facebook',
+ 'g+' => 'Google+',
+ 'more_information' => 'מידע נוסף',
+ 'print' => 'הדפסה',
+ 'shaarli' => 'Shaarli',
+ 'share_name' => 'שיתוף שם לתצוגה',
+ 'share_url' => 'לשימוש שתפו URL',
+ 'title' => 'שיתוף',
+ 'twitter' => 'Twitter',
+ 'wallabag' => 'wallabag',
+ ),
+ 'shortcut' => array(
+ '_' => 'קיצורי דרך',
+ 'article_action' => 'פעולות על מאמרים',
+ 'auto_share' => 'שיתוף',
+ 'auto_share_help' => 'אם יש רק מצב שיתוף אחד, הוא מופעל. אחרת המצבים נבחרים על בסיס המספר שלהם.',
+ 'close_dropdown' => 'Close menus', // @todo
+ 'collapse_article' => 'כיווץ',
+ 'first_article' => 'דילוג למאמר הראשון',
+ 'focus_search' => 'גישה לתיבת החיפוש',
+ 'help' => 'הצגת התיעוד',
+ 'javascript' => 'חובה להפעיל JavaScript על מנת לעשות שימוש בקיצורי דרך',
+ 'last_article' => 'דילוג למאמר האחרון',
+ 'load_more' => 'טעינת מאמרים נוספים',
+ 'mark_read' => 'סימון כנקרא',
+ 'mark_favorite' => 'סימון כמועדף',
+ 'navigation' => 'ניווט',
+ 'navigation_help' => 'בעזרת מקש השיפט קיצורי דרך חלים על הזנות .<br/>עם מקש האלט הם חלים על קטגוריות.',
+ 'next_article' => 'דילוג למאמר הבא',
+ 'other_action' => 'פעולות אחרות',
+ 'previous_article' => 'דילוג למאמר הקודם',
+ 'see_on_website' => 'ראו את המקור באתר',
+ 'shift_for_all_read' => '+ <code>shift</code> על מנת לסמן את כל המאמרים כנקראו',
+ 'title' => 'קיצורי דרך',
+ 'user_filter' => 'גישה למססנים',
+ 'user_filter_help' => 'אם יש רק מזנן אחד הוא יהיה בשימוש. אחרת המסננים ישמשו על בסיס המספר שלהם.',
+ ),
+ 'user' => array(
+ 'articles_and_size' => '%s articles (%s)', // @todo
+ 'current' => 'משתמש נוכחי',
+ 'is_admin' => 'מנהל',
+ 'users' => 'משתמשים',
+ ),
+);
diff --git a/app/i18n/he/feedback.php b/app/i18n/he/feedback.php
new file mode 100644
index 000000000..9a72b2ba0
--- /dev/null
+++ b/app/i18n/he/feedback.php
@@ -0,0 +1,110 @@
+<?php
+
+return array(
+ 'admin' => array(
+ 'optimization_complete' => 'המיטוב הושלם',
+ ),
+ 'access' => array(
+ 'denied' => 'אין לך הרשאות לצפות בדף זה',
+ 'not_found' => 'הדף הזה לא נמצא',
+ ),
+ 'auth' => array(
+ 'form' => array(
+ 'not_set' => 'אירעה שגיאה במהלך הגדרת מערכת האימיות. אנא נסו שוב מאוחר יותר.',
+ 'set' => 'טופס הוא כרגע מערכת האימות כברירת מחדל.',
+ ),
+ 'login' => array(
+ 'invalid' => 'הכניסה לחשבון שגויה',
+ 'success' => 'You are connected', // @todo
+ ),
+ 'logout' => array(
+ 'success' => 'You are disconnected', // @todo
+ ),
+ 'no_password_set' => 'לא הוגדרה סיסמת מנהל. תכונה זו אינה זמינה.',
+ 'not_persona' => 'ניתן לאפס את מערכת הפרסונה בלבד.',
+ ),
+ 'conf' => array(
+ 'error' => 'An error occurred during configuration saving', // @todo
+ 'query_created' => 'השאילתה "%s" נוצרה.',
+ 'shortcuts_updated' => 'קיצורי הדרך עודכנו',
+ 'updated' => 'ההגדרות עודכנו',
+ ),
+ 'extensions' => array(
+ 'already_enabled' => '%s is already enabled', // @todo
+ 'disable' => array(
+ 'ko' => '%s cannot be disabled. <a href="%s">Check FressRSS logs</a> for details.', // @todo
+ 'ok' => '%s is now disabled', // @todo
+ ),
+ 'enable' => array(
+ 'ko' => '%s cannot be enabled. <a href="%s">Check FressRSS logs</a> for details.', // @todo
+ 'ok' => '%s is now enabled', // @todo
+ ),
+ 'no_access' => 'You have no access on %s', // @todo
+ 'not_enabled' => '%s is not enabled yet', // @todo
+ 'not_found' => '%s does not exist', // @todo
+ ),
+ 'import_export' => array(
+ 'export_no_zip_extension' => 'הרחבת ZIP אינה מותקנת על השרת.',
+ 'feeds_imported' => 'ההזנות שלך יובאו וכעת יעודכנו',
+ 'feeds_imported_with_errors' => 'ההזנות שלך יובאו אך אירעו מספר שגיאות',
+ 'file_cannot_be_uploaded' => 'אין אפשרות להעלות את הקובץ!',
+ 'no_zip_extension' => 'הרחבת ZIP אינה מותקנת על השרת.',
+ 'zip_error' => 'אירעה שגיאה במהלך ייבוא קובץ הZIP.',
+ ),
+ 'sub' => array(
+ 'actualize' => 'מימוש',
+ 'category' => array(
+ 'created' => 'Category %s has been created.', // @todo
+ 'deleted' => 'Category has been deleted.', // @todo
+ 'emptied' => 'הקטגוריה רוקנה',
+ 'error' => 'Category cannot be updated', // @todo
+ 'name_exists' => 'Category name already exists.', // @todo
+ 'no_id' => 'You must precise the id of the category.', // @todo
+ 'no_name' => 'Category name cannot be empty.', // @todo
+ 'not_delete_default' => 'You cannot delete the default category!', // @todo
+ 'not_exist' => 'The category does not exist!', // @todo
+ 'over_max' => 'You have reached your limit of categories (%d)', // @todo
+ 'updated' => 'Category has been updated.', // @todo
+ ),
+ 'feed' => array(
+ 'actualized' => '<em>%s</em> עודכן',
+ 'actualizeds' => 'הזנות RSS עודכנו',
+ 'added' => 'RSS הזנת <em>%s</em> נוספה',
+ 'already_subscribed' => 'אתה כבר רשום ל <em>%s</em>',
+ 'deleted' => 'ההזנה נמחקה',
+ 'error' => 'Feed cannot be updated', // @todo
+ 'internal_problem' => 'אין אפשרות להוסיף את ההזנה. <a href="%s">בדקו את הלוגים</a> לפרטים.',
+ 'invalid_url' => 'URL <em>%s</em> אינו תקין',
+ 'marked_read' => 'הזנות סומנו כנקראו',
+ 'n_actualized' => '%d הזנות עודכנו',
+ 'n_entries_deleted' => '%d המאמרים נמחקו',
+ 'no_refresh' => 'אין הזנה שניתן לרענן…',
+ 'not_added' => '<em>%s</em> אין אפשרות להוסיף את',
+ 'over_max' => 'You have reached your limit of feeds (%d)', // @todo
+ 'updated' => 'ההזנה התעדכנה',
+ ),
+ 'purge_completed' => 'הניקוי הושלם (%d מאמרים נמחקו)',
+ ),
+ 'update' => array(
+ 'can_apply' => 'FreshRSS will be now updated to the <strong>version %s</strong>.', // @todo
+ 'error' => 'תהליך העדכון נתקל בשגיאה: %s',
+ 'file_is_nok' => 'יש לבדוק את ההרשאות בתיקייה <em>%s</em>. שרת הHTTP חייב להיות בעל הרשאות כתיבה.',
+ 'finished' => 'העדכון הושלם!',
+ 'none' => 'אין עדכון להחלה',
+ 'server_not_found' => 'לא ניתן למצוא את שרת העדכון. [%s]',
+ ),
+ 'user' => array(
+ 'created' => array(
+ '_' => 'המשתמש %s נוצר',
+ 'error' => 'User %s cannot be created', // @todo
+ ),
+ 'deleted' => array(
+ '_' => 'המשתמש %s נמחק',
+ 'error' => 'User %s cannot be deleted', // @todo
+ ),
+ ),
+ 'profile' => array(
+ 'error' => 'Your profile cannot be modified', // @todo
+ 'updated' => 'Your profile has been modified', // @todo
+ ),
+);
diff --git a/app/i18n/he/gen.php b/app/i18n/he/gen.php
new file mode 100644
index 000000000..cacc8eaed
--- /dev/null
+++ b/app/i18n/he/gen.php
@@ -0,0 +1,191 @@
+<?php
+
+return array(
+ 'action' => array(
+ 'actualize' => 'מימוש',
+ 'back_to_rss_feeds' => '← חזרה להזנות הRSS שלך',
+ 'cancel' => 'ביטול',
+ 'create' => 'יצירה',
+ 'disable' => 'Disable', // @todo
+ 'empty' => 'Empty', // @todo
+ 'enable' => 'Enable', // @todo
+ 'export' => 'ייצוא',
+ 'filter' => 'מסנן',
+ 'import' => 'ייבוא',
+ 'manage' => 'ניהול',
+ 'mark_read' => 'סימון כנקרא',
+ 'mark_favorite' => 'סימון כמועדף',
+ 'remove' => 'Remove', // @todo
+ 'see_website' => 'ראו אתר',
+ 'submit' => 'אישור',
+ 'truncate' => 'מחיקת כל המאמרים',
+ ),
+ 'auth' => array(
+ 'email' => 'Email address', // @todo
+ 'keep_logged_in' => 'השאר מחובר <small>חודש</small>',
+ 'login' => 'כניסה לחשבון',
+ 'logout' => 'יציאה מהחשבון',
+ 'password' => array(
+ '_' => 'סיסמה',
+ 'format' => '<small>At least 7 characters</small>',
+ ),
+ 'registration' => array(
+ '_' => 'New account', // @todo
+ 'ask' => 'Create an account?', // @todo
+ 'title' => 'Account creation', // @todo
+ ),
+ 'reset' => 'איפוס אימות',
+ 'username' => array(
+ '_' => 'שם משתמש',
+ 'admin' => 'שם משתמש של המנהל',
+ 'format' => '<small>maximum 16 alphanumeric characters</small>', // @todo
+ ),
+ ),
+ 'date' => array(
+ 'Apr' => '\\A\\p\\r\\i\\l',
+ 'Aug' => '\\A\\u\\g\\u\\s\\t',
+ 'Dec' => '\\D\\e\\c\\e\\m\\b\\e\\r',
+ 'Feb' => '\\F\\e\\b\\r\\u\\a\\r\\y',
+ 'Jan' => '\\J\\a\\n\\u\\a\\r\\y',
+ 'Jul' => '\\J\\u\\l\\y',
+ 'Jun' => '\\J\\u\\n\\e',
+ 'Mar' => '\\M\\a\\r\\c\\h',
+ 'May' => '\\M\\a\\y',
+ 'Nov' => '\\N\\o\\v\\e\\m\\b\\e\\r',
+ 'Oct' => '\\O\\c\\t\\o\\b\\e\\r',
+ 'Sep' => '\\S\\e\\p\\t\\e\\m\\b\\e\\r',
+ 'apr' => 'apr',
+ 'april' => 'Apr',
+ 'aug' => 'aug',
+ 'august' => 'Aug',
+ 'before_yesterday' => 'ישן יותר',
+ 'dec' => 'dec',
+ 'december' => 'Dec',
+ 'feb' => 'feb',
+ 'february' => 'Feb',
+ 'format_date' => '%s j\\<\\s\\u\\p\\>S\\<\\/\\s\\u\\p\\> Y',
+ 'format_date_hour' => '%s j\\<\\s\\u\\p\\>S\\<\\/\\s\\u\\p\\> Y \\a\\t H\\:i',
+ 'fri' => 'Fri',
+ 'jan' => 'jan',
+ 'january' => 'Jan',
+ 'jul' => 'jul',
+ 'july' => 'Jul',
+ 'jun' => 'jun',
+ 'june' => 'Jun',
+ 'last_3_month' => 'בשלושת החודשים האחרונים',
+ 'last_6_month' => 'בששת החודשים האחרונים',
+ 'last_month' => 'בחודש שעבר',
+ 'last_week' => 'בשבוע שעבר',
+ 'last_year' => 'בשנה האחרונה',
+ 'mar' => 'mar',
+ 'march' => 'Mar',
+ 'may' => 'May',
+ 'may_' => 'May',
+ 'mon' => 'Mon',
+ 'month' => 'חודשים',
+ 'nov' => 'nov',
+ 'november' => 'Nov',
+ 'oct' => 'oct',
+ 'october' => 'Oct',
+ 'sat' => 'Sat',
+ 'sep' => 'sep',
+ 'september' => 'Sep',
+ 'sun' => 'Sun',
+ 'thu' => 'Thu',
+ 'today' => 'היום',
+ 'tue' => 'Tue',
+ 'wed' => 'Wed',
+ 'yesterday' => 'אתמול',
+ ),
+ 'freshrss' => array(
+ '_' => 'FreshRSS',
+ 'about' => 'אודות FreshRSS',
+ ),
+ 'js' => array(
+ 'category_empty' => 'Empty category', // @todo
+ 'confirm_action' => 'האם אתם בטוחים שברצונכם לבצע פעולה זו? אין אפשרות לבטל אותה!',
+ 'confirm_action_feed_cat' => 'האם אתם בטוחים שברצוניכם לבצע פעולה זו? מועדפים ושאילתות עשויות לאבוד. אין אפשרות לבטל אותה!',
+ 'feedback' => array(
+ 'body_new_articles' => 'ישנם \d מאמרים חדשים לקרוא ב FreshRSS.',
+ 'request_failed' => 'A request has failed, it may have been caused by Internet connection problems.', // @todo
+ 'title_new_articles' => 'FreshRSS: מאמרים חדשים!',
+ ),
+ 'new_article' => 'מאמרים חדשים זמינים, לחצו לרענון העמוד.',
+ 'should_be_activated' => 'חובה להפעיל JavaScript',
+ ),
+ 'lang' => array(
+ 'cz' => 'Čeština',
+ 'de' => 'Deutsch',
+ 'en' => 'English',
+ 'es' => 'Español',
+ 'fr' => 'Français',
+ 'he' => 'עברית',
+ 'it' => 'Italiano',
+ 'kr' => '한국어',
+ 'nl' => 'Nederlands',
+ 'pt-br' => 'Português (Brasil)',
+ 'ru' => 'Русский',
+ 'tr' => 'Türkçe',
+ 'zh-cn' => '简体中文',
+ ),
+ 'menu' => array(
+ 'about' => 'אודות',
+ 'admin' => 'ניהול',
+ 'archiving' => 'ארכוב',
+ 'authentication' => 'Authentication', // @todo
+ 'check_install' => 'Installation checking', // @todo
+ 'configuration' => 'הגדרות',
+ 'display' => 'תצוגה',
+ 'extensions' => 'Extensions', // @todo
+ 'logs' => 'לוגים',
+ 'queries' => 'שאילתות',
+ 'reading' => 'קריאה',
+ 'search' => 'חיפוש מילים או #תגים',
+ 'sharing' => 'שיתוף',
+ 'shortcuts' => 'קיצורי דרך',
+ 'stats' => 'סטטיסטיקות',
+ 'system' => 'System configuration', // @todo
+ 'update' => 'עדכון',
+ 'user_management' => 'Manage users', // @todo
+ 'user_profile' => 'Profile', // @todo
+ ),
+ 'pagination' => array(
+ 'first' => 'הראשון',
+ 'last' => 'אחרון',
+ 'load_more' => 'טעינת מאמרים נוספים',
+ 'mark_all_read' => 'סימון הכל כנקרא',
+ 'next' => 'הבא',
+ 'nothing_to_load' => 'אין מאמרים נוספים',
+ 'previous' => 'הקודם',
+ ),
+ 'share' => array(
+ 'blogotext' => 'Blogotext',
+ 'diaspora' => 'Diaspora*',
+ 'email' => 'דואר אלקטרוני',
+ 'facebook' => 'Facebook',
+ 'g+' => 'Google+',
+ 'gnusocial' => 'GNU social',
+ 'jdh' => 'Journal du hacker',
+ 'Known' => 'Known based sites',
+ 'mastodon' => 'Mastodon',
+ 'movim' => 'Movim',
+ 'print' => 'הדפסה',
+ 'shaarli' => 'Shaarli',
+ 'twitter' => 'Twitter',
+ 'wallabag' => 'wallabag v1',
+ 'wallabagv2' => 'wallabag v2',
+ ),
+ 'short' => array(
+ 'attention' => 'זהירות!',
+ 'blank_to_disable' => 'יש להשאיר ריק על מנת לנטרל',
+ 'by_author' => 'מאת <em>%s</em>',
+ 'by_default' => 'ברירת מחדל',
+ 'damn' => 'הו לא!',
+ 'default_category' => 'ללא קטגוריה',
+ 'no' => 'לא',
+ 'not_applicable' => 'Not available', // @todo
+ 'ok' => 'כן!',
+ 'or' => 'או',
+ 'yes' => 'כן',
+ ),
+);
diff --git a/app/i18n/he/index.php b/app/i18n/he/index.php
new file mode 100644
index 000000000..cef07eaf8
--- /dev/null
+++ b/app/i18n/he/index.php
@@ -0,0 +1,61 @@
+<?php
+
+return array(
+ 'about' => array(
+ '_' => 'אודות',
+ 'agpl3' => '<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL 3</a>',
+ 'bugs_reports' => 'דיווח באגים',
+ 'credits' => 'קרדיטים',
+ 'credits_content' => 'מאפייני עיצוב מסויימים הגיעו מ <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> אף על פי ש FreshRSS אינו משתמש בתשתית הזו. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">סמלילים</a> הגיעו מ <a href="https://www.gnome.org/"> פרוייקט GNOME </a>. <em>Open Sans</em> הגופן police נוצר על ידי <a href="https://www.google.com/webfonts/specimen/Open+Sans">Steve Matteson</a>. Favicons נאספים בעזרת <a href="https://getfavicon.appspot.com/">getFavicon API</a>. FreshRSS מבוסס על <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, תשתית PHP.',
+ 'freshrss_description' => 'FreshRSS הוא קורא RSS לאחסון עצמי בדומה ל <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> או <a href="http://projet.idleman.fr/leed/">Leed</a>. אינו צורך משאבים רבים, וקל לתפעול אך בו בזמן חזק וניתן להתאמה.',
+ 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">בגיטהאב</a>',
+ 'license' => 'רישיון',
+ 'project_website' => 'אתר',
+ 'title' => 'אודות',
+ 'version' => 'גרסה',
+ 'website' => 'אתר',
+ ),
+ 'feed' => array(
+ 'add' => 'ניתן להוסיף הזנות חדשות.',
+ 'empty' => 'אין מאמר להצגה.',
+ 'rss_of' => 'הזנת RSS של %s',
+ 'title' => 'ההזנות שלך',
+ 'title_global' => 'תצוגה גלובלית',
+ 'title_fav' => 'המועדפים שלך',
+ ),
+ 'log' => array(
+ '_' => 'לוגים',
+ 'clear' => 'ניקוי הלוגים',
+ 'empty' => 'קובץ הלוג ריק',
+ 'title' => 'לוגים',
+ ),
+ 'menu' => array(
+ 'about' => 'אודות FreshRSS',
+ 'add_query' => 'הוספת שאילתה',
+ 'before_one_day' => 'אתמול',
+ 'before_one_week' => 'לפני שבוע',
+ 'favorites' => 'מועדפים (%s)',
+ 'global_view' => 'תצוגה גלובלית',
+ 'main_stream' => 'הזנה ראשית',
+ 'mark_all_read' => 'סימון הכל כנקרא',
+ 'mark_cat_read' => 'סימון קטגוריה כנקראה',
+ 'mark_feed_read' => 'סימון הזנה כנקראה',
+ 'newer_first' => 'חדשים בראש',
+ 'non-starred' => 'הצגת הכל פרט למועדפים',
+ 'normal_view' => 'תצוגה רגילה',
+ 'older_first' => 'ישנים יותר בראש',
+ 'queries' => 'שאילתות',
+ 'read' => 'הצגת נקראו בלבד',
+ 'reader_view' => 'תצוגת קריאה',
+ 'rss_view' => 'הזנת RSS',
+ 'search_short' => 'חיפוש',
+ 'starred' => 'הצגת מועדפים בלבד',
+ 'stats' => 'סטטיסטיקות',
+ 'subscription' => 'ניהול הרשמות',
+ 'unread' => 'הצגת מאמרים שלא נקראו בלבד',
+ ),
+ 'share' => 'שיתוף',
+ 'tag' => array(
+ 'related' => 'תגיות קשורות',
+ ),
+);
diff --git a/app/i18n/he/install.php b/app/i18n/he/install.php
new file mode 100644
index 000000000..6d7cee661
--- /dev/null
+++ b/app/i18n/he/install.php
@@ -0,0 +1,107 @@
+<?php
+
+return array(
+ 'action' => array(
+ 'finish' => 'השלמת ההתקנה',
+ 'fix_errors_before' => 'יש לתקן את השגיאות לפני המעבר לשלב הבא.',
+ 'next_step' => 'לשלב הבא',
+ ),
+ 'auth' => array(
+ 'email_persona' => 'כתובת דואר אלקטרוני להרשמה<br /><small>(לצורך <a href="https://persona.org/" rel="external">מוזילה פרסונה</a>)</small>',
+ 'form' => 'טופס אינטרנטי (מסורתי, דורש JavaScript)',
+ 'http' => 'HTTP (למשתמשים מתקדמים עם HTTPS)',
+ 'none' => 'ללא (מסוכן)',
+ 'password_form' => 'סיסמה<br /><small>(לשימוש בטפוס ההרשמה)</small>',
+ 'password_format' => 'At least 7 characters', // @todo
+ 'persona' => 'מוזילה פרסונה (מודרני, דורש JavaScript)',
+ 'type' => 'שיטת אימות',
+ ),
+ 'bdd' => array(
+ '_' => 'בסיס נתונים',
+ 'conf' => array(
+ '_' => 'הגדרות בסיס נתונים',
+ 'ko' => 'נא לוודא את הגדרות בסיס הנתונים.',
+ 'ok' => 'הגדרות בסיס הנתונים נשמרו.',
+ ),
+ 'host' => 'מארח',
+ 'prefix' => 'קידומת הטבלה',
+ 'password' => 'HTTP סיסמה',
+ 'type' => 'סוג בסיס הנתונים',
+ 'username' => 'HTTP שם משתמש',
+ ),
+ 'check' => array(
+ '_' => 'בדיקות',
+ 'cache' => array(
+ 'nok' => 'Check permissions on <em>./data/cache</em> directory. HTTP server must have rights to write into', // @todo
+ 'ok' => 'ההרשאות בתיקיית המטמון תקינות',
+ ),
+ 'ctype' => array(
+ 'nok' => 'הספרייה הנדרשת ל character type checking (php-ctype) אינה מותקנת',
+ 'ok' => 'הספרייה הנדרשת ל character type checking (ctype) מותקנת',
+ ),
+ 'curl' => array(
+ 'nok' => 'בURL לא מותקן (php5-curl package)',
+ 'ok' => 'יש לכם את גירסת %s של cURL',
+ ),
+ 'data' => array(
+ 'nok' => 'Check permissions on <em>./data</em> directory. HTTP server must have rights to write into', // @todo
+ 'ok' => 'ההרשאות בתיקיית הדאטא תקינות',
+ ),
+ 'dom' => array(
+ 'nok' => 'הספרייה הנדרשת לסיור ב DOM אינה מותקנת (php-xml package)',
+ 'ok' => 'הספרייה הנדרשת לסיור ב DOM מותקנת',
+ ),
+ 'favicons' => array(
+ 'nok' => 'Check permissions on <em>./data/favicons</em> directory. HTTP server must have rights to write into', // @todo
+ 'ok' => 'ההרשאות בתיקיית הfavicons תקינות',
+ ),
+ 'http_referer' => array(
+ 'nok' => 'נא לדבוק שאינך פוגעת ב HTTP REFERER שלך.',
+ 'ok' => 'הHTTP REFERER ידוע ותאם לשרת שלך.',
+ ),
+ 'minz' => array(
+ 'nok' => 'You lack the Minz framework.', // @todo
+ 'ok' => 'יש לכם את תשתית Minz',
+ ),
+ 'pcre' => array(
+ 'nok' => 'הספרייה הנדרשת לביטויים רגולריים אינה מותקנת (php-pcre)',
+ 'ok' => 'הספרייה הנדרשת לביטויים רגולריים מותקנת (PCRE)',
+ ),
+ 'pdo' => array(
+ 'nok' => 'PDO אינו מותקן או שאחד ממנהלי ההתקנים שלו חסר (pdo_mysql, pdo_sqlite)',
+ 'ok' => 'PDO מותקן ולפחות אחד ממנהלי ההתקן הנתמכים מותקן (pdo_mysql, pdo_sqlite)',
+ ),
+ 'persona' => array(
+ 'nok' => 'Check permissions on <em>./data/persona</em> directory. HTTP server must have rights to write into', // @todo
+ 'ok' => 'ההרשאות בתיקיית מוזילה פרסונה תקינות',
+ ),
+ 'php' => array(
+ 'nok' => 'גירסת PHP שלכם היא %s אך FreshRSS דורש לפחות את גירסה %s',
+ 'ok' => 'גירסת PHP שלכם היא %s, שתואמת ל FreshRSS',
+ ),
+ 'users' => array(
+ 'nok' => 'Check permissions on <em>./data/users</em> directory. HTTP server must have rights to write into', // @todo
+ 'ok' => 'Permissions on users directory are good.', // @todo
+ ),
+ ),
+ 'conf' => array(
+ '_' => 'הגדרות כלליות',
+ 'ok' => 'ההגדרות הכלליות נשמרו.',
+ ),
+ 'congratulations' => 'מזל טוב!',
+ 'default_user' => 'שם המשתמש של משתמש ברירת המחדל <small>(לכל היותר 16 תווים אלפאנומריים)</small>',
+ 'delete_articles_after' => 'מחיקת מאמרים לאחר',
+ 'fix_errors_before' => 'יש לתקן את השגיאות לפני המעבר לשלב הבא.',
+ 'javascript_is_better' => 'FreshRSS מעדיף שתאפשרו JavaScript',
+ 'language' => array(
+ '_' => 'שפה',
+ 'choose' => 'בחירת שפה ל FreshRSS',
+ 'defined' => 'השפה הוגדרה.',
+ ),
+ 'not_deleted' => 'משהו נכשל; יש צורך למחוק את הקובץ <em>%s</em> ידנית.',
+ 'ok' => 'The installation process was successful.', // @todo
+ 'step' => 'step %d', // @todo
+ 'steps' => 'שלבים',
+ 'title' => 'התקנה · FreshRSS',
+ 'this_is_the_end' => 'סיום',
+);
diff --git a/app/i18n/he/sub.php b/app/i18n/he/sub.php
new file mode 100644
index 000000000..333de0edf
--- /dev/null
+++ b/app/i18n/he/sub.php
@@ -0,0 +1,76 @@
+<?php
+
+return array(
+ 'api' => array(
+ 'documentation' => 'Copy the following URL to use it within an external tool.', // @todo
+ 'title' => 'API', // @todo
+ ),
+ 'bookmarklet' => array(
+ 'documentation' => 'Drag this button to your bookmarks toolbar or right-click it and choose "Bookmark This Link". Then click "Subscribe" button in any page you want to subscribe to.', // @todo
+ 'label' => 'Subscribe', // @todo
+ 'title' => 'Bookmarklet', // @todo
+ ),
+ 'category' => array(
+ '_' => 'קטגוריה',
+ 'add' => 'הוספת קטגוריה',
+ 'empty' => 'Empty category', // @todo
+ 'new' => 'קטגוריה חדשה',
+ ),
+ 'feed' => array(
+ 'add' => 'הוספת הזנה',
+ 'advanced' => 'מתקדם',
+ 'archiving' => 'ארכוב',
+ 'auth' => array(
+ 'configuration' => 'כניסה לחשבון',
+ 'help' => 'החיבור מתיר לגשת להזנות RSS מוגנות',
+ 'http' => 'HTTP אימות',
+ 'password' => 'HTTP סיסמה',
+ 'username' => 'HTTP שם משתמש',
+ ),
+ 'css_help' => 'קבלת הזנות RSS קטומות (זהירות, לוקח זמן רב יותר!)',
+ 'css_path' => 'נתיב הCSS של המאמר באתר המקורי',
+ 'description' => 'תיאור',
+ 'empty' => 'הזנה זו ריקה. אנא ודאו שהיא עדיין מתוחזקת.',
+ 'error' => 'הזנה זו נתקלה בשגיאה, אנא ודאו שהיא תקינה ואז נסו שנית.',
+ 'in_main_stream' => 'הצגה בזרם המרכזי',
+ 'informations' => 'מידע',
+ 'keep_history' => 'מסםר מינימלי של מאמרים לשמור',
+ 'moved_category_deleted' => 'כאשר הקטגוריה נמחקת ההזנות שבתוכה אוטומטית מקוטלגות תחת <em>%s</em>.',
+ 'no_selected' => 'אף הזנה לא נבחרה.',
+ 'number_entries' => '%d מאמרים',
+ 'stats' => 'סטטיסטיקות',
+ 'think_to_add' => 'ניתן להוסיף הזנות חדשות.',
+ 'title' => 'כותרת',
+ 'title_add' => 'הוספת הזנה',
+ 'ttl' => 'אין לרענן אוטומטית יותר מ',
+ 'url' => 'הזנה URL',
+ 'validator' => 'בדיקות תקינות ההזנה',
+ 'website' => 'אתר URL',
+ ),
+ 'firefox' => array(
+ 'documentation' => 'Follow the steps described <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">here</a> to add FreshRSS to Firefox feed reader list.', // @todo
+ 'title' => 'Firefox feed reader', // @todo
+ ),
+ 'import_export' => array(
+ 'export' => 'ייצוא',
+ 'export_opml' => 'ייצוא רשימת הזנות (OPML)',
+ 'export_starred' => 'ייצוא מועדפים',
+ 'feed_list' => 'רשימה של %s מאמרים',
+ 'file_to_import' => 'קובץ לייבוא<br />(OPML, Json or Zip)',
+ 'file_to_import_no_zip' => 'קובץ לייבוא<br />(OPML or Json)',
+ 'import' => 'ייבוא',
+ 'starred_list' => 'רשימת מאמרים מועדפים',
+ 'title' => 'יבוא / יצוא ',
+ ),
+ 'menu' => array(
+ 'bookmark' => 'הרשמה (FreshRSS סימניית)',
+ 'import_export' => 'יבוא / יצוא ',
+ 'subscription_management' => 'ניהול הרשמות',
+ 'subscription_tools' => 'Subscription tools', // @todo
+ ),
+ 'title' => array(
+ '_' => 'ניהול הרשמות',
+ 'feed_management' => 'ניהול הזנות RSS',
+ 'subscription_tools' => 'Subscription tools', // @todo
+ ),
+);
diff --git a/app/i18n/it/admin.php b/app/i18n/it/admin.php
index ae46818ae..0248d9317 100644
--- a/app/i18n/it/admin.php
+++ b/app/i18n/it/admin.php
@@ -112,6 +112,13 @@ return array(
),
'title' => 'Estensioni',
'user' => 'Estensioni utente',
+ 'community' => 'Available community extensions', // @todo translate
+ 'name' => 'Name', // @todo translate
+ 'version' => 'Version', // @todo translate
+ 'description' => 'Description', // @todo translate
+ 'author' => 'Author', // @todo translate
+ 'latest' => 'Installed', // @todo translate
+ 'update' => 'Update available', // @todo translate
),
'stats' => array(
'_' => 'Statistiche',
diff --git a/app/i18n/it/gen.php b/app/i18n/it/gen.php
index ae39d7324..ccc8c0e7c 100644
--- a/app/i18n/it/gen.php
+++ b/app/i18n/it/gen.php
@@ -13,8 +13,8 @@ return array(
'filter' => 'Filtra',
'import' => 'Importa',
'manage' => 'Gestisci',
- 'mark_read' => 'Segna come letto',
'mark_favorite' => 'Segna come preferito',
+ 'mark_read' => 'Segna come letto',
'remove' => 'Rimuovi',
'see_website' => 'Vai al sito',
'submit' => 'Conferma',
@@ -79,8 +79,8 @@ return array(
'last_year' => 'Ultimo anno',
'mar' => 'mar.',
'march' => 'marzo',
- 'may_' => 'May',
'may' => 'maggio',
+ 'may_' => 'May',
'mon' => 'Mon',
'month' => 'mesi',
'nov' => 'nov.',
@@ -119,6 +119,7 @@ return array(
'en' => 'English',
'es' => 'Español',
'fr' => 'Français',
+ 'he' => 'עברית',
'it' => 'Italiano',
'kr' => '한국어',
'nl' => 'Nederlands',
@@ -158,20 +159,21 @@ return array(
'previous' => 'Precedente',
),
'share' => array(
+ 'Known' => 'Siti basati su Known',
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Email',
'facebook' => 'Facebook',
'g+' => 'Google+',
+ 'gnusocial' => 'GNU social',
+ 'jdh' => 'Journal du hacker',
+ 'mastodon' => 'Mastodon',
'movim' => 'Movim',
'print' => 'Stampa',
'shaarli' => 'Shaarli',
'twitter' => 'Twitter',
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
- 'jdh' => 'Journal du hacker',
- 'Known' => 'Siti basati su Known',
- 'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => 'Attenzione!',
diff --git a/app/i18n/it/sub.php b/app/i18n/it/sub.php
index e730e2bc8..fe18855fb 100644
--- a/app/i18n/it/sub.php
+++ b/app/i18n/it/sub.php
@@ -1,6 +1,10 @@
<?php
return array(
+ 'api' => array(
+ 'documentation' => 'Copy the following URL to use it within an external tool.',// TODO
+ 'title' => 'API',// TODO
+ ),
'bookmarklet' => array(
'documentation' => 'Drag this button to your bookmarks toolbar or right-click it and choose "Bookmark This Link". Then click "Subscribe" button in any page you want to subscribe to.',// TODO
'label' => 'Subscribe',// TODO
diff --git a/app/i18n/kr/admin.php b/app/i18n/kr/admin.php
index 13f4695e1..9781fb640 100644
--- a/app/i18n/kr/admin.php
+++ b/app/i18n/kr/admin.php
@@ -112,6 +112,13 @@ return array(
),
'title' => '확장 기능',
'user' => '사용자 확장 기능',
+ 'community' => 'Available community extensions', // @todo translate
+ 'name' => 'Name', // @todo translate
+ 'version' => 'Version', // @todo translate
+ 'description' => 'Description', // @todo translate
+ 'author' => 'Author', // @todo translate
+ 'latest' => 'Installed', // @todo translate
+ 'update' => 'Update available', // @todo translate
),
'stats' => array(
'_' => '통계',
diff --git a/app/i18n/kr/gen.php b/app/i18n/kr/gen.php
index 35d5e8143..8997ba14b 100644
--- a/app/i18n/kr/gen.php
+++ b/app/i18n/kr/gen.php
@@ -13,8 +13,8 @@ return array(
'filter' => '해당하는 글 보기',
'import' => '불러오기',
'manage' => '관리',
- 'mark_read' => '읽음으로 표시',
'mark_favorite' => '즐겨찾기에 등록',
+ 'mark_read' => '읽음으로 표시',
'remove' => '삭제',
'see_website' => '웹사이트 열기',
'submit' => '설정 저장',
@@ -79,8 +79,8 @@ return array(
'last_year' => '최근 일 년',
'mar' => '3월',
'march' => '3월',
- 'may_' => '5월',
'may' => '5월',
+ 'may_' => '5월',
'mon' => '월',
'month' => '개월',
'nov' => '11월',
@@ -119,6 +119,7 @@ return array(
'en' => 'English',
'es' => 'Español',
'fr' => 'Français',
+ 'he' => 'עברית',
'it' => 'Italiano',
'kr' => '한국어',
'nl' => 'Nederlands',
@@ -158,20 +159,21 @@ return array(
'previous' => 'Previous',
),
'share' => array(
+ 'Known' => 'Known based sites',
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => '메일',
'facebook' => 'Facebook',
'g+' => 'Google+',
+ 'gnusocial' => 'GNU social',
+ 'jdh' => 'Journal du hacker',
+ 'mastodon' => 'Mastodon',
'movim' => 'Movim',
'print' => '인쇄',
'shaarli' => 'Shaarli',
'twitter' => 'Twitter',
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
- 'jdh' => 'Journal du hacker',
- 'Known' => 'Known based sites',
- 'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => '경고!',
diff --git a/app/i18n/kr/sub.php b/app/i18n/kr/sub.php
index 8af29422d..b8f2385b3 100644
--- a/app/i18n/kr/sub.php
+++ b/app/i18n/kr/sub.php
@@ -1,6 +1,10 @@
<?php
return array(
+ 'api' => array(
+ 'documentation' => 'Copy the following URL to use it within an external tool.',// TODO
+ 'title' => 'API',// TODO
+ ),
'bookmarklet' => array(
'documentation' => '이 버튼을 즐겨찾기 막대로 끌어다 놓거나 마우스 오른쪽 클릭으로 나타나는 메뉴에서 "이 링크를 즐겨찾기에 추가"를 선택하세요. 그리고 피드를 구독하길 원하는 페이지에서 "구독하기" 버튼을 클릭하세요.',
'label' => '구독하기',
diff --git a/app/i18n/nl/admin.php b/app/i18n/nl/admin.php
index fdfe6e3bc..384242b4d 100644
--- a/app/i18n/nl/admin.php
+++ b/app/i18n/nl/admin.php
@@ -112,6 +112,13 @@ return array(
),
'title' => 'Uitbreidingen',
'user' => 'Gebruikersuitbreidingen',
+ 'community' => 'Gebruikersuitbreidingen beschikbaar',
+ 'name' => 'Naam',
+ 'version' => 'Versie',
+ 'description' => 'Beschrijving',
+ 'author' => 'Auteur',
+ 'latest' => 'Geïnstalleerd',
+ 'update' => 'Update beschikbaar',
),
'stats' => array(
'_' => 'Statistieken',
diff --git a/app/i18n/nl/gen.php b/app/i18n/nl/gen.php
index 1617936ab..3215bdfec 100644
--- a/app/i18n/nl/gen.php
+++ b/app/i18n/nl/gen.php
@@ -1,5 +1,5 @@
<?php
-/* Dutch translation by Wanabo. http://www.nieuwskop.be */
+
return array(
'action' => array(
'actualize' => 'Actualiseren',
@@ -13,8 +13,8 @@ return array(
'filter' => 'Filteren',
'import' => 'Importeren',
'manage' => 'Beheren',
- 'mark_read' => 'Markeer als gelezen',
'mark_favorite' => 'Markeer als favoriet',
+ 'mark_read' => 'Markeer als gelezen',
'remove' => 'Verwijder',
'see_website' => 'Bekijk website',
'submit' => 'Opslaan',
@@ -63,8 +63,8 @@ return array(
'december' => 'Dec',
'feb' => 'feb',
'february' => 'Feb',
- 'format_date' => 'j %s Y', //<-- European date format // 'format_date' => '%s j\\<\\s\\u\\p\\>S\\<\\/\\s\\u\\p\\> Y',
- 'format_date_hour' => 'j %s Y \\o\\m H\\:i', //<-- European date format // 'format_date_hour' => '%s j\\<\\s\\u\\p\\>S\\<\\/\\s\\u\\p\\> Y \\a\\t H\\:i',
+ 'format_date' => 'j %s Y',
+ 'format_date_hour' => 'j %s Y \\o\\m H\\:i',
'fri' => 'Vr',
'jan' => 'jan',
'january' => 'Jan',
@@ -79,8 +79,8 @@ return array(
'last_year' => 'Vorig jaar',
'mar' => 'mrt',
'march' => 'Mrt',
- 'may_' => 'Mei',
'may' => 'Mei',
+ 'may_' => 'Mei',
'mon' => 'Ma',
'month' => 'maanden',
'nov' => 'nov',
@@ -119,6 +119,7 @@ return array(
'en' => 'English',
'es' => 'Español',
'fr' => 'Français',
+ 'he' => 'עברית',
'it' => 'Italiano',
'kr' => '한국어',
'nl' => 'Nederlands',
@@ -158,20 +159,21 @@ return array(
'previous' => 'Vorige',
),
'share' => array(
+ 'Known' => 'Known based sites',
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Email',
'facebook' => 'Facebook',
'g+' => 'Google+',
+ 'gnusocial' => 'GNU social',
+ 'jdh' => 'Journal du hacker',
+ 'mastodon' => 'Mastodon',
'movim' => 'Movim',
'print' => 'Print',
'shaarli' => 'Shaarli',
'twitter' => 'Twitter',
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
- 'jdh' => 'Journal du hacker',
- 'Known' => 'Known based sites',
- 'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => 'Attentie!',
diff --git a/app/i18n/nl/sub.php b/app/i18n/nl/sub.php
index bfa0911b7..ce446778c 100644
--- a/app/i18n/nl/sub.php
+++ b/app/i18n/nl/sub.php
@@ -1,10 +1,14 @@
<?php
/* Dutch translation by Wanabo. http://www.nieuwskop.be */
return array(
+ 'api' => array(
+ 'documentation' => 'Kopieer de volgende URL om hem in een externe toepassing te gebruiken.',
+ 'title' => 'API',
+ ),
'bookmarklet' => array(
- 'documentation' => 'Drag this button to your bookmarks toolbar or right-click it and choose "Bookmark This Link". Then click "Subscribe" button in any page you want to subscribe to.',// TODO
- 'label' => 'Subscribe',// TODO
- 'title' => 'Bookmarklet',// TODO
+ 'documentation' => 'Sleep deze knop naar je bladwijzerwerkbalk of klik erop met de rechtermuisknop en kies "Deze link aan bladwijzers toevoegen."',
+ 'label' => 'Abonneren',
+ 'title' => 'Bookmarklet',
),
'category' => array(
'_' => 'Categorie',
@@ -45,8 +49,8 @@ return array(
'website' => 'Website URL',
),
'firefox' => array(
- 'documentation' => 'Follow the steps described <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">here</a> to add FreshRSS to Firefox feed reader list.',// TODO
- 'title' => 'Firefox feed reader',// TODO
+ 'documentation' => 'Volg de stappen die <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">hier</a> beschreven wordem om FreshRSS aan de Firefox-nieuwslezerlijst toe te voegen.',
+ 'title' => 'Firefox-nieuwslezer',
),
'import_export' => array(
'export' => 'Exporteer',
diff --git a/app/i18n/pt-br/admin.php b/app/i18n/pt-br/admin.php
index 1076534b2..e62718e80 100644
--- a/app/i18n/pt-br/admin.php
+++ b/app/i18n/pt-br/admin.php
@@ -112,6 +112,13 @@ return array(
),
'title' => 'Extensões',
'user' => 'Extensões do usuário',
+ 'community' => 'Available community extensions', // @todo translate
+ 'name' => 'Name', // @todo translate
+ 'version' => 'Version', // @todo translate
+ 'description' => 'Description', // @todo translate
+ 'author' => 'Author', // @todo translate
+ 'latest' => 'Installed', // @todo translate
+ 'update' => 'Update available', // @todo translate
),
'stats' => array(
'_' => 'Estatísticas',
diff --git a/app/i18n/pt-br/gen.php b/app/i18n/pt-br/gen.php
index 1a74e1437..4bc8535d6 100644
--- a/app/i18n/pt-br/gen.php
+++ b/app/i18n/pt-br/gen.php
@@ -13,8 +13,8 @@ return array(
'filter' => 'Filtrar',
'import' => 'Importar',
'manage' => 'Gerenciar',
- 'mark_read' => 'Marcar como lido',
'mark_favorite' => 'Marcar como favorito',
+ 'mark_read' => 'Marcar como lido',
'remove' => 'Remover',
'see_website' => 'Ver o site',
'submit' => 'Enviar',
@@ -118,13 +118,14 @@ return array(
'en' => 'English',
'es' => 'Español',
'fr' => 'Français',
+ 'he' => 'עברית',
'it' => 'Italiano',
'kr' => '한국어',
'nl' => 'Nederlands',
'pt-br' => 'Português (Brasil)',
'ru' => 'Русский',
'tr' => 'Türkçe',
- 'zh-cn' => '简体中文'
+ 'zh-cn' => '简体中文',
),
'menu' => array(
'about' => 'Sobre',
@@ -157,20 +158,21 @@ return array(
'previous' => 'Anterior',
),
'share' => array(
+ 'Known' => 'Known based sites',
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Email',
'facebook' => 'Facebook',
'g+' => 'Google+',
+ 'gnusocial' => 'GNU social',
+ 'jdh' => 'Journal du hacker',
+ 'mastodon' => 'Mastodon',
'movim' => 'Movim',
'print' => 'Imprimir',
'shaarli' => 'Shaarli',
'twitter' => 'Twitter',
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
- 'jdh' => 'Journal du hacker',
- 'Known' => 'Known based sites',
- 'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => 'Atencão!',
diff --git a/app/i18n/pt-br/sub.php b/app/i18n/pt-br/sub.php
index 61a80d41e..4249dcabf 100644
--- a/app/i18n/pt-br/sub.php
+++ b/app/i18n/pt-br/sub.php
@@ -1,6 +1,15 @@
<?php
return array(
+ 'api' => array(
+ 'documentation' => 'Copy the following URL to use it within an external tool.',// TODO
+ 'title' => 'API',// TODO
+ ),
+ 'bookmarklet' => array(
+ 'documentation' => 'Drag this button to your bookmarks toolbar or right-click it and choose "Bookmark This Link". Then click "Subscribe" button in any page you want to subscribe to.',// TODO
+ 'label' => 'Subscribe',// TODO
+ 'title' => 'Bookmarklet',// TODO
+ ),
'category' => array(
'_' => 'Categoria',
'add' => 'Adicionar uma categoria',
diff --git a/app/i18n/ru/admin.php b/app/i18n/ru/admin.php
index f5da97371..d877c5006 100644
--- a/app/i18n/ru/admin.php
+++ b/app/i18n/ru/admin.php
@@ -112,6 +112,13 @@ return array(
),
'title' => 'Расширения',
'user' => 'Расширения пользователя',
+ 'community' => 'Available community extensions', // @todo translate
+ 'name' => 'Name', // @todo translate
+ 'version' => 'Version', // @todo translate
+ 'description' => 'Description', // @todo translate
+ 'author' => 'Author', // @todo translate
+ 'latest' => 'Installed', // @todo translate
+ 'update' => 'Update available', // @todo translate
),
'stats' => array(
'_' => 'Статистика',
diff --git a/app/i18n/ru/gen.php b/app/i18n/ru/gen.php
index 3a728016d..cc7e8765c 100644
--- a/app/i18n/ru/gen.php
+++ b/app/i18n/ru/gen.php
@@ -13,8 +13,8 @@ return array(
'filter' => 'Filter',
'import' => 'Import',
'manage' => 'Manage',
- 'mark_read' => 'Mark as read',
'mark_favorite' => 'Mark as favourite',
+ 'mark_read' => 'Mark as read',
'remove' => 'Remove',
'see_website' => 'See website',
'submit' => 'Submit',
@@ -79,8 +79,8 @@ return array(
'last_year' => 'Last year',
'mar' => 'mar',
'march' => 'Mar',
- 'may_' => 'May',
'may' => 'May',
+ 'may_' => 'May',
'mon' => 'Mon',
'month' => 'months',
'nov' => 'nov',
@@ -119,6 +119,7 @@ return array(
'en' => 'English',
'es' => 'Español',
'fr' => 'Français',
+ 'he' => 'עברית',
'it' => 'Italiano',
'kr' => '한국어',
'nl' => 'Nederlands',
@@ -158,20 +159,21 @@ return array(
'previous' => 'Previous',
),
'share' => array(
+ 'Known' => 'Known based sites',
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Email',
'facebook' => 'Facebook',
'g+' => 'Google+',
+ 'gnusocial' => 'GNU social',
'jdh' => 'Journal du hacker',
+ 'mastodon' => 'Mastodon',
'movim' => 'Movim',
'print' => 'Print',
'shaarli' => 'Shaarli',
'twitter' => 'Twitter',
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
- 'Known' => 'Known based sites',
- 'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => 'Warning!',
diff --git a/app/i18n/ru/sub.php b/app/i18n/ru/sub.php
index fa28a3ec4..6a5530de0 100644
--- a/app/i18n/ru/sub.php
+++ b/app/i18n/ru/sub.php
@@ -1,6 +1,10 @@
<?php
return array(
+ 'api' => array(
+ 'documentation' => 'Copy the following URL to use it within an external tool.',// TODO
+ 'title' => 'API',// TODO
+ ),
'bookmarklet' => array(
'documentation' => 'Drag this button to your bookmarks toolbar or right-click it and choose "Bookmark This Link". Then click "Subscribe" button in any page you want to subscribe to.',// TODO
'label' => 'Subscribe',// TODO
diff --git a/app/i18n/tr/admin.php b/app/i18n/tr/admin.php
index 9d10ef9dd..aa3aad7b7 100644
--- a/app/i18n/tr/admin.php
+++ b/app/i18n/tr/admin.php
@@ -112,6 +112,13 @@ return array(
),
'title' => 'Eklentiler',
'user' => 'Kullanıcı eklentileri',
+ 'community' => 'Available community extensions', // @todo translate
+ 'name' => 'Name', // @todo translate
+ 'version' => 'Version', // @todo translate
+ 'description' => 'Description', // @todo translate
+ 'author' => 'Author', // @todo translate
+ 'latest' => 'Installed', // @todo translate
+ 'update' => 'Update available', // @todo translate
),
'stats' => array(
'_' => 'İstatistikler',
diff --git a/app/i18n/tr/gen.php b/app/i18n/tr/gen.php
index 81f926840..dcfd12c01 100644
--- a/app/i18n/tr/gen.php
+++ b/app/i18n/tr/gen.php
@@ -13,8 +13,8 @@ return array(
'filter' => 'Filtrele',
'import' => 'İçe Aktar',
'manage' => 'Yönet',
- 'mark_read' => 'Okundu olarak işaretle',
'mark_favorite' => 'Favoriye ekle',
+ 'mark_read' => 'Okundu olarak işaretle',
'remove' => 'Sil',
'see_website' => 'Siteyi gör',
'submit' => 'Onayla',
@@ -79,8 +79,8 @@ return array(
'last_year' => 'Geçen yıl',
'mar' => 'mar',
'march' => 'Mar',
- 'may_' => 'May',
'may' => 'Mayıs',
+ 'may_' => 'May',
'mon' => 'Pzt',
'month' => 'ay',
'nov' => 'kas',
@@ -119,6 +119,7 @@ return array(
'en' => 'English',
'es' => 'Español',
'fr' => 'Français',
+ 'he' => 'עברית',
'it' => 'Italiano',
'kr' => '한국어',
'nl' => 'Nederlands',
@@ -158,20 +159,21 @@ return array(
'previous' => 'Önceki',
),
'share' => array(
+ 'Known' => 'Known based sites',
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Email',
'facebook' => 'Facebook',
'g+' => 'Google+',
+ 'gnusocial' => 'GNU social',
+ 'jdh' => 'Journal du hacker',
+ 'mastodon' => 'Mastodon',
'movim' => 'Movim',
'print' => 'Print',
'shaarli' => 'Shaarli',
'twitter' => 'Twitter',
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
- 'jdh' => 'Journal du hacker',
- 'Known' => 'Known based sites',
- 'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => 'Tehlike!',
diff --git a/app/i18n/tr/sub.php b/app/i18n/tr/sub.php
index e928fae72..0bbaeec5b 100644
--- a/app/i18n/tr/sub.php
+++ b/app/i18n/tr/sub.php
@@ -1,6 +1,10 @@
<?php
return array(
+ 'api' => array(
+ 'documentation' => 'Copy the following URL to use it within an external tool.',// TODO
+ 'title' => 'API',// TODO
+ ),
'bookmarklet' => array(
'documentation' => 'Drag this button to your bookmarks toolbar or right-click it and choose "Bookmark This Link". Then click "Subscribe" button in any page you want to subscribe to.',// TODO
'label' => 'Subscribe',// TODO
diff --git a/app/i18n/zh-cn/admin.php b/app/i18n/zh-cn/admin.php
index 202114c75..ca18bf63d 100644
--- a/app/i18n/zh-cn/admin.php
+++ b/app/i18n/zh-cn/admin.php
@@ -9,9 +9,9 @@ return array(
'http' => 'HTTP (面向启用 HTTPS 的高级用户)',
'none' => '无 (危险)',
'title' => '认证',
- 'title_reset' => '认证重置',
+ 'title_reset' => '密码重置',
'token' => '认证口令',
- 'token_help' => '允许不经认证访问默认用户的 RSS 输出:',
+ 'token_help' => '用于不经认证访问默认用户的 RSS 输出:',
'type' => '认证方式',
'unsafe_autologin' => '允许不安全的自动登陆方式:',
),
@@ -21,8 +21,8 @@ return array(
'ok' => 'cache 目录权限正常。',
),
'categories' => array(
- 'nok' => '分类表配置错误。',
- 'ok' => '分类表正常。',
+ 'nok' => 'Category 表配置错误。',
+ 'ok' => 'Category 表正常。',
),
'connection' => array(
'nok' => '数据库连接失败。',
@@ -30,11 +30,11 @@ return array(
),
'ctype' => array(
'nok' => '找不到字符类型检测库 (php-ctype) 。',
- 'ok' => '你已有字符类型检测库 (ctype) 。',
+ 'ok' => '已找到字符类型检测库 (ctype) 。',
),
'curl' => array(
'nok' => '找不到 cURL 库 (php-curl package) 。',
- 'ok' => '你已有 cURL 库。',
+ 'ok' => '已找到 cURL 库。',
),
'data' => array(
'nok' => '请检查 <em>./data</em> 目录权限。HTTP 服务器必须有其写入权限。',
@@ -43,40 +43,40 @@ return array(
'database' => '数据库相关',
'dom' => array(
'nok' => '找不到用于浏览 DOM 的库 (php-xml) 。',
- 'ok' => '你已有用于浏览 DOM 的库。',
+ 'ok' => '已找到用于浏览 DOM 的库。',
),
'entries' => array(
- 'nok' => '条目表配置错误。',
- 'ok' => '条目表正常。',
+ 'nok' => 'Entry 表配置错误。',
+ 'ok' => 'Entry 表正常。',
),
'favicons' => array(
'nok' => '请检查 <em>./data/favicons</em> 目录权限。HTTP 服务器必须有其写入权限。',
'ok' => 'favicons 目录权限正常。',
),
'feeds' => array(
- 'nok' => 'RSS 源表配置错误。',
- 'ok' => 'RSS 源表正常。',
+ 'nok' => 'Feed 表配置错误。',
+ 'ok' => 'Feed 表正常。',
),
'fileinfo' => array(
'nok' => '找不到 PHP fileinfo 库 (fileinfo) 。',
- 'ok' => '你已有 fileinfo 库。',
+ 'ok' => '已找到 fileinfo 库。',
),
'files' => '文件相关',
'json' => array(
'nok' => '找不到 JSON 扩展 (php5-json ) 。',
- 'ok' => '你已有 JSON 扩展',
+ 'ok' => '已找到 JSON 扩展',
),
'minz' => array(
'nok' => '找不到 Minz 框架。',
- 'ok' => '你已有 Minz 框架。',
+ 'ok' => '已找到 Minz 框架。',
),
'pcre' => array(
'nok' => '找不到正则表达式解析库 (php-pcre) 。',
- 'ok' => '你已有正则表达式解析库 (PCRE) 。',
+ 'ok' => '已找到正则表达式解析库 (PCRE) 。',
),
'pdo' => array(
'nok' => '找不到 PDO 或支持的驱动 (pdo_mysql, pdo_sqlite, pdo_pgsql) 。',
- 'ok' => '你已有 PDO 和支持的至少一种驱动 (pdo_mysql, pdo_sqlite, pdo_pgsql) 。',
+ 'ok' => '已找到 PDO 和支持的至少一种驱动 (pdo_mysql, pdo_sqlite, pdo_pgsql) 。',
),
'php' => array(
'_' => 'PHP 相关',
@@ -98,7 +98,7 @@ return array(
),
'zip' => array(
'nok' => '找不到 ZIP 扩展 (php-zip) 。',
- 'ok' => '你已有 ZIP 扩展。',
+ 'ok' => '已找到 ZIP 扩展。',
),
),
'extensions' => array(
@@ -112,6 +112,13 @@ return array(
),
'title' => '扩展',
'user' => '用户扩展',
+ 'community' => 'Available community extensions', // @todo translate
+ 'name' => 'Name', // @todo translate
+ 'version' => 'Version', // @todo translate
+ 'description' => 'Description', // @todo translate
+ 'author' => 'Author', // @todo translate
+ 'latest' => 'Installed', // @todo translate
+ 'update' => 'Update available', // @todo translate
),
'stats' => array(
'_' => '统计',
diff --git a/app/i18n/zh-cn/conf.php b/app/i18n/zh-cn/conf.php
index fb62c48ef..1b52ac38f 100644
--- a/app/i18n/zh-cn/conf.php
+++ b/app/i18n/zh-cn/conf.php
@@ -8,7 +8,7 @@ return array(
'help' => '详细选项位于单独的 RSS 源设置',
'keep_history_by_feed' => '至少保存的文章数',
'optimize' => '优化数据库',
- 'optimize_help' => '不时地执行优化可以减少数据库大小',
+ 'optimize_help' => '偶尔执行优化可以减少数据库大小',
'purge_now' => '立即清除',
'title' => '存档',
'ttl' => '最小自动更新时间',
@@ -87,15 +87,15 @@ return array(
'articles_per_page' => '每页文章数',
'auto_load_more' => '在页面底部载入下一篇文章',
'auto_remove_article' => '阅读后隐藏文章',
- 'mark_updated_article_unread' => '将有更新的文章设为未读',
+ 'mark_updated_article_unread' => '有更新的文章设为未读',
'confirm_enabled' => '“全部设为已读”时显示确认对话框',
'display_articles_unfolded' => '默认展开文章',
'display_categories_unfolded' => '默认展开分类',
'hide_read_feeds' => '隐藏没有未读文章的分类或 RSS 源 (启用“显示所有文章”时不生效))',
- 'img_with_lazyload' => '使用 "lazy load" 模式加载图片',
+ 'img_with_lazyload' => '延迟加载图片',
'sides_close_article' => '点击文章外区域以关闭文章',
'jump_next' => '跳转到下一未读项 (RSS 源或分类)',
- 'number_divided_when_reader' => '阅读视图除以 2',
+ 'number_divided_when_reader' => '阅读视图中显示一半',
'read' => array(
'article_open_on_website' => '在打开原文章后',
'article_viewed' => '在文章被浏览后',
@@ -134,7 +134,7 @@ return array(
'print' => '打印',
'shaarli' => 'Shaarli',
'share_name' => '名称',
- 'share_url' => 'URL',
+ 'share_url' => '地址',
'title' => '分享',
'twitter' => 'Twitter',
'wallabag' => 'wallabag',
@@ -143,7 +143,7 @@ return array(
'_' => '快捷键',
'article_action' => '文章操作',
'auto_share' => '分享',
- 'auto_share_help' => '如果有多种分享模式,则会按照它们的编号访问。',
+ 'auto_share_help' => '如果有多种分享模式,则会按照它们的编号依次访问。',
'close_dropdown' => '关闭菜单',
'collapse_article' => '收起文章',
'first_article' => '跳转到第一篇文章',
@@ -163,7 +163,7 @@ return array(
'shift_for_all_read' => '+ <code>shift</code> 可以将全部文章设为已读',
'title' => '快捷键',
'user_filter' => '显示自定义查询',
- 'user_filter_help' => '如果有多个自定义过滤器,则会按照它们的编号访问。',
+ 'user_filter_help' => '如果有多个自定义过滤器,则会按照它们的编号依次访问。',
),
'user' => array(
'articles_and_size' => '%s 篇文章 (%s)',
diff --git a/app/i18n/zh-cn/feedback.php b/app/i18n/zh-cn/feedback.php
index bee5914e9..4ec833668 100644
--- a/app/i18n/zh-cn/feedback.php
+++ b/app/i18n/zh-cn/feedback.php
@@ -15,10 +15,10 @@ return array(
),
'login' => array(
'invalid' => '用户名或密码无效',
- 'success' => '你已成功登录',
+ 'success' => '登录成功',
),
'logout' => array(
- 'success' => '你已登出',
+ 'success' => '登出成功',
),
'no_password_set' => '管理员密码尚未设置。此特性不可用。',
),
@@ -43,7 +43,7 @@ return array(
'not_found' => '%s 不存在',
),
'import_export' => array(
- 'export_no_zip_extension' => '服务器未启用 ZIP 扩展。请尝试一个一个导出文件。',
+ 'export_no_zip_extension' => '服务器未启用 ZIP 扩展。请尝试逐个导出文件。',
'feeds_imported' => '你的 RSS 源已导入,即将更新',
'feeds_imported_with_errors' => '你的 RSS 源已导入,但发生错误',
'file_cannot_be_uploaded' => '文件未能上传!',
diff --git a/app/i18n/zh-cn/gen.php b/app/i18n/zh-cn/gen.php
index a69f02b3f..caaa388c7 100644
--- a/app/i18n/zh-cn/gen.php
+++ b/app/i18n/zh-cn/gen.php
@@ -13,8 +13,8 @@ return array(
'filter' => '过滤器',
'import' => '导入',
'manage' => '管理',
- 'mark_read' => '设为已读',
'mark_favorite' => '加入收藏',
+ 'mark_read' => '设为已读',
'remove' => '删除',
'see_website' => '查看网站',
'submit' => '提交',
@@ -34,7 +34,7 @@ return array(
'ask' => '创建新账户?',
'title' => '账户创建',
),
- 'reset' => '认证重置',
+ 'reset' => '密码重置',
'username' => array(
'_' => '用户名',
'admin' => '管理员用户名',
@@ -79,8 +79,8 @@ return array(
'last_year' => '去年',
'mar' => '三月',
'march' => '三月',
- 'may_' => '五月',
'may' => '五月',
+ 'may_' => '五月',
'mon' => '周一',
'month' => '个月',
'nov' => '十一月',
@@ -119,6 +119,7 @@ return array(
'en' => 'English',
'es' => 'Español',
'fr' => 'Français',
+ 'he' => 'עברית',
'it' => 'Italiano',
'kr' => '한국어',
'nl' => 'Nederlands',
@@ -158,25 +159,26 @@ return array(
'previous' => '上一页',
),
'share' => array(
+ 'Known' => 'Known based sites',
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Email',
'facebook' => 'Facebook',
'g+' => 'Google+',
+ 'gnusocial' => 'GNU social',
+ 'jdh' => 'Journal du hacker',
+ 'mastodon' => 'Mastodon',
'movim' => 'Movim',
'print' => 'Print',
'shaarli' => 'Shaarli',
'twitter' => 'Twitter',
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
- 'jdh' => 'Journal du hacker',
- 'Known' => 'Known based sites',
- 'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => '警告!',
'blank_to_disable' => '留空以禁用',
- 'by_author' => 'By <em>%s</em>',
+ 'by_author' => '作者 <em>%s</em>',
'by_default' => '默认',
'damn' => '错误!',
'default_category' => '未分类',
diff --git a/app/i18n/zh-cn/install.php b/app/i18n/zh-cn/install.php
index f247a20fd..1e172f0d5 100644
--- a/app/i18n/zh-cn/install.php
+++ b/app/i18n/zh-cn/install.php
@@ -38,11 +38,11 @@ return array(
),
'ctype' => array(
'nok' => '找不到字符类型检测库 (php-ctype) 。',
- 'ok' => '你已有字符类型检测库 (ctype) 。',
+ 'ok' => '已找到字符类型检测库 (ctype) 。',
),
'curl' => array(
'nok' => '找不到 cURL 库 (php-curl package) 。',
- 'ok' => '你已有 cURL 库。',
+ 'ok' => '已找到 cURL 库。',
),
'data' => array(
'nok' => '请检查 <em>./data</em> 目录权限。HTTP 服务器必须有其写入权限。',
@@ -50,7 +50,7 @@ return array(
),
'dom' => array(
'nok' => '找不到用于浏览 DOM 的库 (php-xml) 。',
- 'ok' => '你已有用于浏览 DOM 的库。',
+ 'ok' => '已找到用于浏览 DOM 的库。',
),
'favicons' => array(
'nok' => '请检查 <em>./data/favicons</em> 目录权限。HTTP 服务器必须有其写入权限。',
@@ -58,7 +58,7 @@ return array(
),
'fileinfo' => array(
'nok' => '找不到 PHP fileinfo 库 (fileinfo) 。',
- 'ok' => '你已有 fileinfo 库。',
+ 'ok' => '已找到 fileinfo 库。',
),
'http_referer' => array(
'nok' => '请检查你是否修改了 HTTP REFERER。',
@@ -66,19 +66,19 @@ return array(
),
'json' => array(
'nok' => '找不到推荐的 JSON 解析库。',
- 'ok' => '你已有推荐的 JSON 解析库。',
+ 'ok' => '已找到推荐的 JSON 解析库。',
),
'minz' => array(
'nok' => '找不到 Minz 框架。',
- 'ok' => '你已有 Minz 框架。',
+ 'ok' => '已找到 Minz 框架。',
),
'pcre' => array(
'nok' => '找不到正则表达式解析库 (php-pcre) 。',
- 'ok' => '你已有正则表达式解析库 (PCRE) 。',
+ 'ok' => '已找到正则表达式解析库 (PCRE) 。',
),
'pdo' => array(
'nok' => '找不到 PDO 或支持的驱动 (pdo_mysql, pdo_sqlite, pdo_pgsql) 。',
- 'ok' => '你已有 PDO 和支持的至少一种驱动 (pdo_mysql, pdo_sqlite, pdo_pgsql) 。',
+ 'ok' => '已找到 PDO 和支持的至少一种驱动 (pdo_mysql, pdo_sqlite, pdo_pgsql) 。',
),
'php' => array(
'nok' => '你的 PHP 版本为 %s,但 FreshRSS 最低需要 %s。',
@@ -90,7 +90,7 @@ return array(
),
'xml' => array(
'nok' => '找不到用于 XML 解析库。',
- 'ok' => '你已有 XML 解析库。',
+ 'ok' => '已找到 XML 解析库。',
),
),
'conf' => array(
diff --git a/app/i18n/zh-cn/sub.php b/app/i18n/zh-cn/sub.php
index 159bcd919..026f436d7 100644
--- a/app/i18n/zh-cn/sub.php
+++ b/app/i18n/zh-cn/sub.php
@@ -1,10 +1,14 @@
<?php
return array(
+ 'api' => array(
+ 'documentation' => '复制以下地址,可供外部工具使用',
+ 'title' => 'API',
+ ),
'bookmarklet' => array(
- 'documentation' => 'Drag this button to your bookmarks toolbar or right-click it and choose "Bookmark This Link". Then click "Subscribe" button in any page you want to subscribe to.',// TODO
- 'label' => 'Subscribe',// TODO
- 'title' => 'Bookmarklet',// TODO
+ 'documentation' => '拖动此书签到你的书签栏或者右键选择“收藏此链接”,然后在你想要订阅的页面上点击“订阅”按钮',
+ 'label' => '订阅',
+ 'title' => '书签应用',
),
'category' => array(
'_' => '分类',
@@ -18,20 +22,20 @@ return array(
'archiving' => '存档',
'auth' => array(
'configuration' => '认证',
- 'help' => '连接启用 HTTP 认证的 RSS 源',
+ 'help' => '用于连接启用 HTTP 认证的 RSS 源',
'http' => 'HTTP 认证',
'password' => 'HTTP 密码',
'username' => 'HTTP 用户名',
),
- 'css_help' => '获取全文(注意,会耗费更多时间!)',
- 'css_path' => '原网站中文章的 CSS 路径',
+ 'css_help' => '用于获取全文(注意,这将耗费更多时间!)',
+ 'css_path' => '原文的 CSS 选择器',
'description' => '描述',
'empty' => '此源为空。请确认它是否正常更新。',
- 'error' => '此源遇到一些问题。请确认它是否可访问后重试。',
+ 'error' => '此源遇到一些问题。请在确认是否能正常访问后重试。',
'in_main_stream' => '在首页中显示',
'informations' => '信息',
'keep_history' => '至少保存的文章数',
- 'moved_category_deleted' => '删除分类时,其中的 RSS 源会自动归类到<em>%s</em>。',
+ 'moved_category_deleted' => '删除分类时,其中的 RSS 源会自动归类到 <em>%s</em>',
'no_selected' => '未选择 RSS 源。',
'number_entries' => '%d 篇文章',
'stats' => '统计',
@@ -45,8 +49,8 @@ return array(
'pubsubhubbub' => 'PubSubHubbub 即时通知',
),
'firefox' => array(
- 'documentation' => 'Follow the steps described <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">here</a> to add FreshRSS to Firefox feed reader list.',// TODO
- 'title' => 'Firefox feed reader',// TODO
+ 'documentation' => '按照 <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">这里</a> 描述的步骤可将 FreshRSS 添加到 Firefox 阅读器列表',
+ 'title' => 'Firefox RSS 阅读器',
),
'import_export' => array(
'export' => '导出',
@@ -63,11 +67,11 @@ return array(
'bookmark' => '订阅 (FreshRSS 书签)',
'import_export' => '导入/导出',
'subscription_management' => '订阅管理',
- 'subscription_tools' => 'Subscription tools',// TODO
+ 'subscription_tools' => '订阅工具',
),
'title' => array(
'_' => '订阅管理',
'feed_management' => 'RSS 源管理',
- 'subscription_tools' => 'Subscription tools',// TODO
+ 'subscription_tools' => '订阅工具',
),
);
diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml
index 04ee03cd6..2bc693e5d 100644
--- a/app/layout/nav_menu.phtml
+++ b/app/layout/nav_menu.phtml
@@ -186,16 +186,16 @@
if (FreshRSS_Context::$order === 'DESC') {
$order = 'ASC';
$icon = 'up';
- $title = 'index.menu.older_first';
+ $title = _t('index.menu.older_first');
} else {
$order = 'DESC';
$icon = 'down';
- $title = 'index.menu.newer_first';
+ $title = _t('index.menu.newer_first');
}
$url_order = Minz_Request::currentRequest();
$url_order['params']['order'] = $order;
?>
- <a id="toggle-order" class="btn" href="<?php echo Minz_Url::display($url_order); ?>" title="<?php echo _t($title); ?>">
+ <a id="toggle-order" class="btn" href="<?php echo Minz_Url::display($url_order); ?>" title="<?php echo $title; ?>">
<?php echo _i($icon); ?>
</a>
diff --git a/app/views/configure/sharing.phtml b/app/views/configure/sharing.phtml
index ffcfb8b29..b0e6618fa 100644
--- a/app/views/configure/sharing.phtml
+++ b/app/views/configure/sharing.phtml
@@ -9,6 +9,8 @@
<input type="hidden" id="share_##key##_type" name="share[##key##][type]" value="##type##" /></div></div>'
data-advanced='<div class="form-group" id="group-share-##key##"><label class="group-name">##label##</label><div class="group-controls">
<input type="hidden" id="share_##key##_type" name="share[##key##][type]" value="##type##" />
+ <input type="hidden" id="share_##key##_method" name="share[##key##][method]" value="##method##" />
+ <input type="hidden" id="share_##key##_field" name="share[##key##][field]" value="##field##" />
<div class="stick">
<input type="text" id="share_##key##_name" name="share[##key##][name]" class="extend" value="" placeholder="<?php echo _t('conf.sharing.share_name'); ?>" size="64" />
<input type="url" id="share_##key##_url" name="share[##key##][url]" class="extend" value="" placeholder="<?php echo _t('conf.sharing.share_url'); ?>" size="64" />
@@ -28,6 +30,8 @@
</label>
<div class="group-controls">
<input type='hidden' id='share_<?php echo $key; ?>_type' name="share[<?php echo $key; ?>][type]" value='<?php echo $share->type(); ?>' />
+ <input type='hidden' id='share_<?php echo $key; ?>_method' name="share[<?php echo $key; ?>][method]" value='<?php echo $share->method(); ?>' />
+ <input type='hidden' id='share_<?php echo $key; ?>_field' name="share[<?php echo $key; ?>][field]" value='<?php echo $share->field(); ?>' />
<div class="stick">
<input type="text" id="share_<?php echo $key; ?>_name" name="share[<?php echo $key; ?>][name]" class="extend" value="<?php echo $share->name(); ?>" placeholder="<?php echo _t('conf.sharing.share_name'); ?>" size="64" data-leave-validation="<?php echo $share->name(); ?>"/>
<?php if ($share->formType() === 'advanced') { ?>
@@ -48,7 +52,7 @@
<div class="group-controls">
<select>
<?php foreach (FreshRSS_Share::enum() as $share) { ?>
- <option value='<?php echo $share->type(); ?>' data-form='<?php echo $share->formType(); ?>' data-help='<?php echo $share->help(); ?>'>
+ <option value='<?php echo $share->type(); ?>' data-form='<?php echo $share->formType(); ?>' data-help='<?php echo $share->help(); ?>' data-method='<?php echo $share->method(); ?>' data-field='<?php echo $share->field(); ?>'>
<?php echo $share->name(true); ?>
</option>
<?php } ?>
diff --git a/app/views/configure/system.phtml b/app/views/configure/system.phtml
index 935b49fda..37b68c991 100644
--- a/app/views/configure/system.phtml
+++ b/app/views/configure/system.phtml
@@ -33,7 +33,7 @@
<div class="group-controls">
<?php
$number = count(listUsers());
- echo _t($number > 1 ? 'admin.user.numbers' : 'admin.user.number', $number);
+ echo ($number > 1 ? _t('admin.user.numbers', $number) : _t('admin.user.number', $number));
?>
</div>
</div>
diff --git a/app/views/extension/index.phtml b/app/views/extension/index.phtml
index 7cb16bfff..6439a0333 100644
--- a/app/views/extension/index.phtml
+++ b/app/views/extension/index.phtml
@@ -26,13 +26,46 @@
}
?>
<?php
- }
+ }
- if (empty($this->extension_list['system']) && empty($this->extension_list['user'])) {
+ if (empty($this->extension_list['system']) && empty($this->extension_list['user'])) {
?>
<p class="alert alert-warn"><?php echo _t('admin.extensions.empty_list'); ?></p>
<?php } ?>
</form>
+
+ <?php if (!empty($this->available_extensions)) { ?>
+ <h2><?php echo _t('admin.extensions.community'); ?></h2>
+ <table>
+ <tr>
+ <th><?php echo _t('admin.extensions.name'); ?></th>
+ <th><?php echo _t('admin.extensions.version'); ?></th>
+ <th><?php echo _t('admin.extensions.author'); ?></th>
+ <th><?php echo _t('admin.extensions.description'); ?></th>
+ </tr>
+ <?php foreach ($this->available_extensions as $ext) { ?>
+ <tr>
+ <td><a href="<?php echo $ext['url']; ?>" target="_blank"><?php echo $ext['name']; ?></a></td>
+ <td><?php echo $ext['version']; ?></td>
+ <td><?php echo $ext['author']; ?></td>
+ <td>
+ <?php echo $ext['description']; ?>
+ <?php if (isset($this->extensions_installed[$ext['name']])) { ?>
+ <?php if (version_compare($this->extensions_installed[$ext['name']], $ext['version']) >= 0) { ?>
+ <span class="alert alert-success">
+ <?php echo _t('admin.extensions.latest'); ?>
+ </span>
+ <?php } else if ($this->extensions_installed[$ext['name']] != $ext['version']) { ?>
+ <span class="alert alert-warn">
+ <?php echo _t('admin.extensions.update'); ?>
+ </span>
+ <?php } ?>
+ <?php } ?>
+ </td>
+ </tr>
+ <?php } ?>
+ </table>
+ <?php } ?>
</div>
<?php $class = isset($this->extension) ? ' class="active"' : ''; ?>
diff --git a/app/views/helpers/index/normal/entry_bottom.phtml b/app/views/helpers/index/normal/entry_bottom.phtml
index a9d5a80ca..bc23938b0 100644
--- a/app/views/helpers/index/normal/entry_bottom.phtml
+++ b/app/views/helpers/index/normal/entry_bottom.phtml
@@ -52,7 +52,14 @@
$share_options['title'] = $title;
$share->update($share_options);
?><li class="item share">
- <a target="_blank" rel="noreferrer" href="<?php echo $share->url(); ?>"><?php echo $share->name(); ?></a>
+ <?php if ('GET' === $share->method()) {?>
+ <a target="_blank" rel="noreferrer" href="<?php echo $share->url(); ?>"><?php echo $share->name(); ?></a>
+ <?php } else {?>
+ <a href="POST"><?php echo $share->name(); ?></a>
+ <form method="POST" data-url="<?php echo $share->url(); ?>">
+ <input type="hidden" value="<?php echo $link; ?>" name="<?php echo $share->field(); ?>"/>
+ </form>
+ <?php } ?>
</li><?php
}
?></ul>
diff --git a/app/views/subscription/bookmarklet.phtml b/app/views/subscription/bookmarklet.phtml
index 162501be6..76ac700e0 100644
--- a/app/views/subscription/bookmarklet.phtml
+++ b/app/views/subscription/bookmarklet.phtml
@@ -10,4 +10,8 @@
<legend><?php echo _t('sub.firefox.title'); ?></legend>
<p><?php echo _t('sub.firefox.documentation'); ?></p>
<pre>browser.contentHandlers.types.number.uri → <?php echo Minz_Url::display(array('c' => 'feed', 'a' => 'add'), 'html', true); ?>&amp;url_rss=%s</pre>
+
+ <legend><?php echo _t('sub.api.title'); ?></legend>
+ <p><?php echo _t('sub.api.documentation'); ?></p>
+ <pre><?php echo Minz_Url::display(array('c' => 'feed', 'a' => 'add'), 'html', true); ?>&amp;url_rss=%s</pre>
</div> \ No newline at end of file
diff --git a/app/views/update/index.phtml b/app/views/update/index.phtml
index da1bc7ef5..0599d5b0d 100644
--- a/app/views/update/index.phtml
+++ b/app/views/update/index.phtml
@@ -14,7 +14,21 @@
</p>
<?php if (!empty($this->message)) { ?>
- <p class="alert <?php echo $this->message['status'] === 'bad' ? 'alert-error' : 'alert-warn'; ?>">
+ <?php
+ $class = 'alert-warn';
+ switch ($this->message['status']) {
+ case 'bad':
+ $class = 'alert-error';
+ break;
+ case 'latest':
+ $class = 'alert-success';
+ break;
+ default:
+ $class = 'alert-warn';
+ break;
+ }
+ ?>
+ <p class="alert <?php echo $class; ?>">
<span class="alert-head"><?php echo $this->message['title']; ?></span>
<?php echo $this->message['body']; ?>
</p>
diff --git a/app/views/user/profile.phtml b/app/views/user/profile.phtml
index f09c87765..7a63c0941 100644
--- a/app/views/user/profile.phtml
+++ b/app/views/user/profile.phtml
@@ -38,7 +38,7 @@
<input type="password" id="apiPasswordPlain" name="apiPasswordPlain" autocomplete="off" pattern=".{7,}" <?php echo cryptAvailable() ? '' : 'disabled="disabled" '; ?>/>
<a class="btn toggle-password" data-toggle="apiPasswordPlain"><?php echo _i('key'); ?></a>
</div>
- <?php echo _i('help'); ?> <kbd><a href="../api/"><?php echo Minz_Url::display('/api/greader.php', 'html', true); ?></a></kbd>
+ <?php echo _i('help'); ?> <kbd><a href="../api/"><?php echo Minz_Url::display('/api/', 'html', true); ?></a></kbd>
</div>
</div>
<?php } ?>
diff --git a/cli/README.md b/cli/README.md
index ce1be10a7..b7e8ac7f5 100644
--- a/cli/README.md
+++ b/cli/README.md
@@ -69,6 +69,9 @@ cd /usr/share/FreshRSS
# Returns: 1) a * iff the user is admin, 2) the name of the user,
# 3) the date/time of last user action, 4) the size occupied,
# and the number of: 5) categories, 6) feeds, 7) read articles, 8) unread articles, and 9) favourites
+
+./cli/db-optimize.php --user username
+# Optimize database (reduces the size) for a given user (perform `OPTIMIZE TABLE` in MySQL, `VACUUM` in SQLite)
```
@@ -96,51 +99,5 @@ Example to get the number of feeds of a given user:
# Install and updates
-## Using git
-
-If you manage FreshRSS via command line, then installing and updating FreshRSS can be done via git:
-
-```sh
-# If your local user does not have write access, prefix all commands by sudo:
-sudo ...
-
-# Install FreshRSS
-cd /usr/share/
-git clone https://github.com/FreshRSS/FreshRSS.git
-
-# Perform all commands below in your FreshRSS directory:
-cd /usr/share/FreshRSS
-
-# Use the development version of FreshRSS
-git checkout -b dev origin/dev
-
-# Check out a specific version of FreshRSS
-# See release names on https://github.com/FreshRSS/FreshRSS/releases
-# You will then need to manually change version
-# or checkout master or dev branch to get new versions
-git checkout 1.7.0
-
-# Verify what branch is used
-git branch
-
-# Check whether there is a new version of FreshRSS,
-# assuming you are on the /master or /dev branch
-git fetch --all
-git status
-
-# Discard manual changes (do a backup before)
-git reset --hard
-# Then re-delete the file forcing the setup wizard
-rm data/do-install.txt
-
-# Delete manual additions (do a backup before)
-git clean -f -d
-
-# Update to a newer version of FreshRSS,
-# assuming you are on the /master or /dev branch
-git pull
-
-# Set the rights so that your Web server can access the files
-# (Example for Debian / Ubuntu)
-chown -R :www-data . && chmod -R g+r . && chmod -R g+w ./data/
-```
+If you want to administrate FreshRSS using git, please read our [installation docs](https://freshrss.github.io/FreshRSS/en/admins/02_Installation.html)
+and [update guidelines](https://freshrss.github.io/FreshRSS/en/users/08_Updating.html). \ No newline at end of file
diff --git a/cli/_cli.php b/cli/_cli.php
index 1b26ea738..72629171c 100644
--- a/cli/_cli.php
+++ b/cli/_cli.php
@@ -3,8 +3,9 @@ if (php_sapi_name() !== 'cli') {
die('FreshRSS error: This PHP script may only be invoked from command line!');
}
-require(dirname(__FILE__) . '/../constants.php');
+require(__DIR__ . '/../constants.php');
require(LIB_PATH . '/lib_rss.php');
+require(LIB_PATH . '/lib_install.php');
Minz_Configuration::register('system',
DATA_PATH . '/config.php',
@@ -47,3 +48,19 @@ function done($ok = true) {
fwrite(STDERR, 'Result: ' . ($ok ? 'success' : 'fail') . "\n");
exit($ok ? 0 : 1);
}
+
+function performRequirementCheck($databaseType) {
+ $requirements = checkRequirements($databaseType);
+ if ($requirements['all'] !== 'ok') {
+ $message = 'FreshRSS install failed requirements:' . "\n";
+ foreach ($requirements as $requirement => $check) {
+ if ($check !== 'ok' && !in_array($requirement, array('all', 'pdo', 'message'))) {
+ $message .= '• ' . $requirement . "\n";
+ }
+ }
+ if (!empty($requirements['message'])) {
+ $message .= '• ' . $requirements['message'] . "\n";
+ }
+ fail($message);
+ }
+}
diff --git a/cli/_update-or-create-user.php b/cli/_update-or-create-user.php
index 15397f1f6..a5960b58a 100644
--- a/cli/_update-or-create-user.php
+++ b/cli/_update-or-create-user.php
@@ -1,5 +1,5 @@
<?php
-require('_cli.php');
+require(__DIR__ . '/_cli.php');
$params = array(
'user:',
diff --git a/cli/actualize-user.php b/cli/actualize-user.php
index 932c6975c..dd07fc142 100755
--- a/cli/actualize-user.php
+++ b/cli/actualize-user.php
@@ -1,6 +1,6 @@
#!/usr/bin/php
<?php
-require('_cli.php');
+require(__DIR__ . '/_cli.php');
$options = getopt('', array(
'user:',
diff --git a/cli/check.translation.php b/cli/check.translation.php
new file mode 100644
index 000000000..6ebd12973
--- /dev/null
+++ b/cli/check.translation.php
@@ -0,0 +1,106 @@
+<?php
+
+require_once __DIR__ . '/i18n/I18nFile.php';
+require_once __DIR__ . '/i18n/I18nCompletionValidator.php';
+require_once __DIR__ . '/i18n/I18nUsageValidator.php';
+
+$i18nFile = new I18nFile();
+$i18nData = $i18nFile->load();
+
+$options = getopt("dhl:r");
+
+if (array_key_exists('h', $options)) {
+ help();
+}
+if (array_key_exists('l', $options)) {
+ $languages = array($options['l']);
+} else {
+ $languages = $i18nData->getAvailableLanguages();
+}
+$displayResults = array_key_exists('d', $options);
+$displayReport = array_key_exists('r', $options);
+
+$isValidated = true;
+$result = array();
+$report = array();
+
+foreach ($languages as $language) {
+ if ($language === $i18nData::REFERENCE_LANGUAGE) {
+ $i18nValidator = new I18nUsageValidator($i18nData->getReferenceLanguage(), findUsedTranslations());
+ $isValidated = $i18nValidator->validate(include __DIR__ . '/i18n/ignore/' . $language . '.php') && $isValidated;
+ } else {
+ $i18nValidator = new I18nCompletionValidator($i18nData->getReferenceLanguage(), $i18nData->getLanguage($language));
+ if (file_exists(__DIR__ . '/i18n/ignore/' . $language . '.php')) {
+ $isValidated = $i18nValidator->validate(include __DIR__ . '/i18n/ignore/' . $language . '.php') && $isValidated;
+ } else {
+ $isValidated = $i18nValidator->validate(null) && $isValidated;
+ }
+ }
+
+ $report[$language] = sprintf('%-5s - %s', $language, $i18nValidator->displayReport());
+ $result[$language] = $i18nValidator->displayResult();
+}
+
+if ($displayResults) {
+ foreach ($result as $lang => $value) {
+ echo 'Language: ', $lang, PHP_EOL;
+ print_r($value);
+ echo PHP_EOL;
+ }
+}
+
+if ($displayReport) {
+ foreach ($report as $value) {
+ echo $value;
+ }
+}
+
+if (!$isValidated) {
+ exit(1);
+}
+
+/**
+ * Find used translation keys in the project
+ *
+ * Iterates through all php and phtml files in the whole project and extracts all
+ * translation keys used.
+ *
+ * @return array
+ */
+function findUsedTranslations() {
+ $directory = new RecursiveDirectoryIterator(__DIR__ . '/..');
+ $iterator = new RecursiveIteratorIterator($directory);
+ $regex = new RegexIterator($iterator, '/^.+\.(php|phtml)$/i', RecursiveRegexIterator::GET_MATCH);
+ $usedI18n = array();
+ foreach (array_keys(iterator_to_array($regex)) as $file) {
+ $fileContent = file_get_contents($file);
+ preg_match_all('/_t\([\'"](?P<strings>[^\'"]+)[\'"]/', $fileContent, $matches);
+ $usedI18n = array_merge($usedI18n, $matches['strings']);
+ }
+ return $usedI18n;
+}
+
+/**
+ * Output help message.
+ */
+function help() {
+ $help = <<<HELP
+NAME
+ %s
+
+SYNOPSIS
+ php %s [OPTION]...
+
+DESCRIPTION
+ Check if translation files have missing keys or missing translations.
+
+ -d display results.
+ -h display this help and exit.
+ -l=LANG filter by LANG.
+ -r display completion report.
+
+HELP;
+ $file = str_replace(__DIR__ . '/', '', __FILE__);
+ echo sprintf($help, $file, $file);
+ exit;
+}
diff --git a/cli/create-user.php b/cli/create-user.php
index 1b6be3153..5bc6c1e6c 100755
--- a/cli/create-user.php
+++ b/cli/create-user.php
@@ -1,7 +1,7 @@
#!/usr/bin/php
<?php
$isUpdate = false;
-require('_update-or-create-user.php');
+require(__DIR__ . '/_update-or-create-user.php');
$username = $options['user'];
if (!FreshRSS_user_Controller::checkUsername($username)) {
diff --git a/cli/db-optimize.php b/cli/db-optimize.php
new file mode 100755
index 000000000..39dc97638
--- /dev/null
+++ b/cli/db-optimize.php
@@ -0,0 +1,20 @@
+#!/usr/bin/php
+<?php
+require(__DIR__ . '/_cli.php');
+
+$options = getopt('', array(
+ 'user:',
+ ));
+
+if (empty($options['user'])) {
+ fail('Usage: ' . basename(__FILE__) . " --user username");
+}
+
+$username = cliInitUser($options['user']);
+
+echo 'FreshRSS optimizing database for user “', $username, "”…\n";
+
+$databaseDAO = FreshRSS_Factory::createDatabaseDAO($username);
+$ok = $databaseDAO->optimize();
+
+done($ok);
diff --git a/cli/delete-user.php b/cli/delete-user.php
index baa81b893..30cc31754 100755
--- a/cli/delete-user.php
+++ b/cli/delete-user.php
@@ -1,6 +1,6 @@
#!/usr/bin/php
<?php
-require('_cli.php');
+require(__DIR__ . '/_cli.php');
$options = getopt('', array(
'user:',
diff --git a/cli/do-install.php b/cli/do-install.php
index 74bbdfcd4..4ebba0469 100755
--- a/cli/do-install.php
+++ b/cli/do-install.php
@@ -1,7 +1,6 @@
#!/usr/bin/php
<?php
-require('_cli.php');
-require(LIB_PATH . '/lib_install.php');
+require(__DIR__ . '/_cli.php');
if (!file_exists(DATA_PATH . '/do-install.txt')) {
fail('FreshRSS looks to be already installed! Please use `./cli/reconfigure.php` instead.');
@@ -66,19 +65,7 @@ foreach ($dBparams as $dBparam) {
}
}
-$requirements = checkRequirements($config['db']['type']);
-if ($requirements['all'] !== 'ok') {
- $message = 'FreshRSS install failed requirements:' . "\n";
- foreach ($requirements as $requirement => $check) {
- if ($check !== 'ok' && !in_array($requirement, array('all', 'pdo', 'message'))) {
- $message .= '• ' . $requirement . "\n";
- }
- }
- if (!empty($requirements['message'])) {
- $message .= '• ' . $requirements['message'] . "\n";
- }
- fail($message);
-}
+performRequirementCheck($config['db']['type']);
if (!FreshRSS_user_Controller::checkUsername($options['default_user'])) {
fail('FreshRSS error: invalid default username “' . $options['default_user']
diff --git a/cli/export-opml-for-user.php b/cli/export-opml-for-user.php
index 95b12281f..fd0993c35 100755
--- a/cli/export-opml-for-user.php
+++ b/cli/export-opml-for-user.php
@@ -1,6 +1,6 @@
#!/usr/bin/php
<?php
-require('_cli.php');
+require(__DIR__ . '/_cli.php');
$options = getopt('', array(
'user:',
diff --git a/cli/export-zip-for-user.php b/cli/export-zip-for-user.php
index 92fe9bf9a..916ecbb45 100755
--- a/cli/export-zip-for-user.php
+++ b/cli/export-zip-for-user.php
@@ -1,6 +1,6 @@
#!/usr/bin/php
<?php
-require('_cli.php');
+require(__DIR__ . '/_cli.php');
$options = getopt('', array(
'user:',
diff --git a/cli/i18n/I18nCompletionValidator.php b/cli/i18n/I18nCompletionValidator.php
new file mode 100644
index 000000000..2cb71acd5
--- /dev/null
+++ b/cli/i18n/I18nCompletionValidator.php
@@ -0,0 +1,49 @@
+<?php
+
+require_once __DIR__ . '/I18nValidatorInterface.php';
+
+class I18nCompletionValidator implements I18nValidatorInterface {
+
+ private $reference;
+ private $language;
+ private $totalEntries = 0;
+ private $passEntries = 0;
+ private $result = '';
+
+ public function __construct($reference, $language) {
+ $this->reference = $reference;
+ $this->language = $language;
+ }
+
+ public function displayReport() {
+ return sprintf('Translation is %5.1f%% complete.', $this->passEntries / $this->totalEntries * 100) . PHP_EOL;
+ }
+
+ public function displayResult() {
+ return $this->result;
+ }
+
+ public function validate($ignore) {
+ foreach ($this->reference as $file => $data) {
+ foreach ($data as $key => $value) {
+ $this->totalEntries++;
+ if (is_array($ignore) && in_array($key, $ignore)) {
+ $this->passEntries++;
+ continue;
+ }
+ if (!array_key_exists($key, $this->language[$file])) {
+ $this->result .= sprintf('Missing key %s', $key) . PHP_EOL;
+ continue;
+ }
+ if ($value === $this->language[$file][$key]) {
+ $this->result .= sprintf('Untranslated key %s - %s', $key, $value) . PHP_EOL;
+ continue;
+ }
+ $this->passEntries++;
+ }
+ }
+
+ return $this->totalEntries === $this->passEntries;
+ }
+
+}
diff --git a/cli/i18n/I18nData.php b/cli/i18n/I18nData.php
new file mode 100644
index 000000000..cd8ba0765
--- /dev/null
+++ b/cli/i18n/I18nData.php
@@ -0,0 +1,118 @@
+<?php
+
+class I18nData {
+
+ const REFERENCE_LANGUAGE = 'en';
+
+ private $data = array();
+ private $originalData = array();
+
+ public function __construct($data) {
+ $this->data = $data;
+ $this->originalData = $data;
+ }
+
+ public function getData() {
+ return $this->data;
+ }
+
+ /**
+ * Return the available languages
+ *
+ * @return array
+ */
+ public function getAvailableLanguages() {
+ $languages = array_keys($this->data);
+ sort($languages);
+
+ return $languages;
+ }
+
+ /**
+ * Add a new language. It's a copy of the reference language.
+ *
+ * @param string $language
+ */
+ public function addLanguage($language) {
+ if (array_key_exists($language, $this->data)) {
+ throw new Exception('The selected language already exist.');
+ }
+ $this->data[$language] = $this->data[static::REFERENCE_LANGUAGE];
+ }
+
+ /**
+ * Add a key in the reference language
+ *
+ * @param string $key
+ * @param string $value
+ */
+ public function addKey($key, $value) {
+ if (array_key_exists($key, $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)])) {
+ throw new Exception('The selected key already exist.');
+ }
+ $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)][$key] = $value;
+ }
+
+ /**
+ * Duplicate a key from the reference language to all other languages
+ *
+ * @param string $key
+ */
+ public function duplicateKey($key) {
+ if (!array_key_exists($key, $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)])) {
+ throw new Exception('The selected key does not exist.');
+ }
+ $value = $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)][$key];
+ foreach ($this->getAvailableLanguages() as $language) {
+ if (static::REFERENCE_LANGUAGE === $language) {
+ continue;
+ }
+ if (array_key_exists($key, $this->data[$language][$this->getFilenamePrefix($key)])) {
+ throw new Exception(sprintf('The selected key already exist in %s.', $language));
+ }
+ $this->data[$language][$this->getFilenamePrefix($key)][$key] = $value;
+ }
+ }
+
+ /**
+ * Remove a key in all languages
+ *
+ * @param string $key
+ */
+ public function removeKey($key) {
+ if (!array_key_exists($key, $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)])) {
+ throw new Exception('The selected key does not exist.');
+ }
+ foreach ($this->getAvailableLanguages() as $language) {
+ if (array_key_exists($key, $this->data[$language][$this->getFilenamePrefix($key)])) {
+ unset($this->data[$language][$this->getFilenamePrefix($key)][$key]);
+ }
+ }
+ }
+
+ /**
+ * Check if the data has changed
+ *
+ * @return bool
+ */
+ public function hasChanged() {
+ return $this->data !== $this->originalData;
+ }
+
+ public function getLanguage($language) {
+ return $this->data[$language];
+ }
+
+ public function getReferenceLanguage() {
+ return $this->getLanguage(static::REFERENCE_LANGUAGE);
+ }
+
+ /**
+ * @param string $key
+ * @return string
+ */
+ private function getFilenamePrefix($key) {
+ return preg_replace('/\..*/', '.php', $key);
+ }
+
+}
diff --git a/cli/i18n/I18nFile.php b/cli/i18n/I18nFile.php
new file mode 100644
index 000000000..d6489ee21
--- /dev/null
+++ b/cli/i18n/I18nFile.php
@@ -0,0 +1,92 @@
+<?php
+
+require_once __DIR__ . '/I18nData.php';
+
+class i18nFile {
+
+ private $i18nPath;
+
+ public function __construct() {
+ $this->i18nPath = __DIR__ . '/../../app/i18n';
+ }
+
+ public function load() {
+ $dirs = new DirectoryIterator($this->i18nPath);
+ foreach ($dirs as $dir) {
+ if ($dir->isDot()) {
+ continue;
+ }
+ $files = new DirectoryIterator($dir->getPathname());
+ foreach ($files as $file) {
+ if (!$file->isFile()) {
+ continue;
+ }
+ $i18n[$dir->getFilename()][$file->getFilename()] = $this->flatten(include $file->getPathname(), $file->getBasename('.php'));
+ }
+ }
+
+ return new I18nData($i18n);
+ }
+
+ public function dump(I18nData $i18n) {
+ foreach ($i18n->getData() as $language => $file) {
+ $dir = $this->i18nPath . DIRECTORY_SEPARATOR . $language;
+ if (!file_exists($dir)) {
+ mkdir($dir);
+ }
+ foreach ($file as $name => $content) {
+ $filename = $dir . DIRECTORY_SEPARATOR . $name;
+ $fullContent = var_export($this->unflatten($content), true);
+ file_put_contents($filename, sprintf('<?php return %s;', $fullContent));
+ }
+ }
+ }
+
+ /**
+ * Flatten an array of translation
+ *
+ * @param array $translation
+ * @param string $prefix
+ * @return array
+ */
+ private function flatten($translation, $prefix = '') {
+ $a = array();
+
+ if ('' !== $prefix) {
+ $prefix .= '.';
+ }
+
+ foreach ($translation as $key => $value) {
+ if (is_array($value)) {
+ $a += $this->flatten($value, $prefix . $key);
+ } else {
+ $a[$prefix . $key] = $value;
+ }
+ }
+
+ return $a;
+ }
+
+ /**
+ * Unflatten an array of translation
+ *
+ * The first key is dropped since it represents the filename and we have
+ * no use of it.
+ *
+ * @param array $translation
+ * @return array
+ */
+ private function unflatten($translation) {
+ $a = array();
+
+ ksort($translation);
+ foreach ($translation as $compoundKey => $value) {
+ $keys = explode('.', $compoundKey);
+ array_shift($keys);
+ eval("\$a['" . implode("']['", $keys) . "'] = '" . $value . "';");
+ }
+
+ return $a;
+ }
+
+}
diff --git a/cli/i18n/I18nUsageValidator.php b/cli/i18n/I18nUsageValidator.php
new file mode 100644
index 000000000..8ab934971
--- /dev/null
+++ b/cli/i18n/I18nUsageValidator.php
@@ -0,0 +1,47 @@
+<?php
+
+require_once __DIR__ . '/I18nValidatorInterface.php';
+
+class I18nUsageValidator implements I18nValidatorInterface {
+
+ private $code;
+ private $reference;
+ private $totalEntries = 0;
+ private $failedEntries = 0;
+ private $result = '';
+
+ public function __construct($reference, $code) {
+ $this->code = $code;
+ $this->reference = $reference;
+ }
+
+ public function displayReport() {
+ return sprintf('%5.1f%% of translation keys are unused.', $this->failedEntries / $this->totalEntries * 100) . PHP_EOL;
+ }
+
+ public function displayResult() {
+ return $this->result;
+ }
+
+ public function validate($ignore) {
+ foreach ($this->reference as $file => $data) {
+ foreach ($data as $key => $value) {
+ $this->totalEntries++;
+ if (preg_match('/\._$/', $key) && in_array(preg_replace('/\._$/', '', $key), $this->code)) {
+ continue;
+ }
+ if (is_array($ignore) && in_array($key, $ignore)) {
+ continue;
+ }
+ if (!in_array($key, $this->code)) {
+ $this->result .= sprintf('Unused key %s - %s', $key, $value) . PHP_EOL;
+ $this->failedEntries++;
+ continue;
+ }
+ }
+ }
+
+ return 0 === $this->failedEntries;
+ }
+
+}
diff --git a/cli/i18n/I18nValidatorInterface.php b/cli/i18n/I18nValidatorInterface.php
new file mode 100644
index 000000000..edfe7aac0
--- /dev/null
+++ b/cli/i18n/I18nValidatorInterface.php
@@ -0,0 +1,26 @@
+<?php
+
+interface I18nValidatorInterface {
+
+ /**
+ * Display the validation result.
+ * Empty if there are no errors.
+ *
+ * @return array
+ */
+ public function displayResult();
+
+ /**
+ * @param array $ignore Keys to ignore for validation
+ * @return bool
+ */
+ public function validate($ignore);
+
+ /**
+ * Display the validation report.
+ *
+ * @return array
+ */
+ public function displayReport();
+
+}
diff --git a/cli/i18n/ignore/en.php b/cli/i18n/ignore/en.php
new file mode 100644
index 000000000..e231afdda
--- /dev/null
+++ b/cli/i18n/ignore/en.php
@@ -0,0 +1,105 @@
+<?php
+
+return array(
+ 'admin.check_install.cache.nok',
+ 'admin.check_install.cache.ok',
+ 'admin.check_install.categories.nok',
+ 'admin.check_install.categories.ok',
+ 'admin.check_install.connection.nok',
+ 'admin.check_install.connection.ok',
+ 'admin.check_install.ctype.nok',
+ 'admin.check_install.ctype.ok',
+ 'admin.check_install.curl.nok',
+ 'admin.check_install.curl.ok',
+ 'admin.check_install.data.nok',
+ 'admin.check_install.data.ok',
+ 'admin.check_install.dom.nok',
+ 'admin.check_install.dom.ok',
+ 'admin.check_install.entries.nok',
+ 'admin.check_install.entries.ok',
+ 'admin.check_install.favicons.nok',
+ 'admin.check_install.favicons.ok',
+ 'admin.check_install.feeds.nok',
+ 'admin.check_install.feeds.ok',
+ 'admin.check_install.fileinfo.nok',
+ 'admin.check_install.fileinfo.ok',
+ 'admin.check_install.json.nok',
+ 'admin.check_install.json.ok',
+ 'admin.check_install.minz.nok',
+ 'admin.check_install.minz.ok',
+ 'admin.check_install.pcre.nok',
+ 'admin.check_install.pcre.ok',
+ 'admin.check_install.pdo.nok',
+ 'admin.check_install.pdo.ok',
+ 'admin.check_install.php.nok',
+ 'admin.check_install.php.ok',
+ 'admin.check_install.tables.nok',
+ 'admin.check_install.tables.ok',
+ 'admin.check_install.tokens.nok',
+ 'admin.check_install.tokens.ok',
+ 'admin.check_install.users.nok',
+ 'admin.check_install.users.ok',
+ 'admin.check_install.zip.nok',
+ 'admin.check_install.zip.ok',
+ 'conf.query.get_all',
+ 'conf.query.get_category',
+ 'conf.query.get_favorite',
+ 'conf.query.get_feed',
+ 'conf.query.order_asc',
+ 'conf.query.order_desc',
+ 'conf.query.state_0',
+ 'conf.query.state_1',
+ 'conf.query.state_2',
+ 'conf.query.state_3',
+ 'conf.query.state_4',
+ 'conf.query.state_5',
+ 'conf.query.state_6',
+ 'conf.query.state_7',
+ 'conf.query.state_8',
+ 'conf.query.state_9',
+ 'conf.query.state_10',
+ 'conf.query.state_11',
+ 'conf.query.state_12',
+ 'conf.query.state_13',
+ 'conf.query.state_14',
+ 'conf.query.state_15',
+ 'conf.sharing.blogotext',
+ 'conf.sharing.diaspora',
+ 'conf.sharing.email',
+ 'conf.sharing.facebook',
+ 'conf.sharing.g+',
+ 'conf.sharing.print',
+ 'conf.sharing.shaarli',
+ 'conf.sharing.twitter',
+ 'conf.sharing.wallabag',
+ 'gen.lang.cz',
+ 'gen.lang.de',
+ 'gen.lang.en',
+ 'gen.lang.es',
+ 'gen.lang.fr',
+ 'gen.lang.it',
+ 'gen.lang.kr',
+ 'gen.lang.nl',
+ 'gen.lang.pt-br',
+ 'gen.lang.ru',
+ 'gen.lang.tr',
+ 'gen.lang.zh-cn',
+ 'gen.share.blogotext',
+ 'gen.share.diaspora',
+ 'gen.share.email',
+ 'gen.share.facebook',
+ 'gen.share.g+',
+ 'gen.share.movim',
+ 'gen.share.print',
+ 'gen.share.shaarli',
+ 'gen.share.twitter',
+ 'gen.share.wallabag',
+ 'gen.share.wallabagv2',
+ 'gen.share.jdh',
+ 'gen.share.Known',
+ 'gen.share.gnusocial',
+ 'index.menu.non-starred',
+ 'index.menu.read',
+ 'index.menu.starred',
+ 'index.menu.unread',
+);
diff --git a/cli/i18n/ignore/fr.php b/cli/i18n/ignore/fr.php
new file mode 100644
index 000000000..0ac2e8758
--- /dev/null
+++ b/cli/i18n/ignore/fr.php
@@ -0,0 +1,55 @@
+<?php
+
+return array(
+ 'admin.extensions.title',
+ 'admin.stats.number_entries',
+ 'admin.user.articles_and_size',
+ 'conf.display.width.large',
+ 'conf.sharing.blogotext',
+ 'conf.sharing.diaspora',
+ 'conf.sharing.facebook',
+ 'conf.sharing.g+',
+ 'conf.sharing.print',
+ 'conf.sharing.shaarli',
+ 'conf.sharing.twitter',
+ 'conf.sharing.wallabag',
+ 'conf.shortcut.navigation',
+ 'conf.user.articles_and_size',
+ 'gen.freshrss._',
+ 'gen.lang.cz',
+ 'gen.lang.de',
+ 'gen.lang.en',
+ 'gen.lang.es',
+ 'gen.lang.fr',
+ 'gen.lang.it',
+ 'gen.lang.kr',
+ 'gen.lang.nl',
+ 'gen.lang.pt-br',
+ 'gen.lang.ru',
+ 'gen.lang.tr',
+ 'gen.lang.zh-cn',
+ 'gen.menu.admin',
+ 'gen.menu.configuration',
+ 'gen.menu.extensions',
+ 'gen.menu.logs',
+ 'gen.share.blogotext',
+ 'gen.share.diaspora',
+ 'gen.share.facebook',
+ 'gen.share.g+',
+ 'gen.share.movim',
+ 'gen.share.shaarli',
+ 'gen.share.twitter',
+ 'gen.share.wallabag',
+ 'gen.share.wallabagv2',
+ 'gen.share.jdh',
+ 'gen.share.gnusocial',
+ 'index.about.agpl3',
+ 'index.about.version',
+ 'index.log._',
+ 'index.log.title',
+ 'install.title',
+ 'install.this_is_the_end',
+ 'sub.bookmarklet.title',
+ 'sub.feed.description',
+ 'sub.feed.number_entries',
+);
diff --git a/cli/import-for-user.php b/cli/import-for-user.php
index 29084f062..95ff18c8c 100755
--- a/cli/import-for-user.php
+++ b/cli/import-for-user.php
@@ -1,6 +1,6 @@
#!/usr/bin/php
<?php
-require('_cli.php');
+require(__DIR__ . '/_cli.php');
$options = getopt('', array(
'user:',
diff --git a/cli/list-users.php b/cli/list-users.php
index 00a0b2b2c..758bbdb46 100755
--- a/cli/list-users.php
+++ b/cli/list-users.php
@@ -1,6 +1,6 @@
#!/usr/bin/php
<?php
-require('_cli.php');
+require(__DIR__ . '/_cli.php');
$users = listUsers();
sort($users);
diff --git a/cli/manipulate.translation.php b/cli/manipulate.translation.php
new file mode 100644
index 000000000..aace5723a
--- /dev/null
+++ b/cli/manipulate.translation.php
@@ -0,0 +1,79 @@
+<?php
+
+$options = getopt("h");
+
+if (array_key_exists('h', $options)) {
+ help();
+}
+
+if (1 === $argc || 4 < $argc) {
+ help();
+}
+
+require_once __DIR__ . '/i18n/I18nFile.php';
+
+$i18nFile = new I18nFile();
+$i18nData = $i18nFile->load();
+
+switch ($argv[1]) {
+ case 'add_language' :
+ $i18nData->addLanguage($argv[2]);
+ break;
+ case 'add_key' :
+ if (3 === $argc) {
+ help();
+ }
+ $i18nData->addKey($argv[2], $argv[3]);
+ break;
+ case 'duplicate_key' :
+ $i18nData->duplicateKey($argv[2]);
+ break;
+ case 'delete_key' :
+ $i18nData->removeKey($argv[2]);
+ break;
+ default :
+ help();
+}
+
+if ($i18nData->hasChanged()) {
+ $i18nFile->dump($i18nData);
+}
+
+/**
+ * Output help message.
+ */
+function help() {
+ $help = <<<HELP
+NAME
+ %s
+
+SYNOPSIS
+ php %s [OPTION] [OPERATION] [KEY] [VALUE]
+
+DESCRIPTION
+ Manipulate translation files. Available operations are
+ Check if translation files have missing keys or missing translations.
+
+ -h display this help and exit.
+
+OPERATION
+ add_language
+ add a new language by duplicating the referential. This operation
+ needs only a KEY.
+
+ add_key add a new key in the referential. This operation needs a KEY and
+ a VALUE.
+
+ duplicate_key
+ duplicate a referential key in other languages. This operation
+ needs only a KEY.
+
+ delete_key
+ delete a referential key from all languages. This operation needs
+ only a KEY.
+
+HELP;
+ $file = str_replace(__DIR__ . '/', '', __FILE__);
+ echo sprintf($help, $file, $file);
+ exit;
+}
diff --git a/cli/reconfigure.php b/cli/reconfigure.php
index 466d35373..cfe713fa8 100755
--- a/cli/reconfigure.php
+++ b/cli/reconfigure.php
@@ -1,6 +1,6 @@
#!/usr/bin/php
<?php
-require('_cli.php');
+require(__DIR__ . '/_cli.php');
$params = array(
'environment:',
diff --git a/cli/update-user.php b/cli/update-user.php
index ac674484c..4529b8531 100755
--- a/cli/update-user.php
+++ b/cli/update-user.php
@@ -1,7 +1,7 @@
#!/usr/bin/php
<?php
$isUpdate = true;
-require('_update-or-create-user.php');
+require(__DIR__ . '/_update-or-create-user.php');
$username = cliInitUser($options['user']);
diff --git a/cli/user-info.php b/cli/user-info.php
index aa3e239b8..18a415217 100755
--- a/cli/user-info.php
+++ b/cli/user-info.php
@@ -1,6 +1,6 @@
#!/usr/bin/php
<?php
-require('_cli.php');
+require(__DIR__ . '/_cli.php');
$options = getopt('h', array(
'user:',
@@ -19,6 +19,7 @@ foreach ($users as $username) {
$catDAO = new FreshRSS_CategoryDAO();
$feedDAO = FreshRSS_Factory::createFeedDao($username);
$entryDAO = FreshRSS_Factory::createEntryDao($username);
+ $databaseDAO = FreshRSS_Factory::createDatabaseDAO($username);
$nbEntries = $entryDAO->countUnreadRead();
$nbFavorites = $entryDAO->countUnreadReadFavorites();
@@ -27,7 +28,7 @@ foreach ($users as $username) {
echo
$username, "\t",
date('c', FreshRSS_UserDAO::mtime($username)), "\t",
- format_bytes($entryDAO->size()), "\t",
+ format_bytes($databaseDAO->size()), "\t",
$catDAO->count(), " categories\t",
count($feedDAO->listFeedsIds()), " feeds\t",
$nbEntries['read'], " reads\t",
@@ -38,7 +39,7 @@ foreach ($users as $username) {
echo
$username, "\t",
FreshRSS_UserDAO::mtime($username), "\t",
- $entryDAO->size(), "\t",
+ $databaseDAO->size(), "\t",
$catDAO->count(), "\t",
count($feedDAO->listFeedsIds()), "\t",
$nbEntries['read'], "\t",
diff --git a/config.default.php b/config.default.php
index 748df1884..4e4c97e67 100644
--- a/config.default.php
+++ b/config.default.php
@@ -49,7 +49,7 @@ return array(
'auth_type' => 'form',
# Allow or not the use of the API, used for mobile apps.
- # End-point is http://example.net/FreshRSS/p/api/greader.php
+ # End-point is https://freshrss.example.net/api/greader.php
# You need to set the user's API password.
'api_enabled' => false,
diff --git a/constants.php b/constants.php
index be70188e0..2c791c3c5 100644
--- a/constants.php
+++ b/constants.php
@@ -1,27 +1,50 @@
<?php
-define('FRESHRSS_VERSION', '1.8.0');
+//NB: Do not edit; use ./constants.local.php instead.
+
+//<Not customisable>
+define('FRESHRSS_VERSION', '1.8.1-dev');
define('FRESHRSS_WEBSITE', 'https://freshrss.org');
define('FRESHRSS_WIKI', 'https://freshrss.github.io/FreshRSS/');
-// PHP text output compression http://php.net/ob_gzhandler (better to do it at Web server level)
-define('PHP_COMPRESSION', false);
+define('FRESHRSS_PATH', __DIR__);
+define('PUBLIC_PATH', FRESHRSS_PATH . '/p');
+define('PUBLIC_TO_INDEX_PATH', '/i');
+define('INDEX_PATH', PUBLIC_PATH . PUBLIC_TO_INDEX_PATH);
+define('PUBLIC_RELATIVE', '..');
+define('LIB_PATH', FRESHRSS_PATH . '/lib');
+define('APP_PATH', FRESHRSS_PATH . '/app');
+define('EXTENSIONS_PATH', FRESHRSS_PATH . '/extensions');
+//</Not customisable>
+
+function safe_define($name, $value) {
+ if (!defined($name)) {
+ return define($name, $value);
+ }
+}
-// Constantes de chemins
-define('FRESHRSS_PATH', dirname(__FILE__));
+if (file_exists(__DIR__ . '/constants.local.php')) {
+ //Include custom / local settings:
+ include(__DIR__ . '/constants.local.php');
+}
+
+safe_define('FRESHRSS_USERAGENT', 'FreshRSS/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ')');
+
+// PHP text output compression http://php.net/ob_gzhandler (better to do it at Web server level)
+safe_define('PHP_COMPRESSION', false);
- define('PUBLIC_PATH', FRESHRSS_PATH . '/p');
- define('PUBLIC_TO_INDEX_PATH', '/i');
- define('INDEX_PATH', PUBLIC_PATH . PUBLIC_TO_INDEX_PATH);
- define('PUBLIC_RELATIVE', '..');
+// Maximum log file size in Bytes, before it will be divided by two
+safe_define('MAX_LOG_SIZE', 1048576);
- define('DATA_PATH', FRESHRSS_PATH . '/data');
- define('UPDATE_FILENAME', DATA_PATH . '/update.php');
- define('USERS_PATH', DATA_PATH . '/users');
- define('CACHE_PATH', DATA_PATH . '/cache');
- define('PSHB_PATH', DATA_PATH . '/PubSubHubbub');
+//This directory must be writable
+safe_define('DATA_PATH', FRESHRSS_PATH . '/data');
- define('LIB_PATH', FRESHRSS_PATH . '/lib');
- define('APP_PATH', FRESHRSS_PATH . '/app');
- define('EXTENSIONS_PATH', FRESHRSS_PATH . '/extensions');
+safe_define('UPDATE_FILENAME', DATA_PATH . '/update.php');
+safe_define('USERS_PATH', DATA_PATH . '/users');
+safe_define('ADMIN_LOG', USERS_PATH . '/_/log.txt');
+safe_define('API_LOG', USERS_PATH . '/_/log_api.txt');
+safe_define('CACHE_PATH', DATA_PATH . '/cache');
+safe_define('PSHB_LOG', USERS_PATH . '/_/log_pshb.txt');
+safe_define('PSHB_PATH', DATA_PATH . '/PubSubHubbub');
-define('TMP_PATH', sys_get_temp_dir());
+//Directory used for feed mutex with *.freshrss.lock files. Must be writable.
+safe_define('TMP_PATH', sys_get_temp_dir());
diff --git a/data/shares.php b/data/shares.php
index d73ae3826..5403fd48c 100644
--- a/data/shares.php
+++ b/data/shares.php
@@ -14,6 +14,10 @@
* The ~TITLE~ placeholder represents the title of the shared article.
* - transform is an array of transformation to apply on links and titles
* - help is a URL to a help page
+ * - form is the type of form to display during configuration. It's either
+ * 'simple' or 'advanced'. 'simple' is used when only the name is configurable,
+ * 'advanced' is used when the name and the location are configurable.
+ * - method is the HTTP method (POST or GET) used to share a link.
*/
return array(
@@ -22,12 +26,14 @@ return array(
'transform' => array('rawurlencode'),
'help' => 'http://sebsauvage.net/wiki/doku.php?id=php:shaarli',
'form' => 'advanced',
+ 'method' => 'GET',
),
'blogotext' => array(
'url' => '~URL~/admin/links.php?url=~LINK~',
'transform' => array(),
'help' => 'http://lehollandaisvolant.net/blogotext/fr/',
'form' => 'advanced',
+ 'method' => 'GET',
),
'wallabag' => array(
'url' => '~URL~?action=add&amp;url=~LINK~',
@@ -37,6 +43,7 @@ return array(
),
'help' => 'http://www.wallabag.org/',
'form' => 'advanced',
+ 'method' => 'GET',
),
'wallabagv2' => array(
'url' => '~URL~/bookmarklet?url=~LINK~',
@@ -46,59 +53,77 @@ return array(
),
'help' => 'http://www.wallabag.org/',
'form' => 'advanced',
+ 'method' => 'GET',
),
'diaspora' => array(
'url' => '~URL~/bookmarklet?url=~LINK~&amp;title=~TITLE~',
'transform' => array('rawurlencode'),
'help' => 'https://diasporafoundation.org/',
'form' => 'advanced',
+ 'method' => 'GET',
),
'movim' => array(
'url' => '~URL~/?share/~LINK~',
'transform' => array('rawurlencode', 'urlencode'),
'help' => 'https://github.com/edhelas/movim',
'form' => 'advanced',
+ 'method' => 'GET',
),
'twitter' => array(
'url' => 'https://twitter.com/share?url=~LINK~&amp;text=~TITLE~',
'transform' => array('rawurlencode'),
'form' => 'simple',
+ 'method' => 'GET',
),
'g+' => array(
'url' => 'https://plus.google.com/share?url=~LINK~',
'transform' => array('rawurlencode'),
'form' => 'simple',
+ 'method' => 'GET',
),
'facebook' => array(
'url' => 'https://www.facebook.com/sharer.php?u=~LINK~&amp;t=~TITLE~',
'transform' => array('rawurlencode'),
'form' => 'simple',
+ 'method' => 'GET',
),
'email' => array(
'url' => 'mailto:?subject=~TITLE~&amp;body=~LINK~',
'transform' => array('rawurlencode'),
'form' => 'simple',
+ 'method' => 'GET',
),
'print' => array(
'url' => '#',
'transform' => array(),
'form' => 'simple',
+ 'method' => 'GET',
),
'jdh' => array(
'url' => 'https://www.journalduhacker.net/stories/new?url=~LINK~&title=~TITLE~',
'transform' => array('rawurlencode'),
'form' => 'simple',
+ 'method' => 'GET',
),
'Known' => array(
'url' => '~URL~/share?share_url=~LINK~&share_title=~TITLE~',
'transform' => array('rawurlencode'),
'help' => 'https://withknown.com/',
'form' => 'advanced',
+ 'method' => 'GET',
),
'gnusocial' => array(
'url' => '~URL~/notice/new?content=~TITLE~%20~LINK~',
'transform' => array('urlencode'),
'help' => 'https://gnu.io/social/',
'form' => 'advanced',
+ 'method' => 'GET',
+ ),
+ 'mastodon' => array(
+ 'url' => '~URL~/api/v1/statuses',
+ 'transform' => array(),
+ 'form' => 'advanced',
+ 'method' => 'POST',
+ 'field' => 'status',
),
);
diff --git a/docs/en/admins/01_Index.md b/docs/en/admins/01_Index.md
new file mode 100644
index 000000000..446780060
--- /dev/null
+++ b/docs/en/admins/01_Index.md
@@ -0,0 +1,8 @@
+# FreshRSS administration
+
+Learn how to install, update and backup FreshRSS and how to use the command line tools.
+
+* [Install FreshRSS](02_Installation.md) on your server
+* [Update your installation](03_Updating.md) to the latest stable or dev version
+* [The command line interface](https://github.com/FreshRSS/FreshRSS/tree/master/cli) can be used to administrate feeds and users
+* [Automatic feed updates](https://github.com/FreshRSS/FreshRSS#automatic-feed-update) using cron is the preferred way to get the latest feeds entries
diff --git a/docs/en/users/01_Installation.md b/docs/en/admins/02_Installation.md
index b2a717629..ef6531bd0 100644
--- a/docs/en/users/01_Installation.md
+++ b/docs/en/admins/02_Installation.md
@@ -1,6 +1,6 @@
# Server requirements
-FreshRSS is a web application. This means you'll need a web server to run it. FreshRSS requirements are really low, so it could run on most shared host servers.
+FreshRSS is a web application. This means you’ll need a web server to run it. FreshRSS requirements are really low, so it could run on most shared host servers.
You need to verify that your server can run FreshRSS before installing it. If your server has the proper requirements and FreshRSS does not work, please contact us to find a solution.
@@ -10,7 +10,7 @@ You need to verify that your server can run FreshRSS before installing it. If yo
| PHP | **PHP 5.5+** | PHP 5.3.8+ |
| PHP modules | Required: libxml, cURL, PDO_MySQL, PCRE and ctype. \\ Required (32-bit only): GMP \\Recommanded: JSON, Zlib, mbstring, iconv, ZipArchive | |
| Database | **MySQL 5.0.3+** | SQLite 3.7.4+ |
-| Browser | **Firefox** | Chrome, Opera, Safari or IE9+ |
+| Browser | **Firefox** | Chrome, Opera, Safari, or IE11+ |
## Important notice
@@ -35,86 +35,113 @@ As its name suggests, it is the working release for developers. **This release i
# Apache installation
-This is an example Apache virtual hosts configuration file. It covers http and https configuration.
+This is an example Apache virtual hosts configuration file. It covers HTTP and HTTPS configuration.
```
<VirtualHost *:80>
- ServerName example.com
- DocumentRoot /path/to/FreshRSS
+ DocumentRoot /var/www/html/
- ErrorLog ${APACHE_LOG_DIR}/freshrss_error.log
- CustomLog ${APACHE_LOG_DIR}/freshrss_access.log combined
+ #Default site...
- RewriteEngine on
- RewriteCond %{SERVER_NAME} = example.com
- RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]
+ ErrorLog ${APACHE_LOG_DIR}/error.default.log
+ CustomLog ${APACHE_LOG_DIR}/access.default.log vhost_combined
</VirtualHost>
-<IfModule mod_ssl.c>
-<VirtualHost *:443>
- ServerName example.com
- DocumentRoot /path/to/FreshRSS
+<VirtualHost *:80>
+ ServerName rss.example.net
+ DocumentRoot /path/to/FreshRSS/p/
- ErrorLog ${APACHE_LOG_DIR}/freshrss_error.log
- CustomLog ${APACHE_LOG_DIR}/freshrss_access.log combined
+ <Directory /path/to/FreshRSS/p>
+ AllowOverride AuthConfig FileInfo Indexes Limit
+ Require all granted
+ </Directory>
- SSLCertificateFile /path/to/server.crt
- SSLCertificateKeyFile /path/to/server.key
+ ErrorLog ${APACHE_LOG_DIR}/freshrss_error.log
+ CustomLog ${APACHE_LOG_DIR}/freshrss_access.log combined
- # Optional letsencrypt config (uncomment) line below
- #Include /etc/letsencrypt/options-ssl-apache.conf
+ AllowEncodedSlashes On
</VirtualHost>
+
+<IfModule mod_ssl.c>
+ <VirtualHost *:443>
+ ServerName rss.example.net
+ DocumentRoot /path/to/FreshRSS/p/
+
+ <Directory /path/to/FreshRSS/p>
+ AllowOverride AuthConfig FileInfo Indexes Limit
+ Require all granted
+ </Directory>
+
+ ErrorLog ${APACHE_LOG_DIR}/freshrss_error.log
+ CustomLog ${APACHE_LOG_DIR}/freshrss_access.log combined
+
+ <IfModule mod_http2.c>
+ Protocols h2 http/1.1
+ </IfModule>
+
+ # For the API
+ AllowEncodedSlashes On
+
+ SSLEngine on
+ SSLCompression off
+ SSLCertificateFile /path/to/server.crt
+ SSLCertificateKeyFile /path/to/server.key
+ # Additional SSL configuration, e.g. with LetsEncrypt
+ </VirtualHost>
</IfModule>
```
# Nginx installation
-This is an example nginx configuration file. It covers http, https and php-fpm configuration.
+This is an example nginx configuration file. It covers HTTP, HTTP, and php-fpm configuration.
_You can find simpler config file but they may be incompatible with FreshRSS API._
```
server {
- listen 80; # http on port 80
- listen 443 ssl; # https on port 443
-
- # https configuration
- ssl on;
- ssl_certificate /etc/nginx/server.crt;
- ssl_certificate_key /etc/nginx/server.key;
-
- # your server's url(s)
- server_name example.com rss.example.com;
-
- # the folder p of your FreshRSS installation
- root /srv/FreshRSS/p/;
-
- index index.php index.html index.htm;
-
- # nginx log files
- access_log /var/log/nginx/rss.access.log;
- error_log /var/log/nginx/rss.error.log;
-
- # php files handling
- # this regex is mandatory because of the API
- location ~ ^.+?\.php(/.*)?$ {
- fastcgi_pass unix:/var/run/php5-fpm.sock;
- fastcgi_split_path_info ^(.+\.php)(/.*)$;
- # By default, the variable PATH_INFO is not set under PHP-FPM
- # But FreshRSS API greader.php need it. If you have a "Bad Request" error, double check this var !
- fastcgi_param PATH_INFO $fastcgi_path_info;
- include fastcgi_params;
- fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
- }
-
- location / {
- try_files $uri $uri/ index.php;
- }
+ listen 80;
+ listen 443 ssl;
+
+ # HTTPS configuration
+ ssl on;
+ ssl_certificate /etc/nginx/server.crt;
+ ssl_certificate_key /etc/nginx/server.key;
+
+ # your server’s URL(s)
+ server_name rss.example.net;
+
+ # the folder p of your FreshRSS installation
+ root /srv/FreshRSS/p/;
+
+ index index.php index.html index.htm;
+
+ # nginx log files
+ access_log /var/log/nginx/rss.access.log;
+ error_log /var/log/nginx/rss.error.log;
+
+ # php files handling
+ # this regex is mandatory because of the API
+ location ~ ^.+?\.php(/.*)?$ {
+ fastcgi_pass unix:/var/run/php5-fpm.sock;
+ fastcgi_split_path_info ^(.+\.php)(/.*)$;
+ # By default, the variable PATH_INFO is not set under PHP-FPM
+ # But FreshRSS API greader.php need it. If you have a “Bad Request” error, double check this var!
+ fastcgi_param PATH_INFO $fastcgi_path_info;
+ include fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ }
+
+ location / {
+ try_files $uri $uri/ index.php;
+ }
}
```
-A step-by-step tutorial is available [in french](http://www.pihomeserver.fr/2013/05/08/raspberry-pi-home-server-installer-un-agregateur-de-flux-rss-pour-remplacer-google-reader/).
+A step-by-step tutorial is available [in French](http://www.pihomeserver.fr/2013/05/08/raspberry-pi-home-server-installer-un-agregateur-de-flux-rss-pour-remplacer-google-reader/).
# Security
+Make sure to expose only the `./p/` folder on the web, the other directories contain personal and sensitive data.
+See the Apache and nginx config examples above.
+
**TODO**
diff --git a/docs/en/admins/03_Updating.md b/docs/en/admins/03_Updating.md
new file mode 100644
index 000000000..4e1fdfa5d
--- /dev/null
+++ b/docs/en/admins/03_Updating.md
@@ -0,0 +1,90 @@
+
+First things first: we recommend to create a backup before updating:
+
+```sh
+# Perform all commands below in your FreshRSS directory:
+cd /usr/share/FreshRSS
+
+tar -czvf FreshRSS-backup.tgz .
+```
+
+The update process depends on your installation type, see below:
+
+
+## Using the web admin panel
+
+Change to your installation at http://localhost/FreshRSS/p/i/?c=update and hit the "Check for new updates" button.
+
+If there is a new version you will be prompted again.
+
+
+## Using git
+
+If you manage FreshRSS via command line, then installing and updating FreshRSS can be done via git:
+
+```sh
+# If your local user does not have write access, prefix all commands by sudo:
+sudo ...
+
+# Perform all commands below in your FreshRSS directory:
+cd /usr/share/FreshRSS
+
+# Use the development version of FreshRSS
+git checkout -b dev origin/dev
+
+# Check out a specific version of FreshRSS
+# See release names on https://github.com/FreshRSS/FreshRSS/releases
+# You will then need to manually change version
+# or checkout master or dev branch to get new versions
+git checkout 1.7.0
+
+# Verify what branch is used
+git branch
+
+# Check whether there is a new version of FreshRSS,
+# assuming you are on the /master or /dev branch
+git fetch --all
+git status
+
+# Discard manual changes (do a backup before)
+git reset --hard
+# Then re-delete the file forcing the setup wizard
+rm data/do-install.txt
+
+# Delete manual additions (do a backup before)
+git clean -f -d
+
+# Update to a newer version of FreshRSS,
+# assuming you are on the /master or /dev branch
+git pull
+
+# Set the rights so that your Web server can access the files
+# (Example for Debian / Ubuntu)
+chown -R :www-data . && chmod -R g+r . && chmod -R g+w ./data/
+```
+
+
+## Using the zip archive
+
+Perform all commands in your FreshRSS directory:
+```sh
+cd /usr/share/FreshRSS
+```
+
+Commands intended to be executed in order (you can c/p the whole block if desired):
+
+```sh
+wget https://github.com/FreshRSS/FreshRSS/archive/master.zip
+unzip master.zip
+cp -R FreshRSS-master/* .
+chown -R :www-data . && chmod -R g+r . && chmod -R g+w ./data/
+rm -f master.zip
+rm -f data/do-install.txt
+rm -rf FreshRSS-master/
+```
+
+Short explanation of the commands above:
+* Download the latest version and unzip it
+* Overwrite all your existing files with the new ones
+* Fix possible permission issues
+* Cleanup by deleting the downloaded zip, the file forcing the setup wizard and the temporary directory
diff --git a/docs/en/contributing.md b/docs/en/contributing.md
index 7f0c3da6c..19f9cb9b1 100644
--- a/docs/en/contributing.md
+++ b/docs/en/contributing.md
@@ -32,9 +32,9 @@ Did you want to fix a bug? To keep a great coordination between collaborators, y
3. [Create a new branch](https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/). The name of the branch must be explicit and being prefixed by the related ticket id. For instance, `783-contributing-file` to fix [ticket #783](https://github.com/FreshRSS/FreshRSS/issues/783).
4. Make your changes to your fork and [send a pull request](https://help.github.com/articles/using-pull-requests/) on the **dev branch**.
-If you have to write code, please follow [our coding style recommendations](http://doc2.freshrss.org/en/Developer_documentation/First_steps/Coding_style).
+If you have to write code, please follow [our coding style recommendations](developers/01_First_steps.md).
-**Tip:** if you are searching for bugs easy to fix, have a look at the « [New comers](https://github.com/FreshRSS/FreshRSS/labels/New%20comers) » ticket label.
+**Tip:** if you are searching for easy-to-fix bugs, have a look at the « [good first issue](https://github.com/FreshRSS/FreshRSS/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) » ticket label.
## Submit an idea
@@ -52,4 +52,5 @@ We are working on a better way to handle internationalization but don't hesitate
## Contribute to documentation
-The documentation needs a lot of improvements in order to be more useful to new contributors and we are working on it. If you want to give some help, meet us on [the dedicated repository](https://github.com/FreshRSS/documentation)!
+The documentation needs a lot of improvements in order to be more useful to new contributors and we are working on it.
+If you want to give some help, meet us in the main repositories [docs directory](https://github.com/FreshRSS/FreshRSS/tree/master/docs)!
diff --git a/docs/en/developers/01_First_steps.md b/docs/en/developers/01_First_steps.md
index e35dbda12..adca4495b 100644
--- a/docs/en/developers/01_First_steps.md
+++ b/docs/en/developers/01_First_steps.md
@@ -6,6 +6,10 @@
**TODO**
+# Extensions
+
+If you want to create your own FreshRSS extension, take a look at the [extension documentation](03_Backend/05_Extensions.md).
+
# Coding style
If you want to contribute to the source code, it is important to follow the project coding style. The actual code does not follow it throughout the project, but every time we have an opportunity, we should fix it.
diff --git a/docs/en/developers/03_Backend/05_Extensions.md b/docs/en/developers/03_Backend/05_Extensions.md
index b0b5793df..fb85234c4 100644
--- a/docs/en/developers/03_Backend/05_Extensions.md
+++ b/docs/en/developers/03_Backend/05_Extensions.md
@@ -6,21 +6,41 @@ FreshRSS is an RSS / Atom feeds aggregator written in PHP since October 2012. Th
## Problem to solve
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+FreshRSS is limited in its technical possibilities by various factors:
+
+* The number of developers
+* The will to integrate certain changes
+* The level of "hacking" required to integrate marginal features
+
+While the first limitation can, in theory, be lifted by the participation of new contributors to the project, it depends on the willingness of contributors to take an interest in the source code of the entire project. In order to remove the other two limitations, most of the time it will be necessary to create a "fork".
+
+Another solution consists of an extension system. By allowing users to write their own extension without taking an interest in the core of the basic software, we allow for:
+
+1. Reducing the amount of source code a new contributor has to take in
+2. Unofficial integration of novelties
+3. No necessity of forking or main developer approvement.
+
+Note: it is quite conceivable that the functionalities of an extension can later be officially integrated into the FreshRSS code. Extensions make it easy to propose a proof of concept.
## Understanding basic mechanics (Minz and MVC)
**TODO** : move to 02_Minz.md
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+This data sheet should refer to the official FreshRSS and Minz documentation (the PHP framework on which FreshRSS is based). Unfortunately, this documentation does not yet exist. In a few words, here are the main things you should know. It is not necessary to read all the chapters in this section if you don't need to use a feature in your extension (if you don't need to translate your extension, no need to know more about the `Minz_Translate` module for example).
### MVC Architecture
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+Minz relies on and imposes an MVC architecture for projects using it. This architecture consists of three main components:
+
+* The model: this is the base object that we will manipulate. In FreshRSS, categories, flows and articles are templates. The part of the code that makes it possible to manipulate them in a database is also part of the model but is separated from the base model: we speak of DAO (for "Data Access Object"). The templates are stored in a `Models` folder.
+* The view: this is what the user sees. The view is therefore simply HTML code mixed with PHP to display dynamic information. The views are stored in an `views` folder.
+* The controller: this is what makes it possible to link models and views. Typically, a controller will load templates from the database (like a list of items) to "pass" them to a view for display. Controllers are stored in a `Controllers` directory.
### Routing
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+In order to link a URL to a controller, first you have to go through a "routing" phase. In FreshRSS, this is particularly simple because it suffices to specify the name of the controller to load into the URL using a `c` parameter. For example, the address http://exemple.com?c=hello will execute the code contained in the `hello` controller.
+
+One concept that has not yet been discussed is the "actions" system. An action is executed *on* a controller. Concretely, a controller is represented by a class and its actions by methods. To execute an action, it is necessary to specify an `a` parameter in the URL.
Code example:
@@ -40,13 +60,17 @@ class FreshRSS_hello_Controller extends Minz_ActionController {
?>
```
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+When loading the address http://exemple.com?c=hello&a=world, the `world` action is executed on the `hello` controller.
+
+Note: if `c` or `a` is not specified, the default value for each of these variables is `index`. So the address http://exemple.com?c=hello will execute the `index` action of the `hello` controller.
+
+Later, the `hello/world` convention will be used to refer to a controller/action pair.
### Views
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+Each view is associated with a controller and an action. The view associated with `hello/world` will be stored in a very specific file: `views/hello/world. phtml`. This convention is imposed by Minz.
-Code example:
+As explained above, the views consist of HTML mixed with PHP. Code example:
```html
<p>
@@ -54,12 +78,11 @@ Code example:
</p>
```
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+The variable `$this->a_variable` is passed by the controller (see previous example). The difference is that in the controller it is necessary to pass `$this->view`, while in the view `$this` suffices.
### Working with GET / POST
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
-
+It is often necessary to take advantage of parameters passed by GET or POST. In Minz, these parameters are accessible using the `Minz_Request` class.
Code example:
```php
@@ -82,15 +105,17 @@ echo Minz_Request::param('bar');
?>
```
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+The `Minz_Request::isPost()` method can be used to execute a piece of code only if it is a POST request.
+
+Note: it is preferable to use `Minz_Request` only in controllers. It is likely that you will encounter this method in FreshRSS views, or even in templates, but be aware that this is **not** good practice.
### Access session settings
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+The access to session parameters is strangely similar to the GET / POST parameters but passes through the `Minz_Session` class this time! There is no example here because you can repeat the previous example by changing all `Minz_Request` to `Minz_Session`.
### Working with URLs
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+To take full advantage of the Minz routing system, it is strongly discouraged to write hard URLs in your code. For example, the following view should be avoided:
```html
<p>
@@ -98,7 +123,9 @@ echo Minz_Request::param('bar');
</p>
```
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+Should it be decided one day to use a "url rewriting" system to have addresses in a http://exemple.com/controller/action format, all previous addresses would become ineffective!
+
+So use the `Minz_Url` class and its `display()` method instead. `Minz_Url::display()` takes an array of the following form as its argument:
```php
<?php
@@ -117,7 +144,7 @@ echo Minz_Url::display($url_array);
?>
```
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+Since this can become a bit tedious to use in the long run, especially in views, it is preferable to use the `_url()' shortcut:
```php
<?php
@@ -128,11 +155,11 @@ echo _url('hello', 'world', 'foo', 'bar');
?>
```
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+Note: as a general rule, the shortened form (`_url()`) should be used in views, while the long form (`Minz_Url::display()`) should be used in controllers.
### Redirections
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+It is often necessary to redirect a user to another page. To do so, the `Minz_Request` class offers another useful method: `forward()`. This method takes the same URL format as the one seen just before as its argument.
Code example:
@@ -156,7 +183,7 @@ Minz_Request::forward($url_array, true);
?>
```
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+It is very common to want display a message to the user while performing a redirect, to tell the user how the action was carried out (validation of a form for example). Such a message is passed through a `notification` session variable (note: we will talk about feedback from now on to avoid confusion with a notification that can occur at any time). To facilitate this kind of very frequent action, there are two shortcuts that both perform a 302 redirect by assigning a feedback message:
```php
<?php
@@ -179,7 +206,21 @@ Minz_Request::bad($feedback_bad, $url_array);
### Translation Management
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+It is common (and that's an understatement) to want to show some text to the user. In the previous example, for example, we display feedback to the user based on the result of form validation. The problem is that FreshRSS has users of different nationalities. It is therefore necessary to be able to manage different languages in order not to remain confined to English or French.
+
+The solution is to use the `Minz_Translate` class, which allows dynamic translation of FreshRSS (or any Minz-based application). Before using this module, it is necessary to know where to find the strings to be translated. Each language has its own subdirectory in a parent directory named `i18n`. For example, English language files are located in `i18n/fr/`. There are seven different files:
+
+- `admin.php` for anything related to FreshRSS administration
+- `conf.php` for configuration
+- `feedback.php` contains translations of feedback messages
+- `gen.php` stores what is global to FreshRSS (gen for "general")
+- `index.php` for the main page that lists feeds and the About page
+- `install.php` contains strings related FreshRSS installation
+- `sub.php` for subscription management (sub for :subscription")
+
+This organization makes it possible to avoid a single huge translation file.
+
+The translation files are quite simple: it is only a matter of returning a PHP table containing the translations. Extract from `app/i18n/en/gen.php`:
```php
<?php
@@ -201,8 +242,7 @@ return array(
?>
```
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
-
+To access these translations, `Minz_Translate` will help us with its `Minz_Translate::t()` method. As this can be a bit long to type, a shortcut has been introduced that **must** be used in all circumstances: `_t()`.
Code example:
```html
@@ -213,11 +253,11 @@ Code example:
</p>
```
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+The string to pass to the `_t()` function consists of a series of identifiers separated by dots. The first identifier indicates from which file to extract the translation (in this case, `gen.php`), while the following ones indicate table entries. Thus `action` is an entry of the main array and `back_to_rss_feeds` is an entry of the `action` array. This allows us to further organize our translation files.
-### Configuration management
+There is a small special case that sometimes makes life easier: the `_` identifier. This must necessarily be present at the end of the chain and gives a value to the higher-level identifier. It's pretty hard to explain but very simple to understand. In the example given above, a `_` is associated with the value `FreshRSS`: this means that there is no need to write `_t('gen.freshrss._')` but `_t('gen.freshrss')` suffices.
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+### Configuration management
## Write an extension for FreshRSS
@@ -254,7 +294,7 @@ You may also need additional files or subdirectories depending on your needs:
- `configure.phtml` is the file containing the form to parameterize your extension
- A `static/` directory containing CSS and JavaScript files that you will need for your extension (note that if you need to write a lot of CSS it may be more interesting to write a complete theme)
-- A `controllers` directory containing additional controllers
+- A `Controllers` directory containing additional controllers
- An `i18n` directory containing additional translations
- `layout` and` views` directories to define new views or to overwrite the current views
@@ -298,7 +338,7 @@ In addition, you will have a number of methods directly inherited from `Minz_Ext
- `getFileUrl($filename, $type)` will return the URL to a file in the `static` directory. The first parameter is the name of the file (without `static /`), the second is the type of file to be used (`css` or` js`).
- `registerController($base_name)` will tell Minz to take into account the given controller in the routing system. The controller must be located in your `Controllers` directory, the name of the file must be` <base_name>Controller.php` and the name of the `FreshExtension_<base_name>_Controller` class.
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+**TODO**
- `registerViews()`
- `registerTranslates()`
@@ -331,4 +371,4 @@ The following events are available:
When you want to support user configurations for your extension or simply display some information, you have to create the `configure.phtml` file.
-**TODO** translate from [french version](https://github.com/FreshRSS/documentation/blob/master/fr/docs/developers/03_Backend/05_Extensions.md)
+**TODO**
diff --git a/docs/en/index.md b/docs/en/index.md
index c2f12380b..a0c97a0d9 100644
--- a/docs/en/index.md
+++ b/docs/en/index.md
@@ -16,7 +16,9 @@ FreshRSS has a lot of features including:
- Multi-users so you can host your friends and your family
- And a lot more!
-This documentation is splitted in two sections:
+This documentation is split into four sections:
-- [users documentation](users/02_First_steps.md) so you can discover all the power of FreshRSS
-- [developers documentation](developers/01_First_steps.md) to guide you in the source code of FreshRSS and to help you if you want to contribute
+- [user documentation](users/02_First_steps.md) so you can discover all the power of FreshRSS
+- [developer documentation](developers/01_First_steps.md) to guide you in the source code of FreshRSS and to help you if you want to contribute
+- [administrator documentation](admins/01_Index.md) to guide you in the source code of FreshRSS and to help you if you want to contribute
+- [contributor guidelines](contributing.md) for all of you who want to help improving FreshRSS
diff --git a/docs/en/users/02_First_steps.md b/docs/en/users/02_First_steps.md
index 96ee264a6..2fb1b7256 100644
--- a/docs/en/users/02_First_steps.md
+++ b/docs/en/users/02_First_steps.md
@@ -2,7 +2,7 @@ Learning how to handle a new application is not always easy. We build FreshRSS t
This section guides you to the pages you need as a new comer.
-[After you installed the application](01_Installation.md), the first step is to add one or more feeds. You have few options:
+[After installing the application](../admins/02_Installation.md), the first step is to add some feeds. You have a few options:
1. [Add a feed manually](04_Subscriptions.md#adding-a-feed)
2. [Import an OPML or JSON file](04_Subscriptions.md#import-and-export)
@@ -24,3 +24,5 @@ Now that you know the basic usages, it is time to configure FreshRSS to improve
* [Filter articles](03_Main_view.md#filtering-articles) for a fast access to a selection
* [Search an article](03_Main_view.md#searching-articles) published some time ago
* [Access your feeds on a mobile device](06_Mobile_access.md)
+* [Add some extensions](https://github.com/FreshRSS/Extensions)
+* [Frequently asked questions](07_Frequently_Asked_Questions.md)
diff --git a/docs/en/users/05_Configuration.md b/docs/en/users/05_Configuration.md
index f8ac56cd7..d0951e905 100644
--- a/docs/en/users/05_Configuration.md
+++ b/docs/en/users/05_Configuration.md
@@ -20,7 +20,7 @@ In matters of taste and color, there can be no disputes. This is why FreshRSS of
If none of these are suitable for you, it is always possible to create your own.
-To select a theme, simply scroll through the themes and select a theme that appears. After confirmation, the theme will be applied to the interface.
+To select a theme, simply scroll through the themes and select one that strikes your fancy. After confirmation, the theme will be applied to the interface.
## Content width
@@ -69,10 +69,10 @@ The duration of this notification can be set. By default, the value is 0.
### HTTP Authentication (Apache)
- 1. User control is based on the .htaccess file
- 2. It is best practice to place the .htaccess file in ./i/ subdirecotry so API and other third party services can work.
- 3. If you want to limit all access to registered users only, place the file in the directory of FreshRSS or in a parent directory. Note that PubsubHubbub and API will not work!
- 4. Example .htaccess file for a user "marie":
+ 1. User control is based on the `.htaccess` file.
+ 2. It is best practice to place the `.htaccess` file in the `./i/` subdirectory so the API and other third party services can work.
+ 3. If you want to limit all access to registered users only, place the file in the FreshRSS directory itself or in a parent directory. Note that PubsubHubbub and API will not work!
+ 4. Example `.htaccess` file for a user "marie":
```
AuthUserFile /home/marie/repertoire/.htpasswd
@@ -82,7 +82,7 @@ AuthType Basic
Require user marie
```
-More information can be found in [Apache documentation](http://httpd.apache.org/docs/trunk/howto/auth.html#gettingitworking).
+More information can be found in the [Apache documentation](http://httpd.apache.org/docs/trunk/howto/auth.html#gettingitworking).
# Subscription management
@@ -104,7 +104,7 @@ More information can be found in [Apache documentation](http://httpd.apache.org/
The question comes up regularly, so we will try to clarify here how one can retrieve a truncated RSS feed with FreshRSS. Please note that the process is absolutely not "user friendly", but it works :)
-Also know that this way you are generating much more traffic to the originating sites and that they can block you accordingly. The performance of FreshRSS is also imapcted because you have to fetch the contents of the articles one by one. So it's a feature to use sparingly!
+Also know that this way you are generating much more traffic to the originating sites and that they might block you accordingly. The performance of FreshRSS is also negatively affected because you have to fetch the full article content one by one. So it's a feature to use sparingly!
What is meant by "CSS path of articles on the original site" actually corresponds to the "path" consisting of IDs and classes (which in html, matches the id and class attributes) to retrieve only the interesting part that corresponds to the article. Ideally, this path starts with an id (which is unique to the page).
@@ -118,4 +118,4 @@ We find here that the block that encompasses only the content of the article is
* Rue89: ```#article .content```
* PCINpact: ```#actu_content```
-* Lesnumériques: ```article#body div.text.clearfix``` \ No newline at end of file
+* Lesnumériques: ```article#body div.text.clearfix```
diff --git a/docs/en/users/06_Mobile_access.md b/docs/en/users/06_Mobile_access.md
index e1a23c8ba..3472172b0 100644
--- a/docs/en/users/06_Mobile_access.md
+++ b/docs/en/users/06_Mobile_access.md
@@ -1 +1,51 @@
-**TODO**
+This page assumes you have completed the [server setup](../admins/02_Installation.md).
+
+# Enable the API in FreshRSS
+
+1. Under the section “Authentication”, enable the option “Allow API access (required for mobile apps)”.
+2. Under the section “Profile”, fill-in the field “API password (e.g., for mobile apps)”.
+ * Every user must define an API password.
+ * The reason for an API-specific password is that it may be used in less safe situations than the main password, and does not grant access to as many things.
+
+
+# Testing
+
+3. Under the section “Profile”, click on the link like `https://rss.example.net/api/` next to the field “API password”.
+4. Click on first link “Check full server configuration”:
+ * If you get *PASS* then you are done, all is good: you may proceed to step 6.
+ * If you get *Bad Request!* or *Not Found*, then your server probably does not accept slashes `/` that are escaped `%2F`. Proceed to step 5.
+ * If you get any other error message, proceed to step 5.
+
+
+# Fix server configuration
+
+5. Click on the second link “Check partial server configuration (without `%2F` support)”:
+ * If you get `PASS`, then the problem is indeed that your server does not accept slashes `/` that are escaped `%2F`.
+ * With Apache, remember the directive [`AllowEncodedSlashes On`](http://httpd.apache.org/docs/trunk/mod/core.html#allowencodedslashes)
+ * Or use a client that does not escape slashes (such as EasyRSS), in which case proceed to step 6.
+ * If you get *Service Unavailable!*, then check from step 1 again.
+ * With __Apache__:
+ * If you get *FAIL getallheaders!*, the combination of your PHP version and your Web server does not provide access to [`getallheaders`](http://php.net/getallheaders)
+ * Update to PHP 5.4+, or use PHP as module instead of CGI. Otherwise turn on Apache `mod_rewrite`:
+ * Allow [`FileInfo` in `.htaccess`](http://httpd.apache.org/docs/trunk/mod/core.html#allowoverride): see the [server setup](../admins/02_Installation.md) again.
+ * Enable [`mod_rewrite`](http://httpd.apache.org/docs/trunk/mod/mod_rewrite.html):
+ * With Debian / Ubuntu: `sudo a2enmod rewrite`
+ * With __nginx__:
+ * If you get *Bad Request!*, check your server `PATH_INFO` configuration.
+ * If you get *File not found!*, check your server `fastcgi_split_path_info`.
+ * If you get *FAIL 64-bit or GMP extension!*, then your PHP version does not pass the requirement of being 64-bit and/or have PHP [GMP](http://php.net/gmp) extension.
+ * The easiest is to add the GMP extension. On Debian / Ubuntu: `sudo apt install php-gmp`
+ * Update and try again from step 3.
+
+
+# Compatible clients
+
+6. On the same FreshRSS API page, note the adress given under “Your API address”, like `https://freshrss.example.net/api/greader.php`
+ * You will type it in a client, together with your FreshRSS username, and the corresponding special API password.
+
+7. Pick a client supporting a Google Reader-like API. Selection:
+ * Android
+ * [News+](https://play.google.com/store/apps/details?id=com.noinnion.android.newsplus) with [News+ Google Reader extension](https://play.google.com/store/apps/details?id=com.noinnion.android.newsplus.extension.google_reader) (Closed source)
+ * [EasyRSS](https://github.com/Alkarex/EasyRSS) (Open source, [F-Droid](https://f-droid.org/packages/org.freshrss.easyrss/))
+ * Linux
+ * [FeedReader 2.0+](https://jangernert.github.io/FeedReader/) (Open source)
diff --git a/docs/fr/developers/03_Backend/05_Extensions.md b/docs/fr/developers/03_Backend/05_Extensions.md
index a3dc5ad20..f844accd1 100644
--- a/docs/fr/developers/03_Backend/05_Extensions.md
+++ b/docs/fr/developers/03_Backend/05_Extensions.md
@@ -277,7 +277,7 @@ Il est possible aussi que vous ayez besoin de fichiers ou sous-répertoires addi
- `configure.phtml` est le fichier contenant le formulaire permettant de paramétrer votre extension ;
- Un répertoire `static/` contenant fichiers CSS et JavaScript dont vous aurez besoin pour votre extension. Notez que si vous devez écrire beaucoup de CSS il est peut-être plus intéressant d'écrire un thème complet (mais ce n'est pas le sujet de cette fiche technique) ;
-- Un répertoire `controllers` contenant des contrôleurs additionnels ;
+- Un répertoire `Controllers` contenant des contrôleurs additionnels ;
- Un répertoire `i18n` contenant des traductions supplémentaires ;
- Des répertoires `layout` et `views` permettant de définir de nouvelles vues ou d'écraser les vues actuelles.
@@ -332,4 +332,4 @@ TODO :
### Écrire le fichier configure.phtml
-TODO
+Lorsque vous voulez ajouter de la configuration à votre extension ou afficher ses informations, vous devez créer le fichier `configure.phtml`.
diff --git a/docs/fr/users/01_Installation.md b/docs/fr/users/01_Installation.md
index 765cb9481..dd1bacd00 100644
--- a/docs/fr/users/01_Installation.md
+++ b/docs/fr/users/01_Installation.md
@@ -1,6 +1,6 @@
# Les pré-requis sur le serveur
-FreshRSS est un logiciel développé en PHP reposant sur le modèle client - serveur. C'est-à-dire qu'il vous faudra un serveur web pour en profiter. Ensuite, FreshRSS ne demande pas une configuration très fournie et peut donc, en théorie, tourner sur la plupart des serveurs mutualisés.
+FreshRSS est un logiciel développé en PHP reposant sur le modèle client - serveur. C’est-à-dire qu’il vous faudra un serveur web pour en profiter. Ensuite, FreshRSS ne demande pas une configuration très fournie et peut donc, en théorie, tourner sur la plupart des serveurs mutualisés.
Il est toutefois de votre responsabilité de vérifier que votre hébergement permettra de faire tourner FreshRSS avant de nous taper dessus. Dans le cas où les informations listées ci-dessous ne seraient pas à jour, vous pourrez.
@@ -10,7 +10,7 @@ Il est toutefois de votre responsabilité de vérifier que votre hébergement pe
| PHP | **PHP 5.5+** | PHP 5.3.8+ |
| Modules PHP | Requis : libxml, cURL, PDO_MySQL, PCRE et ctype \\ Requis (32 bits seulement) : GMP \\ Recommandé : JSON, Zlib, mbstring et iconv, ZipArchive | |
| Base de données | **MySQL 5.0.3+** | SQLite 3.7.4+ |
- | Navigateur | **Firefox** | Chrome, Opera, Safari or IE 9+ |
+ | Navigateur | **Firefox** | Chrome, Opera, Safari, or IE 11+ |
## Note importante
@@ -24,63 +24,113 @@ FreshRSS possède trois versions différentes (nous parlons de branches) qui sor
[Téléchargement](https://github.com/FreshRSS/FreshRSS/archive/master.zip)
-Cette version sort lorsqu'on considère qu'on a répondu à nos objectifs en terme de nouvelles fonctionnalités. Deux versions peuvent ainsi sortir de façon très rapprochée si les développeurs travaillent bien. En pratique, comme nous nous fixons de nombreux objectifs et que nous travaillons sur notre temps libre, les versions sont souvent assez espacées (plusieurs mois). Son avantage est que le code est particulièrement stable et vous ne devriez pas faire face à de méchants bugs.
+Cette version sort lorsqu’on considère qu’on a répondu à nos objectifs en terme de nouvelles fonctionnalités. Deux versions peuvent ainsi sortir de façon très rapprochée si les développeurs travaillent bien. En pratique, comme nous nous fixons de nombreux objectifs et que nous travaillons sur notre temps libre, les versions sont souvent assez espacées (plusieurs mois). Son avantage est que le code est particulièrement stable et vous ne devriez pas faire face à de méchants bugs.
## La version de développement
[Téléchargement](https://github.com/FreshRSS/FreshRSS/archive/dev.zip)
-Comme son nom l'indique, il s'agit de la version sur laquelle les développeurs travaillent. **Elle est donc totalement instable !** Si vous souhaitez recevoir les améliorations au jour le jour, vous pouvez l'utiliser, mais attention à bien suivre les évolutions sur Github (via [le flux RSS de la branche](https://github.com/FreshRSS/FreshRSS/commits/dev.atom) par exemple). On raconte que les développeurs principaux l'utilisent quotidiennement sans avoir de soucis. Sans doute savent-ils ce qu'ils font…
+Comme son nom l’indique, il s’agit de la version sur laquelle les développeurs travaillent. **Elle est donc instable !** Si vous souhaitez recevoir les améliorations au jour le jour, vous pouvez l’utiliser, mais attention à bien suivre les évolutions sur Github (via [le flux RSS de la branche](https://github.com/FreshRSS/FreshRSS/commits/dev.atom) par exemple). On raconte que les développeurs principaux l’utilisent quotidiennement sans avoir de soucis. Sans doute savent-ils ce qu’ils font…
# Installation sur Apache
-**TODO**
+```
+<VirtualHost *:80>
+ DocumentRoot /var/www/html/
+
+ #Site par défaut...
+
+ ErrorLog ${APACHE_LOG_DIR}/error.default.log
+ CustomLog ${APACHE_LOG_DIR}/access.default.log vhost_combined
+</VirtualHost>
+
+<VirtualHost *:80>
+ ServerName rss.example.net
+ DocumentRoot /path/to/FreshRSS/p/
+
+ <Directory /path/to/FreshRSS/p>
+ AllowOverride AuthConfig FileInfo Indexes Limit
+ Require all granted
+ </Directory>
+
+ ErrorLog ${APACHE_LOG_DIR}/freshrss_error.log
+ CustomLog ${APACHE_LOG_DIR}/freshrss_access.log combined
-Cette partie n'a pas encore été écrite. Néanmoins, comme il s'agit d'une bête application PHP, cela ne pose généralement pas de soucis à installer :)
+ AllowEncodedSlashes On
+</VirtualHost>
+
+<IfModule mod_ssl.c>
+ <VirtualHost *:443>
+ ServerName rss.example.net
+ DocumentRoot /path/to/FreshRSS/p/
+
+ <Directory /path/to/FreshRSS/p>
+ AllowOverride AuthConfig FileInfo Indexes Limit
+ Require all granted
+ </Directory>
+
+ ErrorLog ${APACHE_LOG_DIR}/freshrss_error.log
+ CustomLog ${APACHE_LOG_DIR}/freshrss_access.log combined
+
+ <IfModule mod_http2.c>
+ Protocols h2 http/1.1
+ </IfModule>
+
+ # Pour l’API
+ AllowEncodedSlashes On
+
+ SSLEngine on
+ SSLCompression off
+ SSLCertificateFile /path/to/server.crt
+ SSLCertificateKeyFile /path/to/server.key
+ # Additional SSL configuration, e.g. with LetsEncrypt
+ </VirtualHost>
+</IfModule>
+```
# Installation sur Nginx
-Voici un fichier de configuration pour nginx. Il couvre la configuration pour http, https et php.
+Voici un fichier de configuration pour nginx. Il couvre la configuration pour HTTP, HTTPS, et PHP.
-_Vous pourrez trouver d'autres fichiers de configuration plus simples mais ces derniers ne seront peut-être pas compatibles avec l'API FreshRSS._
+_Vous pourrez trouver d’autres fichiers de configuration plus simples mais ces derniers ne seront peut-être pas compatibles avec l’API FreshRSS._
```
server {
- listen 80; # http sur le port 80
- listen 443 ssl; # https sur le port 443
-
- # configuration https
- ssl on;
- ssl_certificate /etc/nginx/server.crt;
- ssl_certificate_key /etc/nginx/server.key;
-
- # l'url ou les urls de votre serveur
- server_name example.com rss.example.com;
-
- # le répertoire où se trouve le dossier p de FreshRSS
- root /srv/FreshRSS/p/;
-
- index index.php index.html index.htm;
-
- # les fichiers de log nginx
- access_log /var/log/nginx/rss.access.log;
- error_log /var/log/nginx/rss.error.log;
-
- # gestion des fichiers php
- # il est nécessaire d'utiliser cette expression régulière pour le bon fonctionnement de l'API
- location ~ ^.+?\.php(/.*)?$ {
- fastcgi_pass unix:/var/run/php5-fpm.sock;
- fastcgi_split_path_info ^(.+\.php)(/.*)$;
- # Par défaut la variable PATH_INFO n'est pas définie sous PHP-FPM
- # or l'API FreshRSS greader.php en a besoin. Si vous avez un "Bad Request", vérifiez bien cette dernière !
- fastcgi_param PATH_INFO $fastcgi_path_info;
- include fastcgi_params;
- fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
- }
-
- location / {
- try_files $uri $uri/ index.php;
- }
+ listen 80;
+ listen 443 ssl;
+
+ # configuration https
+ ssl on;
+ ssl_certificate /etc/nginx/server.crt;
+ ssl_certificate_key /etc/nginx/server.key;
+
+ # l’URL ou les URLs de votre serveur
+ server_name rss.example.net;
+
+ # le répertoire où se trouve le dossier p de FreshRSS
+ root /srv/FreshRSS/p/;
+
+ index index.php index.html index.htm;
+
+ # les fichiers de log nginx
+ access_log /var/log/nginx/rss.access.log;
+ error_log /var/log/nginx/rss.error.log;
+
+ # gestion des fichiers php
+ # il est nécessaire d’utiliser cette expression régulière pour le bon fonctionnement de l’API
+ location ~ ^.+?\.php(/.*)?$ {
+ fastcgi_pass unix:/var/run/php5-fpm.sock;
+ fastcgi_split_path_info ^(.+\.php)(/.*)$;
+ # Par défaut la variable PATH_INFO n’est pas définie sous PHP-FPM
+ # or l’API FreshRSS greader.php en a besoin. Si vous avez un “Bad Request”, vérifiez bien cette dernière !
+ fastcgi_param PATH_INFO $fastcgi_path_info;
+ include fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ }
+
+ location / {
+ try_files $uri $uri/ index.php;
+ }
}
```
diff --git a/docs/fr/users/06_Mobile_access.md b/docs/fr/users/06_Mobile_access.md
index e1a23c8ba..185c94098 100644
--- a/docs/fr/users/06_Mobile_access.md
+++ b/docs/fr/users/06_Mobile_access.md
@@ -1 +1,49 @@
-**TODO**
+Cette page suppose que vous avez fini [l’installation du serveur](01_Installation.md).
+
+# Activer l’API dans FreshRSS
+
+1. Dans la section “Authentification”, cocher l’option “Autoriser l’accès par API (nécessaire pour les applis mobiles)”.
+2. Dans la section “Profil”, remplir le champ “Mot de passe API (ex. : pour applis mobiles)”.
+ * Chaque utilisateur doit choisir son mot de passe API.
+ * La raison d’être d’un mot de passe API différent du mot de passe principal est que le mot de passe API est potentiellement utilisé de manière moins sûre, mais il permet aussi moins de choses.
+
+
+# Tester
+
+3. Dans la section “Profil”, cliquer sur le lien de la forme `https://rss.example.net/api/` à côté du champ “Mot de passe API”.
+4. Cliquer sur le premier lien “Check full server configuration”:
+ * Si vous obtenez `PASS`, tout est bon : passer à l’étape 6.
+ * Si vous obtenez *Bad Request!* ou *Not Found*, alors votre serveur ne semble pas accepter les slashs `/` qui sont encodés `%2F`. Passer à l’étape 5.
+ * Si vous obtenez un autre message d’erreur, passer à l’étape 5.
+
+
+# Débogger la configuration du serveur
+
+5. Cliquer sur le second lien “Check partial server configuration (without `%2F` support)”:
+ * Si vous obtenez `PASS`, alors le problème est bien que votre serveur n’accepte pas les slashs `/` qui sont encodés `%2F`.
+ * Avec Apache, vérifiez la directive [`AllowEncodedSlashes On`](http://httpd.apache.org/docs/trunk/mod/core.html#allowencodedslashes)
+ * Ou utilisez un client qui n’encode pas les slashs (comme EasyRSS), auquel cas passer à l’étape 6.
+ * Si vous obtenez *Service Unavailable!*, retourner à l’étape 6.
+ * Avec __Apache__:
+ * Si vous obtenez *FAIL getallheaders!*, alors la combinaison de votre version de PHP et de votre serveur Web ne permet pas l’accès à [`getallheaders`](http://php.net/getallheaders)
+ * Utilisez au moins PHP 5.4+, ou utilisez PHP en tant que module plutôt que CGI. Sinon, activer Apache `mod_rewrite` :
+ * Autoriser [`FileInfo` dans `.htaccess`](http://httpd.apache.org/docs/trunk/mod/core.html#allowoverride) : revoir [l’installation du serveur](01_Installation.md).
+ * Activer [`mod_rewrite`](http://httpd.apache.org/docs/trunk/mod/mod_rewrite.html) :
+ * Sur Debian / Ubuntu : `sudo a2enmod rewrite`
+ * Avec __nginx__:
+ * Si vous obtenez *Bad Request!*, vérifier la configuration `PATH_INFO` de votre serveur.
+ * Si vous obtenez *File not found!*, vérifier la configuration `fastcgi_split_path_info` de votre serveur.
+ * Si vous obtenez *FAIL 64-bit or GMP extension!*, alors votre installation PHP soit n’est pas en 64 bit, soit n’a pas l’extension PHP [GMP](http://php.net/gmp) activée.
+ * Le plus simple est d’activer l’extension GMP. Sur Debian / Ubuntu : `sudo apt install php-gmp`
+ * Mettre à jour et retourner à l’étape 3.
+
+
+# Clients compatibles
+
+Tout client supportant une API de type Google Reader. Sélection :
+
+* Android
+ * [News+](https://play.google.com/store/apps/details?id=com.noinnion.android.newsplus) avec [News+ Google Reader extension](https://play.google.com/store/apps/details?id=com.noinnion.android.newsplus.extension.google_reader) (Propriétaire)
+ * [EasyRSS](https://github.com/Alkarex/EasyRSS) (Libre, F-Droid)
+* Linux
+ * [FeedReader 2.0+](https://jangernert.github.io/FeedReader/) (Libre)
diff --git a/lib/Minz/Dispatcher.php b/lib/Minz/Dispatcher.php
index bdb1c76f6..f05b285b5 100644
--- a/lib/Minz/Dispatcher.php
+++ b/lib/Minz/Dispatcher.php
@@ -10,7 +10,6 @@
* C'est un singleton
*/
class Minz_Dispatcher {
- const CONTROLLERS_PATH_NAME = '/Controllers';
/* singleton */
private static $instance = null;
@@ -149,7 +148,7 @@ class Minz_Dispatcher {
*/
private static function loadController($base_name) {
$base_path = self::$registrations[$base_name];
- $controller_filename = $base_path . '/controllers/' . $base_name . 'Controller.php';
+ $controller_filename = $base_path . '/Controllers/' . $base_name . 'Controller.php';
include_once $controller_filename;
}
diff --git a/lib/Minz/ExtensionManager.php b/lib/Minz/ExtensionManager.php
index c5c68a8d4..02a99701f 100644
--- a/lib/Minz/ExtensionManager.php
+++ b/lib/Minz/ExtensionManager.php
@@ -94,8 +94,8 @@ class Minz_ExtensionManager {
* If the extension class name is `TestExtension`, entry point will be `Test`.
* `entry_point` must be composed of alphanumeric characters.
*
- * @param $meta is an array of values.
- * @return true if the array is valid, false else.
+ * @param array $meta is an array of values.
+ * @return bool true if the array is valid, false else.
*/
public static function isValidMetadata($meta) {
$valid_chars = array('_');
@@ -107,8 +107,8 @@ class Minz_ExtensionManager {
/**
* Load the extension source code based on info metadata.
*
- * @param $info an array containing information about extension.
- * @return an extension inheriting from Minz_Extension.
+ * @param array $info an array containing information about extension.
+ * @return Minz_Extension|null an extension inheriting from Minz_Extension.
*/
public static function load($info) {
$entry_point_filename = $info['path'] . '/' . self::$ext_entry_point;
@@ -127,9 +127,9 @@ class Minz_ExtensionManager {
$extension = null;
try {
$extension = new $ext_class_name($info);
- } catch (Minz_ExtensionException $e) {
+ } catch (Exception $e) {
// We cannot load the extension? Invalid!
- Minz_Log::warning('In `' . $metadata_filename . '`: ' . $e->getMessage());
+ Minz_Log::warning('Invalid extension `' . $ext_class_name . '`: ' . $e->getMessage());
return null;
}
@@ -149,7 +149,7 @@ class Minz_ExtensionManager {
* If the extension is present in $ext_auto_enabled and if its type is "system",
* it will be enabled in the same time.
*
- * @param $ext a valid extension.
+ * @param Minz_Extension $ext a valid extension.
*/
public static function register($ext) {
$name = $ext->getName();
@@ -168,7 +168,7 @@ class Minz_ExtensionManager {
*
* The extension init() method will be called.
*
- * @param $ext_name is the name of a valid extension present in $ext_list.
+ * @param Minz_Extension $ext_name is the name of a valid extension present in $ext_list.
*/
public static function enable($ext_name) {
if (isset(self::$ext_list[$ext_name])) {
@@ -182,7 +182,7 @@ class Minz_ExtensionManager {
/**
* Enable a list of extensions.
*
- * @param $ext_list the names of extensions we want to load.
+ * @param string[] $ext_list the names of extensions we want to load.
*/
public static function enableByList($ext_list) {
foreach ($ext_list as $ext_name) {
@@ -193,8 +193,8 @@ class Minz_ExtensionManager {
/**
* Return a list of extensions.
*
- * @param $only_enabled if true returns only the enabled extensions (false by default).
- * @return an array of extensions.
+ * @param bool $only_enabled if true returns only the enabled extensions (false by default).
+ * @return Minz_Extension[] an array of extensions.
*/
public static function listExtensions($only_enabled = false) {
if ($only_enabled) {
@@ -207,8 +207,8 @@ class Minz_ExtensionManager {
/**
* Return an extension by its name.
*
- * @param $ext_name the name of the extension.
- * @return the corresponding extension or null if it doesn't exist.
+ * @param string $ext_name the name of the extension.
+ * @return Minz_Extension|null the corresponding extension or null if it doesn't exist.
*/
public static function findExtension($ext_name) {
if (!isset(self::$ext_list[$ext_name])) {
@@ -224,9 +224,9 @@ class Minz_ExtensionManager {
* The hook name must be a valid one. For the valid list, see self::$hook_list
* array keys.
*
- * @param $hook_name the hook name (must exist).
- * @param $hook_function the function name to call (must be callable).
- * @param $ext the extension which register the hook.
+ * @param string $hook_name the hook name (must exist).
+ * @param callable $hook_function the function name to call (must be callable).
+ * @param Minz_Extension $ext the extension which register the hook.
*/
public static function addHook($hook_name, $hook_function, $ext) {
if (isset(self::$hook_list[$hook_name]) && is_callable($hook_function)) {
@@ -241,8 +241,8 @@ class Minz_ExtensionManager {
* The hook name must be a valid one. For the valid list, see self::$hook_list
* array keys.
*
- * @param $hook_name the hook to call.
- * @param additionnal parameters (for signature, please see self::$hook_list).
+ * @param string $hook_name the hook to call.
+ * @param additional parameters (for signature, please see self::$hook_list).
* @return the final result of the called hook.
*/
public static function callHook($hook_name) {
diff --git a/lib/Minz/Log.php b/lib/Minz/Log.php
index 9559a0bd4..a8dbf8350 100644
--- a/lib/Minz/Log.php
+++ b/lib/Minz/Log.php
@@ -29,6 +29,7 @@ class Minz_Log {
* @param $information message d'erreur / information à enregistrer
* @param $level niveau d'erreur
* @param $file_name fichier de log
+ * @throws Minz_PermissionDeniedException
*/
public static function record ($information, $level, $file_name = null) {
try {
@@ -70,6 +71,8 @@ class Minz_Log {
. ' [' . $level_label . ']'
. ' --- ' . $information . "\n";
+ self::ensureMaxLogSize($file_name);
+
if (file_put_contents($file_name, $log, FILE_APPEND | LOCK_EX) === false) {
throw new Minz_PermissionDeniedException($file_name, Minz_Exception::ERROR);
}
@@ -77,6 +80,37 @@ class Minz_Log {
}
/**
+ * Make sure we do not waste a huge amount of disk space with old log messages.
+ *
+ * This method can be called multiple times for one script execution, but its result will not change unless
+ * you call clearstatcache() in between. We won't due do that for performance reasons.
+ *
+ * @param $file_name
+ * @throws Minz_PermissionDeniedException
+ */
+ protected static function ensureMaxLogSize($file_name) {
+ $maxSize = defined('MAX_LOG_SIZE') ? MAX_LOG_SIZE : 1048576;
+ if ($maxSize > 0 && @filesize($file_name) > $maxSize) {
+ $fp = fopen($file_name, 'c+');
+ if ($fp && flock($fp, LOCK_EX)) {
+ fseek($fp, -intval($maxSize / 2), SEEK_END);
+ $content = fread($fp, $maxSize);
+ rewind($fp);
+ ftruncate($fp, 0);
+ fwrite($fp, $content ? $content : '');
+ fwrite($fp, sprintf("[%s] [notice] --- Log rotate.\n", date('r')));
+ fflush($fp);
+ flock($fp, LOCK_UN);
+ } else {
+ throw new Minz_PermissionDeniedException($file_name, Minz_Exception::ERROR);
+ }
+ if ($fp) {
+ fclose($fp);
+ }
+ }
+ }
+
+ /**
* Automatise le log des variables globales $_GET et $_POST
* Fait appel à la fonction record(...)
* Ne fonctionne qu'en environnement "development"
diff --git a/lib/favicons.php b/lib/favicons.php
index a7ed966a1..fe2e65f1f 100644
--- a/lib/favicons.php
+++ b/lib/favicons.php
@@ -24,19 +24,21 @@ function isImgMime($content) {
function downloadHttp(&$url, $curlOptions = array()) {
syslog(LOG_INFO, 'FreshRSS Favicon GET ' . $url);
if (substr($url, 0, 2) === '//') {
- $url = 'https:' . $favicon;
+ $url = 'https:' . $url;
}
if ($url == '' || filter_var($url, FILTER_VALIDATE_URL) === false) {
return '';
}
$ch = curl_init($url);
curl_setopt_array($ch, array(
- CURLOPT_FOLLOWLOCATION => true,
- CURLOPT_MAXREDIRS => 10,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
- CURLOPT_USERAGENT => 'FreshRSS/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ')',
+ CURLOPT_USERAGENT => FRESHRSS_USERAGENT,
+ CURLOPT_MAXREDIRS => 10,
));
+ if (version_compare(PHP_VERSION, '5.6.0') >= 0 || ini_get('open_basedir') == '') {
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); //Keep option separated for open_basedir PHP bug 65646
+ }
if (defined('CURLOPT_ENCODING')) {
curl_setopt($ch, CURLOPT_ENCODING, ''); //Enable all encodings
}
diff --git a/lib/lib_rss.php b/lib/lib_rss.php
index 09048700d..e9c4da049 100644
--- a/lib/lib_rss.php
+++ b/lib/lib_rss.php
@@ -4,7 +4,7 @@ if (version_compare(PHP_VERSION, '5.3.8', '<')) {
}
if (!function_exists('json_decode')) {
- require_once('JSON.php');
+ require_once(__DIR__ . '/JSON.php');
function json_decode($var, $assoc = false) {
$JSON = new Services_JSON($assoc ? SERVICES_JSON_LOOSE_TYPE : 0);
return $JSON->decode($var);
@@ -12,7 +12,7 @@ if (!function_exists('json_decode')) {
}
if (!function_exists('json_encode')) {
- require_once('JSON.php');
+ require_once(__DIR__ . '/JSON.php');
function json_encode($var) {
$JSON = new Services_JSON();
return $JSON->encodeUnsafe($var);
@@ -62,7 +62,12 @@ function idn_to_puny($url) {
$parts = parse_url($url);
if (!empty($parts['host'])) {
$idn = $parts['host'];
- $puny = idn_to_ascii($idn);
+ // INTL_IDNA_VARIANT_UTS46 is defined starting in PHP 5.4
+ if (defined('INTL_IDNA_VARIANT_UTS46')) {
+ $puny = idn_to_ascii($idn, 0, INTL_IDNA_VARIANT_UTS46);
+ } else {
+ $puny = idn_to_ascii($idn);
+ }
$pos = strpos($url, $idn);
if ($pos !== false) {
return substr_replace($url, $puny, $pos, strlen($idn));
@@ -174,7 +179,7 @@ function customSimplePie() {
$system_conf = Minz_Configuration::get('system');
$limits = $system_conf->limits;
$simplePie = new SimplePie();
- $simplePie->set_useragent('FreshRSS/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ') ' . SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION);
+ $simplePie->set_useragent(FRESHRSS_USERAGENT);
$simplePie->set_syslog($system_conf->simplepie_syslog_enabled);
$simplePie->set_cache_location(CACHE_PATH);
$simplePie->set_cache_duration($limits['cache_duration']);
@@ -397,12 +402,13 @@ function is_referer_from_same_domain() {
*/
function check_install_php() {
$pdo_mysql = extension_loaded('pdo_mysql');
+ $pdo_pgsql = extension_loaded('pdo_pgsql');
$pdo_sqlite = extension_loaded('pdo_sqlite');
return array(
'php' => version_compare(PHP_VERSION, '5.3.8') >= 0,
'minz' => file_exists(LIB_PATH . '/Minz'),
'curl' => extension_loaded('curl'),
- 'pdo' => $pdo_mysql || $pdo_sqlite,
+ 'pdo' => $pdo_mysql || $pdo_sqlite || $pdo_pgsql,
'pcre' => extension_loaded('pcre'),
'ctype' => extension_loaded('ctype'),
'fileinfo' => extension_loaded('fileinfo'),
diff --git a/p/.htaccess b/p/.htaccess
index 4321c82d7..74ba7ed11 100644
--- a/p/.htaccess
+++ b/p/.htaccess
@@ -6,6 +6,11 @@ FileETag None
AddDefaultCharset UTF-8
<IfModule mod_mime.c>
+ AddType application/json .map
+ AddType application/font-woff .woff
+ AddType application/font-woff2 .woff2
+
+ AddCharset UTF-8 .css
AddCharset UTF-8 .html
AddCharset UTF-8 .js
</IfModule>
@@ -16,6 +21,8 @@ AddDefaultCharset UTF-8
<IfModule mod_expires.c>
ExpiresActive on
+ ExpiresByType application/font-woff "access plus 1 month"
+ ExpiresByType application/font-woff2 "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType application/xhtml+xml "access plus 1 month"
ExpiresByType image/x-icon "access plus 1 month"
@@ -27,7 +34,7 @@ AddDefaultCharset UTF-8
</IfModule>
<IfModule mod_headers.c>
- <FilesMatch "\.(css|gif|html|ico|js|png|svg|woff)$">
+ <FilesMatch "\.(css|gif|html|ico|js|png|svg|woff|woff2)$">
Header merge Cache-Control "public"
</FilesMatch>
</IfModule>
diff --git a/p/api/greader.php b/p/api/greader.php
index b87fcc225..99304f4ec 100644
--- a/p/api/greader.php
+++ b/p/api/greader.php
@@ -20,7 +20,7 @@ Server-side API compatible with Google Reader API layer 2
* https://github.com/theoldreader/api
*/
-require('../../constants.php');
+require(__DIR__ . '/../../constants.php');
require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader
$ORIGINAL_INPUT = file_get_contents('php://input', false, null, 0, 1048576);
@@ -78,10 +78,6 @@ class MyPDO extends Minz_ModelPdo {
}
}
-function logMe($text) {
- file_put_contents(join_path(USERS_PATH, '_', 'log_api.txt'), date('c') . "\t" . $text . "\n", FILE_APPEND);
-}
-
function debugInfo() {
if (function_exists('getallheaders')) {
$ALL_HEADERS = getallheaders();
@@ -107,16 +103,14 @@ function debugInfo() {
}
function badRequest() {
- logMe("badRequest()");
- logMe(debugInfo());
+ Minz_Log::warning('badRequest() ' . debugInfo(), API_LOG);
header('HTTP/1.1 400 Bad Request');
header('Content-Type: text/plain; charset=UTF-8');
die('Bad Request!');
}
function unauthorized() {
- logMe("unauthorized()");
- logMe(debugInfo());
+ Minz_Log::warning('unauthorized() ' . debugInfo(), API_LOG);
header('HTTP/1.1 401 Unauthorized');
header('Content-Type: text/plain; charset=UTF-8');
header('Google-Bad-Token: true');
@@ -124,22 +118,21 @@ function unauthorized() {
}
function notImplemented() {
- logMe("notImplemented()");
- logMe(debugInfo());
+ Minz_Log::warning('notImplemented() ' . debugInfo(), API_LOG);
header('HTTP/1.1 501 Not Implemented');
header('Content-Type: text/plain; charset=UTF-8');
die('Not Implemented!');
}
function serviceUnavailable() {
- logMe("serviceUnavailable()");
+ Minz_Log::warning('serviceUnavailable() ' . debugInfo(), API_LOG);
header('HTTP/1.1 503 Service Unavailable');
header('Content-Type: text/plain; charset=UTF-8');
die('Service Unavailable!');
}
function checkCompatibility() {
- logMe("checkCompatibility()");
+ Minz_Log::warning('checkCompatibility() ' . debugInfo(), API_LOG);
header('Content-Type: text/plain; charset=UTF-8');
if (PHP_INT_SIZE < 8 && !function_exists('gmp_init')) {
die('FAIL 64-bit or GMP extension!');
@@ -170,7 +163,7 @@ function authorizationToUser() {
if ($headerAuthX[1] === sha1(FreshRSS_Context::$system_conf->salt . $user . FreshRSS_Context::$user_conf->apiPasswordHash)) {
return $user;
} else {
- logMe('Invalid API authorisation for user ' . $user . ': ' . $headerAuthX[1]);
+ Minz_Log::warning('Invalid API authorisation for user ' . $user . ': ' . $headerAuthX[1], API_LOG);
Minz_Log::warning('Invalid API authorisation for user ' . $user . ': ' . $headerAuthX[1]);
unauthorized();
}
@@ -183,7 +176,6 @@ function authorizationToUser() {
}
function clientLogin($email, $pass) { //http://web.archive.org/web/20130604091042/http://undoc.in/clientLogin.html
- //logMe('clientLogin(' . $email . ")");
if (ctype_alnum($email)) {
if (!function_exists('password_verify')) {
include_once(LIB_PATH . '/password_compat.php');
@@ -215,7 +207,7 @@ function token($conf) {
//http://blog.martindoms.com/2009/08/15/using-the-google-reader-api-part-1/
//https://github.com/ericmann/gReader-Library/blob/master/greader.class.php
$user = Minz_Session::param('currentUser', '_');
- //logMe('token('. $user . ")"); //TODO: Implement real token that expires
+ //Minz_Log::debug('token('. $user . ')', API_LOG); //TODO: Implement real token that expires
$token = str_pad(sha1(FreshRSS_Context::$system_conf->salt . $user . $conf->apiPasswordHash), 57, 'Z'); //Must have 57 characters
echo $token, "\n";
exit();
@@ -224,7 +216,6 @@ function token($conf) {
function checkToken($conf, $token) {
//http://code.google.com/p/google-reader-api/wiki/ActionToken
$user = Minz_Session::param('currentUser', '_');
- //logMe('checkToken(' . $token . ")");
if ($token === str_pad(sha1(FreshRSS_Context::$system_conf->salt . $user . $conf->apiPasswordHash), 57, 'Z')) {
return true;
}
@@ -232,7 +223,6 @@ function checkToken($conf, $token) {
}
function userInfo() { //https://github.com/theoldreader/api#user-info
- //logMe("userInfo()");
$user = Minz_Session::param('currentUser', '_');
exit(json_encode(array(
'userId' => $user,
@@ -243,7 +233,6 @@ function userInfo() { //https://github.com/theoldreader/api#user-info
}
function tagList() {
- //logMe("tagList()");
header('Content-Type: application/json; charset=UTF-8');
$pdo = new MyPDO();
@@ -268,7 +257,6 @@ function tagList() {
}
function subscriptionList() {
- //logMe("subscriptionList()");
header('Content-Type: application/json; charset=UTF-8');
$pdo = new MyPDO();
@@ -303,7 +291,6 @@ function subscriptionList() {
}
function subscriptionEdit($streamNames, $titles, $action, $add = '', $remove = '') {
- //logMe("subscriptionEdit()");
//https://github.com/mihaip/google-reader-api/blob/master/wiki/ApiSubscriptionEdit.wiki
switch ($action) {
case 'subscribe':
@@ -360,7 +347,7 @@ function subscriptionEdit($streamNames, $titles, $action, $add = '', $remove = '
$feed = FreshRSS_feed_Controller::addFeed($streamName, $title, $addCatId, $c_name, $http_auth);
continue;
} catch (Exception $e) {
- logMe("subscriptionEdit error subscribe: " . $e->getMessage());
+ Minz_Log::error('subscriptionEdit error subscribe: ' . $e->getMessage(), API_LOG);
}
}
badRequest();
@@ -389,7 +376,6 @@ function subscriptionEdit($streamNames, $titles, $action, $add = '', $remove = '
}
function quickadd($url) {
- //logMe("quickadd($url)");
try {
$feed = FreshRSS_feed_Controller::addFeed($url);
exit(json_encode(array(
@@ -397,7 +383,7 @@ function quickadd($url) {
'streamId' => $feed->id(),
)));
} catch (Exception $e) {
- logMe("subscriptionEdit error subscribe: " . $e->getMessage());
+ Minz_Log::error('quickadd error: ' . $e->getMessage(), API_LOG);
die(json_encode(array(
'numResults' => 0,
'error' => $e->getMessage(),
@@ -406,7 +392,6 @@ function quickadd($url) {
}
function unreadCount() { //http://blog.martindoms.com/2009/10/16/using-the-google-reader-api-part-2/#unread-count
- //logMe("unreadCount()");
header('Content-Type: application/json; charset=UTF-8');
$totalUnreads = 0;
@@ -453,7 +438,6 @@ function unreadCount() { //http://blog.martindoms.com/2009/10/16/using-the-googl
function streamContents($path, $include_target, $start_time, $count, $order, $exclude_target, $continuation) {
//http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI
//http://blog.martindoms.com/2009/10/16/using-the-google-reader-api-part-2/#feed
- //logMe("streamContents($path, $include_target, $start_time, $count, $order, $exclude_target, $continuation)");
header('Content-Type: application/json; charset=UTF-8');
$feedDAO = FreshRSS_Factory::createFeedDao();
@@ -517,7 +501,7 @@ function streamContents($path, $include_target, $start_time, $count, $order, $ex
'title' => $entry->title(),
'summary' => array('content' => $entry->content()),
'alternate' => array(
- array('href' => $entry->link()),
+ array('href' => htmlspecialchars_decode($entry->link(), ENT_QUOTES)),
),
'categories' => array(
'user/-/state/com.google/reading-list',
@@ -562,8 +546,6 @@ function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude
//http://code.google.com/p/google-reader-api/wiki/ApiStreamItemsIds
//http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI
//http://blog.martindoms.com/2009/10/16/using-the-google-reader-api-part-2/#feed
- //logMe("streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude_target)");
-
$type = 'A';
$id = '';
if ($streamId === 'user/-/state/com.google/reading-list') {
@@ -610,8 +592,6 @@ function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude
}
function editTag($e_ids, $a, $r) {
- //logMe("editTag()");
-
foreach ($e_ids as $i => $e_id) {
$e_ids[$i] = hex2dec(basename($e_id)); //Strip prefix 'tag:google.com,2005:reader/item/'
}
@@ -645,7 +625,6 @@ function editTag($e_ids, $a, $r) {
}
function renameTag($s, $dest) {
- //logMe("renameTag()");
if ($s != '' && strpos($s, 'user/-/label/') === 0 &&
$dest != '' && strpos($dest, 'user/-/label/') === 0) {
$s = substr($s, 13);
@@ -661,7 +640,6 @@ function renameTag($s, $dest) {
}
function disableTag($s) {
- //logMe("disableTag($s)");
if ($s != '' && strpos($s, 'user/-/label/') === 0) {
$s = substr($s, 13);
$categoryDAO = new FreshRSS_CategoryDAO();
@@ -679,7 +657,6 @@ function disableTag($s) {
}
function markAllAsRead($streamId, $olderThanId) {
- //logMe("markAllAsRead($streamId, $olderThanId)");
$entryDAO = FreshRSS_Factory::createEntryDao();
if (strpos($streamId, 'feed/') === 0) {
$f_id = basename($streamId);
@@ -696,8 +673,8 @@ function markAllAsRead($streamId, $olderThanId) {
exit('OK');
}
-//logMe('----------------------------------------------------------------');
-//logMe(debugInfo());
+//Minz_Log::debug('----------------------------------------------------------------', API_LOG);
+//Minz_Log::debug(debugInfo(), API_LOG);
$pathInfo = empty($_SERVER['PATH_INFO']) ? '/Error' : urldecode($_SERVER['PATH_INFO']);
$pathInfos = explode('/', $pathInfo);
@@ -718,8 +695,6 @@ if ($user !== '') {
FreshRSS_Context::$user_conf = get_user_configuration($user);
}
-//logMe('User => ' . $user);
-
Minz_Session::_param('currentUser', $user);
if (count($pathInfos) < 3) {
diff --git a/p/api/index.php b/p/api/index.php
index 08f7b6b7b..429b25225 100644
--- a/p/api/index.php
+++ b/p/api/index.php
@@ -14,7 +14,7 @@
<dl>
<dt>Your API address:</dt>
<dd><?php
-require('../../constants.php');
+require(__DIR__ . '/../../constants.php');
require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader
Minz_Configuration::register('system', DATA_PATH . '/config.php', FRESHRSS_PATH . '/config.default.php');
echo Minz_Url::display('/api/greader.php', 'html', true);
diff --git a/p/api/pshb.php b/p/api/pshb.php
index ed8326cf5..57a7bb0dd 100644
--- a/p/api/pshb.php
+++ b/p/api/pshb.php
@@ -1,19 +1,19 @@
<?php
-require('../../constants.php');
+require(__DIR__ . '/../../constants.php');
require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader
-define('MAX_PAYLOAD', 3145728);
+const MAX_PAYLOAD = 3145728;
header('Content-Type: text/plain; charset=UTF-8');
header('X-Content-Type-Options: nosniff');
-function logMe($text) {
- file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND);
-}
-
$ORIGINAL_INPUT = file_get_contents('php://input', false, null, 0, MAX_PAYLOAD);
-//logMe(print_r(array('_SERVER' => $_SERVER, '_GET' => $_GET, '_POST' => $_POST, 'INPUT' => $ORIGINAL_INPUT), true));
+Minz_Configuration::register('system', DATA_PATH . '/config.php', FRESHRSS_PATH . '/config.default.php');
+$system_conf = Minz_Configuration::get('system');
+$system_conf->auth_type = 'none'; // avoid necessity to be logged in (not saved!)
+
+//Minz_Log::debug(print_r(array('_SERVER' => $_SERVER, '_GET' => $_GET, '_POST' => $_POST, 'INPUT' => $ORIGINAL_INPUT), true), PSHB_LOG);
$key = isset($_GET['k']) ? substr($_GET['k'], 0, 128) : '';
if (!ctype_xdigit($key)) {
@@ -24,31 +24,31 @@ chdir(PSHB_PATH);
$canonical64 = @file_get_contents('keys/' . $key . '.txt');
if ($canonical64 === false) {
if (!empty($_REQUEST['hub_mode']) && $_REQUEST['hub_mode'] === 'unsubscribe') {
- logMe('Warning: Accept unknown unsubscribe');
+ Minz_Log::warning('Warning: Accept unknown unsubscribe', PSHB_LOG);
header('Connection: close');
exit(isset($_REQUEST['hub_challenge']) ? $_REQUEST['hub_challenge'] : '');
}
header('HTTP/1.1 404 Not Found');
- logMe('Warning: Feed key not found!: ' . $key);
+ Minz_Log::warning('Warning: Feed key not found!: ' . $key, PSHB_LOG);
die('Feed key not found!');
}
$canonical64 = trim($canonical64);
if (!preg_match('/^[A-Za-z0-9_-]+$/D', $canonical64)) {
header('HTTP/1.1 500 Internal Server Error');
- logMe('Error: Invalid key reference!: ' . $canonical64);
+ Minz_Log::error('Error: Invalid key reference!: ' . $canonical64, PSHB_LOG);
die('Invalid key reference!');
}
$hubFile = @file_get_contents('feeds/' . $canonical64 . '/!hub.json');
if ($hubFile === false) {
header('HTTP/1.1 404 Not Found');
unlink('keys/' . $key . '.txt');
- logMe('Error: Feed info not found!: ' . $canonical64);
+ Minz_Log::error('Error: Feed info not found!: ' . $canonical64, PSHB_LOG);
die('Feed info not found!');
}
$hubJson = json_decode($hubFile, true);
if (!$hubJson || empty($hubJson['key']) || $hubJson['key'] !== $key) {
header('HTTP/1.1 500 Internal Server Error');
- logMe('Error: Invalid key cross-check!: ' . $key);
+ Minz_Log::error('Error: Invalid key cross-check!: ' . $key, PSHB_LOG);
die('Invalid key cross-check!');
}
chdir('feeds/' . $canonical64);
@@ -56,7 +56,7 @@ $users = glob('*.txt', GLOB_NOSORT);
if (empty($users)) {
header('HTTP/1.1 410 Gone');
$url = base64url_decode($canonical64);
- logMe('Warning: Nobody subscribes to this feed anymore!: ' . $url);
+ Minz_Log::warning('Warning: Nobody subscribes to this feed anymore!: ' . $url, PSHB_LOG);
unlink('../../keys/' . $key . '.txt');
Minz_Configuration::register('system',
DATA_PATH . '/config.php',
@@ -101,10 +101,6 @@ if ($ORIGINAL_INPUT == '') {
die('Missing XML payload!');
}
-Minz_Configuration::register('system', DATA_PATH . '/config.php', FRESHRSS_PATH . '/config.default.php');
-$system_conf = Minz_Configuration::get('system');
-$system_conf->auth_type = 'none'; // avoid necessity to be logged in (not saved!)
-
$simplePie = customSimplePie();
$simplePie->set_raw_data($ORIGINAL_INPUT);
$simplePie->init();
@@ -115,7 +111,7 @@ $self = isset($links[0]) ? $links[0] : null;
if ($self !== base64url_decode($canonical64)) {
//header('HTTP/1.1 422 Unprocessable Entity');
- logMe('Warning: Self URL [' . $self . '] does not match registered canonical URL!: ' . base64url_decode($canonical64));
+ Minz_Log::warning('Warning: Self URL [' . $self . '] does not match registered canonical URL!: ' . base64url_decode($canonical64), PSHB_LOG);
//die('Self URL does not match registered canonical URL!');
$self = base64url_decode($canonical64);
}
@@ -124,7 +120,7 @@ $nb = 0;
foreach ($users as $userFilename) {
$username = basename($userFilename, '.txt');
if (!file_exists(USERS_PATH . '/' . $username . '/config.php')) {
- logMe('Warning: Removing broken user link: ' . $username . ' for ' . $self);
+ Minz_Log::warning('Warning: Removing broken user link: ' . $username . ' for ' . $self, PSHB_LOG);
unlink($userFilename);
continue;
}
@@ -140,11 +136,11 @@ foreach ($users as $userFilename) {
if ($updated_feeds > 0 || $feed != false) {
$nb++;
} else {
- logMe('Warning: User ' . $username . ' does not subscribe anymore to ' . $self);
+ Minz_Log::warning('Warning: User ' . $username . ' does not subscribe anymore to ' . $self, PSHB_LOG);
unlink($userFilename);
}
} catch (Exception $e) {
- logMe('Error: ' . $e->getMessage() . ' for user ' . $username . ' and feed ' . $self);
+ Minz_Log::error('Error: ' . $e->getMessage() . ' for user ' . $username . ' and feed ' . $self, PSHB_LOG);
}
}
@@ -153,12 +149,12 @@ unset($simplePie);
if ($nb === 0) {
header('HTTP/1.1 410 Gone');
- logMe('Warning: Nobody subscribes to this feed anymore after all!: ' . $self);
+ Minz_Log::warning('Warning: Nobody subscribes to this feed anymore after all!: ' . $self, PSHB_LOG);
die('Nobody subscribes to this feed anymore after all!');
} elseif (!empty($hubJson['error'])) {
$hubJson['error'] = false;
file_put_contents('./!hub.json', json_encode($hubJson));
}
-logMe('PubSubHubbub ' . $self . ' done: ' . $nb);
+Minz_Log::notice('PubSubHubbub ' . $self . ' done: ' . $nb, PSHB_LOG);
exit('Done: ' . $nb . "\n");
diff --git a/p/ext.php b/p/ext.php
index 5c9f9125f..bb16d02d3 100644
--- a/p/ext.php
+++ b/p/ext.php
@@ -5,7 +5,7 @@ if (!isset($_GET['f']) ||
die();
}
-require('../constants.php');
+require(__DIR__ . '/../constants.php');
/**
* Check if a file can be served by ext.php. A valid file is under a
diff --git a/p/f.php b/p/f.php
index c47fa747a..81df8984b 100644
--- a/p/f.php
+++ b/p/f.php
@@ -1,5 +1,5 @@
<?php
-require('../constants.php');
+require(__DIR__ . '/../constants.php');
require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader
require(LIB_PATH . '/favicons.php');
require(LIB_PATH . '/http-conditional.php');
diff --git a/p/i/index.php b/p/i/index.php
index d3fc0b37c..a1212b570 100755
--- a/p/i/index.php
+++ b/p/i/index.php
@@ -18,7 +18,7 @@
#
# ***** END LICENSE BLOCK *****
-require('../../constants.php');
+require(__DIR__ . '/../../constants.php');
require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader
if (file_exists(DATA_PATH . '/do-install.txt')) {
diff --git a/p/scripts/main.js b/p/scripts/main.js
index 278ecfee9..ce8070008 100644
--- a/p/scripts/main.js
+++ b/p/scripts/main.js
@@ -1172,6 +1172,14 @@ function init_print_action() {
});
}
+function init_post_action() {
+ $('.item.share > a[href="POST"]').click(function (event) {
+ event.preventDefault();
+ var form = $(this).next('form');
+ $.post(form.data('url'), form.serialize());
+ });
+}
+
function init_share_observers() {
shares = $('.group-share').length;
@@ -1182,6 +1190,8 @@ function init_share_observers() {
row = row.replace(/##type##/g, opt.val());
row = row.replace(/##help##/g, opt.data('help'));
row = row.replace(/##key##/g, shares);
+ row = row.replace(/##method##/g, opt.data('method'));
+ row = row.replace(/##field##/g, opt.data('field'));
$(this).parents('.form-group').before(row);
shares++;
@@ -1398,6 +1408,7 @@ function init_afterDOM() {
init_posts();
init_nav_entries();
init_print_action();
+ init_post_action();
init_notifs_html5();
window.setInterval(refreshUnreads, 120000);
} else {
diff --git a/p/themes/BlueLagoon/BlueLagoon.css b/p/themes/BlueLagoon/BlueLagoon.css
index 150e27908..186258752 100644
--- a/p/themes/BlueLagoon/BlueLagoon.css
+++ b/p/themes/BlueLagoon/BlueLagoon.css
@@ -1,11 +1,5 @@
@charset "UTF-8";
-/*=== FONTS */
-@font-face {
- font-family: "OpenSans";
- src: url("../fonts/openSans.woff") format("woff");
-}
-
/*=== GENERAL */
/*============*/
html, body {
diff --git a/p/themes/Dark/dark.css b/p/themes/Dark/dark.css
index d8415ef25..348b00009 100644
--- a/p/themes/Dark/dark.css
+++ b/p/themes/Dark/dark.css
@@ -1,11 +1,5 @@
@charset "UTF-8";
-/*=== FONTS */
-@font-face {
- font-family: "OpenSans";
- src: url("../fonts/openSans.woff") format("woff");
-}
-
/*=== GENERAL */
/*============*/
html, body {
diff --git a/p/themes/Flat/flat.css b/p/themes/Flat/flat.css
index 0240fe4b4..62c4808a4 100644
--- a/p/themes/Flat/flat.css
+++ b/p/themes/Flat/flat.css
@@ -1,11 +1,5 @@
@charset "UTF-8";
-/*=== FONTS */
-@font-face {
- font-family: "OpenSans";
- src: url("../fonts/openSans.woff") format("woff");
-}
-
/*=== GENERAL */
/*============*/
html, body {
diff --git a/p/themes/Origine/origine.css b/p/themes/Origine/origine.css
index becf3f433..4a697e811 100644
--- a/p/themes/Origine/origine.css
+++ b/p/themes/Origine/origine.css
@@ -1,11 +1,5 @@
@charset "UTF-8";
-/*=== FONTS */
-@font-face {
- font-family: "OpenSans";
- src: url("../fonts/openSans.woff") format("woff");
-}
-
/*=== GENERAL */
/*============*/
html, body {
diff --git a/p/themes/Pafat/pafat.css b/p/themes/Pafat/pafat.css
index 23bc6671d..1b6ebca29 100644
--- a/p/themes/Pafat/pafat.css
+++ b/p/themes/Pafat/pafat.css
@@ -1,11 +1,5 @@
@charset "UTF-8";
-/*=== FONTS */
-@font-face {
- font-family: "OpenSans";
- src: url("../fonts/openSans.woff") format("woff");
-}
-
/*=== GENERAL */
/*============*/
html, body {
@@ -1069,4 +1063,4 @@ a.btn {
.notification a.close .icon {
display: none;
}
-} \ No newline at end of file
+}
diff --git a/p/themes/Screwdriver/screwdriver.css b/p/themes/Screwdriver/screwdriver.css
index b2c539b13..969695f13 100644
--- a/p/themes/Screwdriver/screwdriver.css
+++ b/p/themes/Screwdriver/screwdriver.css
@@ -1,11 +1,5 @@
@charset "UTF-8";
-/*=== FONTS */
-@font-face {
- font-family: "OpenSans";
- src: url("../fonts/openSans.woff") format("woff");
-}
-
/*=== GENERAL */
/*============*/
html, body {
diff --git a/p/themes/base-theme/base.css b/p/themes/base-theme/base.css
index 1bf73d8b3..e265cd7ff 100644
--- a/p/themes/base-theme/base.css
+++ b/p/themes/base-theme/base.css
@@ -1,11 +1,5 @@
@charset "UTF-8";
-/*=== FONTS */
-@font-face {
- font-family: "OpenSans";
- src: url("../fonts/openSans.woff") format("woff");
-}
-
/*=== GENERAL */
/*============*/
html, body {
diff --git a/p/themes/base-theme/template.css b/p/themes/base-theme/template.css
index 320ad2d9b..e5e1bca05 100644
--- a/p/themes/base-theme/template.css
+++ b/p/themes/base-theme/template.css
@@ -2,9 +2,19 @@
/*=== GENERAL */
/*============*/
+@font-face {
+ font-family: 'OpenSans';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Open Sans'), local('OpenSans'),
+ url('../fonts/OpenSans.woff2') format('woff2'),
+ url('../fonts/OpenSans.woff') format('woff');
+}
+
html, body {
margin: 0;
padding: 0;
+ font-family: "OpenSans", "Cantarell", "Helvetica", "Arial", sans-serif;
font-size: 100%;
}
diff --git a/p/themes/fonts/OpenSans.woff b/p/themes/fonts/OpenSans.woff
new file mode 100644
index 000000000..9a96e3baf
--- /dev/null
+++ b/p/themes/fonts/OpenSans.woff
Binary files differ
diff --git a/p/themes/fonts/OpenSans.woff2 b/p/themes/fonts/OpenSans.woff2
new file mode 100644
index 000000000..0964c7c46
--- /dev/null
+++ b/p/themes/fonts/OpenSans.woff2
Binary files differ
diff --git a/p/themes/fonts/openSans.woff b/p/themes/fonts/openSans.woff
deleted file mode 100644
index 55b25f867..000000000
--- a/p/themes/fonts/openSans.woff
+++ /dev/null
Binary files differ
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index 896929649..5c42f12f7 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -3,5 +3,5 @@
error_reporting(E_ALL);
ini_set('display_errors', 1);
-require('../constants.php');
+require(__DIR__ . '/../constants.php');
require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader