Răsfoiți Sursa

Merge pull request #2001 from FreshRSS/dev

FreshRSS 1.11.2
Alexandre Alapetite 7 ani în urmă
părinte
comite
44bd07e506
100 a modificat fișierele cu 407 adăugiri și 156 ștergeri
  1. 0 1
      .travis.yml
  2. 29 0
      CHANGELOG.md
  3. 13 14
      CONTRIBUTING.md
  4. 3 0
      CREDITS.md
  5. 1 1
      Docker/Dockerfile
  6. 1 1
      Docker/README.md
  7. 4 5
      README.fr.md
  8. 4 5
      README.md
  9. 10 10
      app/Controllers/entryController.php
  10. 11 12
      app/Controllers/feedController.php
  11. 1 1
      app/Controllers/indexController.php
  12. 2 2
      app/Controllers/javascriptController.php
  13. 11 0
      app/Controllers/userController.php
  14. 1 1
      app/FreshRSS.php
  15. 1 1
      app/Models/Category.php
  16. 1 1
      app/Models/CategoryDAO.php
  17. 3 2
      app/Models/Entry.php
  18. 20 18
      app/Models/EntryDAO.php
  19. 10 7
      app/Models/EntryDAOSQLite.php
  20. 11 4
      app/Models/Feed.php
  21. 2 2
      app/Models/FeedDAO.php
  22. 5 1
      app/i18n/cz/admin.php
  23. 4 1
      app/i18n/cz/feedback.php
  24. 1 0
      app/i18n/cz/gen.php
  25. 1 0
      app/i18n/cz/index.php
  26. 4 0
      app/i18n/cz/install.php
  27. 5 1
      app/i18n/de/admin.php
  28. 4 1
      app/i18n/de/feedback.php
  29. 2 1
      app/i18n/de/gen.php
  30. 1 0
      app/i18n/de/index.php
  31. 4 0
      app/i18n/de/install.php
  32. 5 1
      app/i18n/en/admin.php
  33. 4 1
      app/i18n/en/feedback.php
  34. 1 0
      app/i18n/en/gen.php
  35. 1 0
      app/i18n/en/index.php
  36. 4 0
      app/i18n/en/install.php
  37. 5 1
      app/i18n/es/admin.php
  38. 4 1
      app/i18n/es/feedback.php
  39. 1 0
      app/i18n/es/gen.php
  40. 1 0
      app/i18n/es/index.php
  41. 4 0
      app/i18n/es/install.php
  42. 6 2
      app/i18n/fr/admin.php
  43. 4 1
      app/i18n/fr/feedback.php
  44. 1 0
      app/i18n/fr/gen.php
  45. 2 1
      app/i18n/fr/index.php
  46. 6 2
      app/i18n/fr/install.php
  47. 7 3
      app/i18n/he/admin.php
  48. 4 1
      app/i18n/he/feedback.php
  49. 1 0
      app/i18n/he/gen.php
  50. 1 0
      app/i18n/he/index.php
  51. 9 1
      app/i18n/he/install.php
  52. 5 1
      app/i18n/it/admin.php
  53. 4 1
      app/i18n/it/feedback.php
  54. 1 0
      app/i18n/it/gen.php
  55. 1 0
      app/i18n/it/index.php
  56. 4 0
      app/i18n/it/install.php
  57. 8 4
      app/i18n/kr/admin.php
  58. 5 5
      app/i18n/kr/conf.php
  59. 7 4
      app/i18n/kr/feedback.php
  60. 2 1
      app/i18n/kr/gen.php
  61. 1 0
      app/i18n/kr/index.php
  62. 5 1
      app/i18n/kr/install.php
  63. 2 2
      app/i18n/kr/sub.php
  64. 5 1
      app/i18n/nl/admin.php
  65. 4 1
      app/i18n/nl/feedback.php
  66. 1 0
      app/i18n/nl/gen.php
  67. 1 0
      app/i18n/nl/index.php
  68. 4 0
      app/i18n/nl/install.php
  69. 5 1
      app/i18n/pt-br/admin.php
  70. 4 1
      app/i18n/pt-br/feedback.php
  71. 1 0
      app/i18n/pt-br/gen.php
  72. 1 0
      app/i18n/pt-br/index.php
  73. 5 1
      app/i18n/pt-br/install.php
  74. 5 1
      app/i18n/ru/admin.php
  75. 4 1
      app/i18n/ru/feedback.php
  76. 1 0
      app/i18n/ru/gen.php
  77. 1 0
      app/i18n/ru/index.php
  78. 4 0
      app/i18n/ru/install.php
  79. 5 1
      app/i18n/tr/admin.php
  80. 4 1
      app/i18n/tr/feedback.php
  81. 1 0
      app/i18n/tr/gen.php
  82. 1 0
      app/i18n/tr/index.php
  83. 4 0
      app/i18n/tr/install.php
  84. 5 1
      app/i18n/zh-cn/admin.php
  85. 4 1
      app/i18n/zh-cn/feedback.php
  86. 1 0
      app/i18n/zh-cn/gen.php
  87. 1 0
      app/i18n/zh-cn/index.php
  88. 4 0
      app/i18n/zh-cn/install.php
  89. 7 1
      app/install.php
  90. 2 1
      app/layout/header.phtml
  91. 19 5
      app/layout/nav_menu.phtml
  92. 6 0
      app/shares.php
  93. 4 4
      app/views/helpers/export/opml.phtml
  94. 1 1
      app/views/helpers/index/normal/entry_bottom.phtml
  95. 1 1
      app/views/helpers/javascript_vars.phtml
  96. 1 1
      app/views/helpers/pagination.phtml
  97. 1 1
      app/views/stats/index.phtml
  98. 1 1
      app/views/stats/repartition.phtml
  99. 1 1
      cli/README.md
  100. 1 1
      cli/do-install.php

+ 0 - 1
.travis.yml

@@ -7,7 +7,6 @@ php:
   - '7.1'
   - '7.2'
   - hhvm
-  - nightly
 
 install:
   # newest version without https://github.com/squizlabs/PHP_CodeSniffer/pull/1404

+ 29 - 0
CHANGELOG.md

@@ -1,5 +1,34 @@
 # FreshRSS changelog
 
+## 2018-09-09 FreshRSS 1.11.2
+
+* Features
+	* New menu to mark selected articles (view) as unread [#1966](https://github.com/FreshRSS/FreshRSS/issues/1966)
+	* Share with LinkedIn [#1960](https://github.com/FreshRSS/FreshRSS/pull/1960)
+* Deployment
+	* Update Docker image to Alpine 3.8 with PHP 7.2 [#1956](https://github.com/FreshRSS/FreshRSS/pull/1956)
+* Bug fixing
+	* Fix bugs when searching with special characters (e.g. preventing marking as read) [#1944](https://github.com/FreshRSS/FreshRSS/issues/1944)
+	* Avoid cutting in the middle of a multi-byte Unicode character [#1996](https://github.com/FreshRSS/FreshRSS/pull/1996)
+	* Fix username check in API to allow underscores [#1955](https://github.com/FreshRSS/FreshRSS/issues/1955)
+	* Fix Fever API to allow 32-bit architectures [#1962](https://github.com/FreshRSS/FreshRSS/issues/1962)
+	* Fix CSS font bug for *Origine-compact* theme [#1990](https://github.com/FreshRSS/FreshRSS/issues/1990)
+	* Fix last user activity for SQLite and PostgreSQL [#2008](https://github.com/FreshRSS/FreshRSS/pull/2008)
+	* Fix article counts with SQLite [#2009](https://github.com/FreshRSS/FreshRSS/pull/2009)
+	* Fix some automatic URL generation cases [#1946](https://github.com/FreshRSS/FreshRSS/issues/1946)
+* Security
+	* Avoid feed credentials in logs [#1949](https://github.com/FreshRSS/FreshRSS/pull/1949)
+* UI
+	* Improved mark-as-read the bottom articles during scrolling [#1973](https://github.com/FreshRSS/FreshRSS/issues/1973)
+	* Show all authors for articles with multiple authors [#1968](https://github.com/FreshRSS/FreshRSS/issues/1968)
+* I18n
+	* Updated Korean [#1985](https://github.com/FreshRSS/FreshRSS/pull/1985)
+* Mics.
+	* Auto-login after self user creation [#1928](https://github.com/FreshRSS/FreshRSS/issues/1928)
+	* Better test if server has public address [#2010](https://github.com/FreshRSS/FreshRSS/pull/2010)
+	* Allow `-` in database name at install time [#2005](https://github.com/FreshRSS/FreshRSS/pull/2005)
+
+
 ## 2018-06-16 FreshRSS 1.11.1
 
 * Features

+ 13 - 14
CONTRIBUTING.md

@@ -1,19 +1,18 @@
 # How to contribute to FreshRSS?
 
-## Join us on the mailing lists
+## Chat with us
 
-Do you want to ask us some questions? Do you want to discuss with us? Don't hesitate to subscribe to our mailing lists!
+Do you want to ask us some questions? Do you want to discuss with us?
+Don’t hesitate to [join our Mattermost chat](https://framateam.org/signup_user_complete/?id=e2680d3e3128b9fac8fdb3003b0024ee)!
 
-- The first mailing is destined to generic information, it should be adapted to users. [Join mailing@freshrss.org](https://freshrss.org/mailman/listinfo/mailing).
-- The second mailing is mainly for developers. [Join dev@freshrss.org](https://freshrss.org/mailman/listinfo/dev)
 
 ## Report a bug
 
-You found a bug? Don't panic, here are some steps to report it easily:
+You found a bug? Dont panic, here are some steps to report it easily:
 
-1. Search for it on [the bug tracker](https://github.com/FreshRSS/FreshRSS/issues) (don't forget to use the search bar).
-2. If you find a similar bug, don't hesitate to post a comment to add more importance to the related ticket.
-3. If you didn't find it, [open a new ticket](https://github.com/FreshRSS/FreshRSS/issues/new).
+1. Search for it on [the bug tracker](https://github.com/FreshRSS/FreshRSS/issues) (dont forget to use the search bar).
+2. If you find a similar bug, dont hesitate to post a comment to add more importance to the related ticket.
+3. If you didnt find it, [open a new ticket](https://github.com/FreshRSS/FreshRSS/issues/new).
 
 If you have to create a new ticket, try to apply the following advices:
 
@@ -34,24 +33,24 @@ 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](https://freshrss.github.io/FreshRSS/en/developers/01_First_steps.html).
 
-**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 bugs easy to fix, have a look at the « [Good first issue](https://github.com/FreshRSS/FreshRSS/issues?q=label%3A%22good+first+issue+%3Ababy%3A%22) » and/or « [Help wanted](https://github.com/FreshRSS/FreshRSS/issues?q=label%3A%22help+wanted+%3Aoctocat%3A%22) » ticket labels.
 
 ## Submit an idea
 
 You have great ideas, yes! Don't be shy and open [a new ticket](https://github.com/FreshRSS/FreshRSS/issues/new) on our bug tracker to ask if we can implement it. The greatest ideas often come from the shyest suggestions!
 
-If your idea is nice, we'll have a look at it.
+If your idea is nice, well have a look at it.
 
 ## Contribute to internationalization (i18n)
 
-If you want to improve internationalization, please open a new ticket first and follow indications from « Fix a bug » section.
+If you want to improve internationalization, please open a new ticket first and follow indications from « Fix a bug » section.
 
 Translations are present in the subdirectories of `./app/i18n/`.
 
-We are working on a better way to handle internationalization but don't hesitate to suggest any idea!
+We are working on a better way to handle internationalization but dont hesitate to suggest any idea!
 
 ## 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](https://freshrss.github.io/FreshRSS/) always needs improvements in order to be more useful to newcomers. If you want to give some help, meet us on [the dedicated sub-folder](https://github.com/FreshRSS/FreshRSS/tree/master/docs)!

+ 3 - 0
CREDITS.md

@@ -34,6 +34,7 @@ People are sorted by name so please keep this order.
 * [Julien Reichardt](https://github.com/j8r): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=j8r), [Web](https://blog.jrei.ch/)
 * [Kevin Papst](https://github.com/kevinpapst): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=kevinpapst), [Web](http://www.kevinpapst.de/)
 * [Luc Didry](https://github.com/ldidry): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=ldidry), [Web](https://www.fiat-tux.fr/)
+* [Luc Sanchez](https://github.com/ColonelMoutarde): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=ColonelMoutarde), (https://www.luc-sanchez.fr/)
 * [marcomrc](https://github.com/marcomrc): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=marcomrc)
 * [Marcus Rohrmoser](https://github.com/mro): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=mro), [Web](http://mro.name/~me)
 * [Marien Fressinaud](https://github.com/marienfressinaud): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=marienfressinaud), [Web](https://marienfressinaud.fr/)
@@ -47,6 +48,7 @@ People are sorted by name so please keep this order.
 * [perrinjerome](https://github.com/perrinjerome): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:perrinjerome)
 * [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)
+* [primaeval](https://github.com/primaeval): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:primaeval)
 * [purexo](https://github.com/purexo): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:purexo), [Web](https://purexo.mom/)
 * [Quentin Dufour](https://github.com/superboum): [contributions](https://github.com/FreshRSS/documentation/commits?author=superboum), [Web](http://quentin.dufour.io/)
 * [Ramón Cutanda](https://github.com/rcutanda): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:rcutanda)
@@ -55,4 +57,5 @@ People are sorted by name so please keep this order.
 * [Tets42](https://github.com/Tets42): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=Tets42)
 * [Thomas Citharel](https://github.com/tcitworld): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:tomgue), [Web](https://www.tcit.fr/)
 * [tomgue](https://github.com/tomgue): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=tomgue)
+* [Uncovery](https://github.com/uncovery): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:uncovery)
 * [Wanabo](https://github.com/Wanabo): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=Wanabo)

+ 1 - 1
Docker/Dockerfile

@@ -1,4 +1,4 @@
-FROM alpine:3.7
+FROM alpine:3.8
 
 RUN apk add --no-cache \
 	apache2 php7-apache2 \

+ 1 - 1
Docker/README.md

@@ -20,7 +20,7 @@ git clone https://github.com/FreshRSS/FreshRSS.git
 
 cd ./FreshRSS/
 git pull
-sudo docker pull alpine:3.7
+sudo docker pull alpine:3.8
 sudo docker build --tag freshrss/freshrss -f Docker/Dockerfile .
 ```
 

+ 4 - 5
README.fr.md

@@ -37,7 +37,7 @@ Nous sommes une communauté amicale.
 * Serveur Web Apache2 (recommandé), ou nginx, lighttpd (non testé sur les autres)
 * PHP 5.3.8+ (PHP 5.4+ recommandé, et PHP 5.5+ pour les performances, et PHP 7+ pour d’encore meilleures performances)
 	* Requis : [cURL](https://secure.php.net/curl), [DOM](https://secure.php.net/dom), [XML](https://secure.php.net/xml), [session](https://secure.php.net/session), [ctype](https://secure.php.net/ctype), et [PDO_MySQL](https://secure.php.net/pdo-mysql) ou [PDO_SQLite](https://secure.php.net/pdo-sqlite) ou [PDO_PGSQL](https://secure.php.net/pdo-pgsql)
-	* Recommandés : [JSON](https://secure.php.net/json), [GMP](https://secure.php.net/gmp) (pour accès API sur plateformes < 64 bits), [IDN](https://secure.php.net/intl.idn) (pour les noms de domaines internationalisés), [mbstring](https://secure.php.net/mbstring) et/ou [iconv](https://secure.php.net/iconv) (pour conversion d’encodages), [ZIP](https://secure.php.net/zip) (pour import/export), [zlib](https://secure.php.net/zlib) (pour les flux compressés)
+	* Recommandés : [JSON](https://secure.php.net/json), [GMP](https://secure.php.net/gmp) (pour accès API sur plateformes < 64 bits), [IDN](https://secure.php.net/intl.idn) (pour les noms de domaines internationalisés), [mbstring](https://secure.php.net/mbstring) (pour le texte Unicode), [iconv](https://secure.php.net/iconv) (pour conversion d’encodages), [ZIP](https://secure.php.net/zip) (pour import/export), [zlib](https://secure.php.net/zlib) (pour les flux compressés)
 * MySQL 5.5.3+ (recommandé), ou SQLite 3.7.4+, ou PostgreSQL 9.2+
 * Un navigateur Web récent tel que Firefox / IceCat, Internet Explorer 11 / Edge, Chromium / Chrome, Opera, Safari.
 	* Fonctionne aussi sur mobile
@@ -54,16 +54,15 @@ Nous sommes une communauté amicale.
 4. Accédez à FreshRSS à travers votre navigateur Web et suivez les instructions d’installation
 	* 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`.
+6. Des paramètres de configuration avancés peuvent être vus dans [config.default.php](config.default.php) et modifiés dans `data/config.php`.
 7. Avec Apache, activer [`AllowEncodedSlashes`](https://httpd.apache.org/docs/trunk/mod/core.html#allowencodedslashes) pour une meilleure compatibilité avec les clients mobiles.
 
 Plus d’informations sur l’installation et la configuration serveur peuvent être trouvées dans [notre documentation](https://freshrss.github.io/FreshRSS/fr/users/01_Installation.md).
 
 ## Installation automatisée
-* [Docker](./Docker/)
+* [![Docker](https://www.docker.com/sites/default/files/horizontal.png)](./Docker/)
+* [![YunoHost](https://install-app.yunohost.org/install-with-yunohost.png)](https://install-app.yunohost.org/?app=freshrss)
 * [![Cloudron](https://cloudron.io/img/button.svg)](https://cloudron.io/button.html?app=org.freshrss.cloudronapp)
-* [![DP deploy](https://raw.githubusercontent.com/DFabric/DPlatform-ShellCore/gh-pages/img/deploy.png)](https://dfabric.github.io/DPlatform-ShellCore)
-* [YunoHost](https://github.com/YunoHost-Apps/freshrss_ynh)
 
 ## Exemple d’installation complète sur Linux Debian/Ubuntu
 ```sh

+ 4 - 5
README.md

@@ -37,7 +37,7 @@ We are a friendly community.
 * A web server: Apache2 (recommended), nginx, lighttpd (not tested on others)
 * PHP 5.3.8+ (PHP 5.4+ recommended, and PHP 5.5+ for performance, and PHP 7 for even higher performance)
 	* Required extensions: [cURL](https://secure.php.net/curl), [DOM](https://secure.php.net/dom), [XML](https://secure.php.net/xml), [session](https://secure.php.net/session), [ctype](https://secure.php.net/ctype), and [PDO_MySQL](https://secure.php.net/pdo-mysql) or [PDO_SQLite](https://secure.php.net/pdo-sqlite) or [PDO_PGSQL](https://secure.php.net/pdo-pgsql)
-	* Recommended extensions: [JSON](https://secure.php.net/json), [GMP](https://secure.php.net/gmp) (for API access on platforms < 64 bits), [IDN](https://secure.php.net/intl.idn) (for Internationalized Domain Names), [mbstring](https://secure.php.net/mbstring) and/or [iconv](https://secure.php.net/iconv) (for charset conversion), [ZIP](https://secure.php.net/zip) (for import/export), [zlib](https://secure.php.net/zlib) (for compressed feeds)
+	* Recommended extensions: [JSON](https://secure.php.net/json), [GMP](https://secure.php.net/gmp) (for API access on platforms < 64 bits), [IDN](https://secure.php.net/intl.idn) (for Internationalized Domain Names), [mbstring](https://secure.php.net/mbstring) (for Unicode strings), [iconv](https://secure.php.net/iconv) (for charset conversion), [ZIP](https://secure.php.net/zip) (for import/export), [zlib](https://secure.php.net/zlib) (for compressed feeds)
 * MySQL 5.5.3+ (recommended), or SQLite 3.7.4+, or PostgreSQL 9.2+
 * A recent browser like Firefox / IceCat, Internet Explorer 11 / Edge, Chromium / Chrome, Opera, Safari.
 	* Works on mobile
@@ -59,11 +59,10 @@ We are a friendly community.
 
 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
-* [Docker](./Docker/)
+## Automated install 
+* [![Docker](https://www.docker.com/sites/default/files/horizontal.png)](./Docker/)
+* [![YunoHost](https://install-app.yunohost.org/install-with-yunohost.png)](https://install-app.yunohost.org/?app=freshrss)
 * [![Cloudron](https://cloudron.io/img/button.svg)](https://cloudron.io/button.html?app=org.freshrss.cloudronapp)
-* [![DP deploy](https://raw.githubusercontent.com/DFabric/DPlatform-ShellCore/gh-pages/img/deploy.png)](https://dfabric.github.io/DPlatform-ShellCore)
-* [YunoHost](https://github.com/YunoHost-Apps/freshrss_ynh)
 
 ## Example of full installation on Linux Debian/Ubuntu
 ```sh

+ 10 - 10
app/Controllers/entryController.php

@@ -40,6 +40,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController {
 		$get = Minz_Request::param('get');
 		$next_get = Minz_Request::param('nextGet', $get);
 		$id_max = Minz_Request::param('idMax', 0);
+		$is_read = (bool)(Minz_Request::param('is_read', true));
 		FreshRSS_Context::$search = new FreshRSS_BooleanSearch(Minz_Request::param('search', ''));
 
 		FreshRSS_Context::$state = Minz_Request::param('state', 0);
@@ -63,39 +64,38 @@ class FreshRSS_entry_Controller extends Minz_ActionController {
 
 			if (!$get) {
 				// No get? Mark all entries as read (from $id_max)
-				$entryDAO->markReadEntries($id_max);
+				$entryDAO->markReadEntries($id_max, $is_read);
 			} else {
 				$type_get = $get[0];
 				$get = substr($get, 2);
 				switch($type_get) {
 				case 'c':
-					$entryDAO->markReadCat($get, $id_max, FreshRSS_Context::$search, FreshRSS_Context::$state);
+					$entryDAO->markReadCat($get, $id_max, FreshRSS_Context::$search, FreshRSS_Context::$state, $is_read);
 					break;
 				case 'f':
-					$entryDAO->markReadFeed($get, $id_max, FreshRSS_Context::$search, FreshRSS_Context::$state);
+					$entryDAO->markReadFeed($get, $id_max, FreshRSS_Context::$search, FreshRSS_Context::$state, $is_read);
 					break;
 				case 's':
-					$entryDAO->markReadEntries($id_max, true, 0, FreshRSS_Context::$search);
+					$entryDAO->markReadEntries($id_max, true, 0, FreshRSS_Context::$search, FreshRSS_Context::$state, $is_read);
 					break;
 				case 'a':
-					$entryDAO->markReadEntries($id_max, false, 0, FreshRSS_Context::$search, FreshRSS_Context::$state);
+					$entryDAO->markReadEntries($id_max, false, 0, FreshRSS_Context::$search, FreshRSS_Context::$state, $is_read);
 					break;
 				}
 
 				if ($next_get !== 'a') {
 					// Redirect to the correct page (category, feed or starred)
-					// Not "a" because it is the default value if nothing is
-					// given.
+					// Not "a" because it is the default value if nothing is given.
 					$params['get'] = $next_get;
 				}
 			}
 		} else {
-			$is_read = (bool)(Minz_Request::param('is_read', true));
 			$entryDAO->markRead($id, $is_read);
 		}
 
 		if (!$this->ajax) {
-			Minz_Request::good(_t('feedback.sub.feed.marked_read'), array(
+			Minz_Request::good(_t($is_read ? 'feedback.sub.articles.marked_read' : 'feedback.sub.articles.marked_unread'),
+			array(
 				'c' => 'index',
 				'a' => 'index',
 				'params' => $params,
@@ -186,7 +186,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController {
 				$nb = $entryDAO->cleanOldEntries($feed->id(), $date_min, $feed_history);
 				if ($nb > 0) {
 					$nb_total += $nb;
-					Minz_Log::debug($nb . ' old entries cleaned in feed [' . $feed->url() . ']');
+					Minz_Log::debug($nb . ' old entries cleaned in feed [' . $feed->url(false) . ']');
 				}
 			}
 		}

+ 11 - 12
app/Controllers/feedController.php

@@ -295,12 +295,12 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
 				if ($feed->lastUpdate() + 10 >= $mtime) {
 					continue;	//Nothing newer from other users
 				}
-				//Minz_Log::debug($feed->url() . ' was updated at ' . date('c', $mtime) . ' by another user');
+				//Minz_Log::debug($feed->url(false) . ' was updated at ' . date('c', $mtime) . ' by another user');
 				//Will take advantage of the newer cache
 			}
 
 			if (!$feed->lock()) {
-				Minz_Log::notice('Feed already being actualized: ' . $feed->url());
+				Minz_Log::notice('Feed already being actualized: ' . $feed->url(false));
 				continue;
 			}
 
@@ -351,7 +351,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
 							//This entry already exists and is unchanged. TODO: Remove the test with the zero'ed hash in FreshRSS v1.3
 							$oldGuids[] = $entry->guid();
 						} else {	//This entry already exists but has been updated
-							//Minz_Log::debug('Entry with GUID `' . $entry->guid() . '` updated in feed ' . $feed->url() .
+							//Minz_Log::debug('Entry with GUID `' . $entry->guid() . '` updated in feed ' . $feed->url(false) .
 								//', old hash ' . $existingHash . ', new hash ' . $entry->hash());
 							$mark_updated_article_unread = $feed->attributes('mark_updated_article_unread') !== null ? (
 									$feed->attributes('mark_updated_article_unread')
@@ -413,7 +413,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
 				$entryDAO->updateLastSeen($feed->id(), $oldGuids, $mtime);
 			}
 
-			if ($feed_history >= 0 && rand(0, 30) === 1) {
+			if ($feed_history >= 0 && mt_rand(0, 30) === 1) {
 				// TODO: move this function in web cron when available (see entry::purge)
 				// Remove old entries once in 30.
 				if (!$entryDAO->inTransaction()) {
@@ -425,8 +425,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
 				                                max($feed_history, count($entries) + 10));
 				if ($nb > 0) {
 					$needFeedCacheRefresh = true;
-					Minz_Log::debug($nb . ' old entries cleaned in feed [' .
-					                $feed->url() . ']');
+					Minz_Log::debug($nb . ' old entries cleaned in feed [' . $feed->url(false) . ']');
 				}
 			}
 
@@ -442,25 +441,25 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
 				if ($feed->selfUrl() !== $url) {	//https://code.google.com/p/pubsubhubbub/wiki/MovingFeedsOrChangingHubs
 					$selfUrl = checkUrl($feed->selfUrl());
 					if ($selfUrl) {
-						Minz_Log::debug('PubSubHubbub unsubscribe ' . $feed->url());
+						Minz_Log::debug('PubSubHubbub unsubscribe ' . $feed->url(false));
 						if (!$feed->pubSubHubbubSubscribe(false)) {	//Unsubscribe
-							Minz_Log::warning('Error while PubSubHubbub unsubscribing from ' . $feed->url());
+							Minz_Log::warning('Error while PubSubHubbub unsubscribing from ' . $feed->url(false));
 						}
 						$feed->_url($selfUrl, false);
-						Minz_Log::notice('Feed ' . $url . ' canonical address moved to ' . $feed->url());
+						Minz_Log::notice('Feed ' . $url . ' canonical address moved to ' . $feed->url(false));
 						$feedDAO->updateFeed($feed->id(), array('url' => $feed->url()));
 					}
 				}
 			} elseif ($feed->url() !== $url) {	// HTTP 301 Moved Permanently
-				Minz_Log::notice('Feed ' . $url . ' moved permanently to ' . $feed->url());
+				Minz_Log::notice('Feed ' . $url . ' moved permanently to ' . $feed->url(false));
 				$feedDAO->updateFeed($feed->id(), array('url' => $feed->url()));
 			}
 
 			$feed->faviconPrepare();
 			if ($pubsubhubbubEnabledGeneral && $feed->pubSubHubbubPrepare()) {
-				Minz_Log::notice('PubSubHubbub subscribe ' . $feed->url());
+				Minz_Log::notice('PubSubHubbub subscribe ' . $feed->url(false));
 				if (!$feed->pubSubHubbubSubscribe(true)) {	//Subscribe
-					Minz_Log::warning('Error while PubSubHubbub subscribing to ' . $feed->url());
+					Minz_Log::warning('Error while PubSubHubbub subscribing to ' . $feed->url(false));
 				}
 			}
 			$feed->unlock();

+ 1 - 1
app/Controllers/indexController.php

@@ -139,7 +139,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 		}
 
 		// No layout for RSS output.
-		$this->view->url = empty($_SERVER['QUERY_STRING']) ? '' : '?' . $_SERVER['QUERY_STRING'];
+		$this->view->url = PUBLIC_TO_INDEX_PATH . '/' . (empty($_SERVER['QUERY_STRING']) ? '' : '?' . $_SERVER['QUERY_STRING']);
 		$this->view->rss_title = FreshRSS_Context::$name . ' | ' . Minz_View::title();
 		$this->view->_useLayout(false);
 		header('Content-Type: application/rss+xml; charset=utf-8');

+ 2 - 2
app/Controllers/javascriptController.php

@@ -47,8 +47,8 @@ class FreshRSS_javascript_Controller extends Minz_ActionController {
 		$this->view->salt1 = sprintf('$2a$%02d$', FreshRSS_user_Controller::BCRYPT_COST);
 		$alphabet = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
 		for ($i = 22; $i > 0; $i--) {
-			$this->view->salt1 .= $alphabet[rand(0, 63)];
+			$this->view->salt1 .= $alphabet[mt_rand(0, 63)];
 		}
-		$this->view->nonce = sha1(rand());
+		$this->view->nonce = sha1(mt_rand());
 	}
 }

+ 11 - 0
app/Controllers/userController.php

@@ -230,6 +230,17 @@ class FreshRSS_user_Controller extends Minz_ActionController {
 			$_POST['new_user_passwordPlain'] = '';
 			invalidateHttpCache();
 
+			// If the user has admin access, it means he's already logged in
+			// and we don't want to login with the new account. Otherwise, the
+			// user just created its account himself so he probably wants to
+			// get started immediately.
+			if ($ok && !FreshRSS_Auth::hasAccess('admin')) {
+				$user_conf = get_user_configuration($new_user_name);
+				Minz_Session::_param('currentUser', $new_user_name);
+				Minz_Session::_param('passwordHash', $user_conf->passwordHash);
+				FreshRSS_Auth::giveAccess();
+			}
+
 			$notif = array(
 				'type' => $ok ? 'good' : 'bad',
 				'content' => _t('feedback.user.created' . (!$ok ? '.error' : ''), $new_user_name)

+ 1 - 1
app/FreshRSS.php

@@ -66,7 +66,7 @@ class FreshRSS extends Minz_FrontController {
 				403,
 				array('error' => array(
 					_t('feedback.access.denied'),
-					' [HTTP_REFERER=' . htmlspecialchars($http_referer) . ']'
+					' [HTTP_REFERER=' . htmlspecialchars($http_referer, ENT_NOQUOTES, 'UTF-8') . ']'
 				))
 			);
 		}

+ 1 - 1
app/Models/Category.php

@@ -68,7 +68,7 @@ class FreshRSS_Category extends Minz_Model {
 		$this->id = $value;
 	}
 	public function _name($value) {
-		$this->name = substr(trim($value), 0, 255);
+		$this->name = mb_strcut(trim($value), 0, 255, 'UTF-8');
 	}
 	public function _feeds($values) {
 		if (!is_array($values)) {

+ 1 - 1
app/Models/CategoryDAO.php

@@ -9,7 +9,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable
 		$stm = $this->bd->prepare($sql);
 
 		$values = array(
-			substr($valuesTmp['name'], 0, 255),
+			mb_strcut($valuesTmp['name'], 0, 255, 'UTF-8'),
 		);
 
 		if ($stm && $stm->execute($values)) {

+ 3 - 2
app/Models/Entry.php

@@ -31,6 +31,7 @@ class FreshRSS_Entry extends Minz_Model {
 		$this->_isRead($is_read);
 		$this->_isFavorite($is_favorite);
 		$this->_feedId($feedId);
+		$tags = mb_strcut($tags, 0, 1023, 'UTF-8');
 		$this->_tags(preg_split('/[\s#]/', $tags));
 		$this->_guid($guid);
 	}
@@ -123,11 +124,11 @@ class FreshRSS_Entry extends Minz_Model {
 	}
 	public function _title($value) {
 		$this->hash = null;
-		$this->title = $value;
+		$this->title = mb_strcut($value, 0, 255, 'UTF-8');
 	}
 	public function _author($value) {
 		$this->hash = null;
-		$this->author = $value;
+		$this->author = mb_strcut($value, 0, 255, 'UTF-8');
 	}
 	public function _content($value) {
 		$this->hash = null;

+ 20 - 18
app/Models/EntryDAO.php

@@ -160,9 +160,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 			$valuesTmp['guid'] = substr($valuesTmp['guid'], 0, 760);
 			$valuesTmp['guid'] = safe_ascii($valuesTmp['guid']);
 			$this->addEntryPrepared->bindParam(':guid', $valuesTmp['guid']);
-			$valuesTmp['title'] = substr($valuesTmp['title'], 0, 255);
+			$valuesTmp['title'] = mb_strcut($valuesTmp['title'], 0, 255, 'UTF-8');
 			$this->addEntryPrepared->bindParam(':title', $valuesTmp['title']);
-			$valuesTmp['author'] = substr($valuesTmp['author'], 0, 255);
+			$valuesTmp['author'] = mb_strcut($valuesTmp['author'], 0, 255, 'UTF-8');
 			$this->addEntryPrepared->bindParam(':author', $valuesTmp['author']);
 			$this->addEntryPrepared->bindParam(':content', $valuesTmp['content']);
 			$valuesTmp['link'] = substr($valuesTmp['link'], 0, 1023);
@@ -176,7 +176,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 			$valuesTmp['is_favorite'] = $valuesTmp['is_favorite'] ? 1 : 0;
 			$this->addEntryPrepared->bindParam(':is_favorite', $valuesTmp['is_favorite'], PDO::PARAM_INT);
 			$this->addEntryPrepared->bindParam(':id_feed', $valuesTmp['id_feed'], PDO::PARAM_INT);
-			$valuesTmp['tags'] = substr($valuesTmp['tags'], 0, 1023);
+			$valuesTmp['tags'] = mb_strcut($valuesTmp['tags'], 0, 1023, 'UTF-8');
 			$this->addEntryPrepared->bindParam(':tags', $valuesTmp['tags']);
 
 			if ($this->hasNativeHex()) {
@@ -243,9 +243,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 
 		$valuesTmp['guid'] = substr($valuesTmp['guid'], 0, 760);
 		$this->updateEntryPrepared->bindParam(':guid', $valuesTmp['guid']);
-		$valuesTmp['title'] = substr($valuesTmp['title'], 0, 255);
+		$valuesTmp['title'] = mb_strcut($valuesTmp['title'], 0, 255, 'UTF-8');
 		$this->updateEntryPrepared->bindParam(':title', $valuesTmp['title']);
-		$valuesTmp['author'] = substr($valuesTmp['author'], 0, 255);
+		$valuesTmp['author'] = mb_strcut($valuesTmp['author'], 0, 255, 'UTF-8');
 		$this->updateEntryPrepared->bindParam(':author', $valuesTmp['author']);
 		$this->updateEntryPrepared->bindParam(':content', $valuesTmp['content']);
 		$valuesTmp['link'] = substr($valuesTmp['link'], 0, 1023);
@@ -258,7 +258,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 			$this->updateEntryPrepared->bindValue(':is_read', $valuesTmp['is_read'] ? 1 : 0, PDO::PARAM_INT);
 		}
 		$this->updateEntryPrepared->bindParam(':id_feed', $valuesTmp['id_feed'], PDO::PARAM_INT);
-		$valuesTmp['tags'] = substr($valuesTmp['tags'], 0, 1023);
+		$valuesTmp['tags'] = mb_strcut($valuesTmp['tags'], 0, 1023, 'UTF-8');
 		$this->updateEntryPrepared->bindParam(':tags', $valuesTmp['tags']);
 
 		if ($this->hasNativeHex()) {
@@ -437,7 +437,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 	 * @param integer $priorityMin
 	 * @return integer affected rows
 	 */
-	public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0, $filters = null, $state = 0) {
+	public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0, $filters = null, $state = 0, $is_read = true) {
 		FreshRSS_UserDAO::touch();
 		if ($idMax == 0) {
 			$idMax = time() . '000000';
@@ -445,14 +445,14 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 		}
 
 		$sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id '
-			 . 'SET e.is_read=1 '
-			 . 'WHERE e.is_read=0 AND e.id <= ?';
+			 . 'SET e.is_read=? '
+			 . 'WHERE e.is_read <> ? AND e.id <= ?';
 		if ($onlyFavorites) {
 			$sql .= ' AND e.is_favorite=1';
 		} elseif ($priorityMin >= 0) {
 			$sql .= ' AND f.priority > ' . intval($priorityMin);
 		}
-		$values = array($idMax);
+		$values = array($is_read ? 1 : 0, $is_read ? 1 : 0, $idMax);
 
 		list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filters, $state);
 
@@ -480,7 +480,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 	 * @param integer $idMax fail safe article ID
 	 * @return integer affected rows
 	 */
-	public function markReadCat($id, $idMax = 0, $filters = null, $state = 0) {
+	public function markReadCat($id, $idMax = 0, $filters = null, $state = 0, $is_read = true) {
 		FreshRSS_UserDAO::touch();
 		if ($idMax == 0) {
 			$idMax = time() . '000000';
@@ -488,9 +488,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 		}
 
 		$sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id '
-			 . 'SET e.is_read=1 '
-			 . 'WHERE f.category=? AND e.is_read=0 AND e.id <= ?';
-		$values = array($id, $idMax);
+			 . 'SET e.is_read=? '
+			 . 'WHERE f.category=? AND e.is_read <> ? AND e.id <= ?';
+		$values = array($is_read ? 1 : 0, $id, $is_read ? 1 : 0, $idMax);
 
 		list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filters, $state);
 
@@ -518,7 +518,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 	 * @param integer $idMax fail safe article ID
 	 * @return integer affected rows
 	 */
-	public function markReadFeed($id_feed, $idMax = 0, $filters = null, $state = 0) {
+	public function markReadFeed($id_feed, $idMax = 0, $filters = null, $state = 0, $is_read = true) {
 		FreshRSS_UserDAO::touch();
 		if ($idMax == 0) {
 			$idMax = time() . '000000';
@@ -527,9 +527,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 		$this->bd->beginTransaction();
 
 		$sql = 'UPDATE `' . $this->prefix . 'entry` '
-			 . 'SET is_read=1 '
-			 . 'WHERE id_feed=? AND is_read=0 AND id <= ?';
-		$values = array($id_feed, $idMax);
+			 . 'SET is_read=? '
+			 . 'WHERE id_feed=? AND is_read <> ? AND id <= ?';
+		$values = array($is_read ? 1 : 0, $id_feed, $is_read ? 1 : 0, $idMax);
 
 		list($searchValues, $search) = $this->sqlListEntriesWhere('', $filters, $state);
 
@@ -909,6 +909,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 		$stm = $this->bd->prepare($sql);
 		$stm->execute();
 		$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
+		rsort($res);
 		$all = empty($res[0]) ? 0 : $res[0];
 		$unread = empty($res[1]) ? 0 : $res[1];
 		return array('all' => $all, 'unread' => $unread, 'read' => $all - $unread);
@@ -963,6 +964,7 @@ SQL;
 		$stm = $this->bd->prepare($sql);
 		$stm->execute(array(':priority_normal' => FreshRSS_Feed::PRIORITY_NORMAL));
 		$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
+		rsort($res);
 		$all = empty($res[0]) ? 0 : $res[0];
 		$unread = empty($res[1]) ? 0 : $res[1];
 		return array('all' => $all, 'unread' => $unread, 'read' => $all - $unread);

+ 10 - 7
app/Models/EntryDAOSQLite.php

@@ -101,6 +101,7 @@ DROP TABLE IF EXISTS `tmp`;
 	 * @return integer affected rows
 	 */
 	public function markRead($ids, $is_read = true) {
+		FreshRSS_UserDAO::touch();
 		if (is_array($ids)) {	//Many IDs at once (used by API)
 			if (true) {	//Speed heuristics	//TODO: Not implemented yet for SQLite (so always call IDs one by one)
 				$affected = 0;
@@ -159,19 +160,20 @@ DROP TABLE IF EXISTS `tmp`;
 	 * @param integer $priorityMin
 	 * @return integer affected rows
 	 */
-	public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0, $filters = null, $state = 0) {
+	public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0, $filters = null, $state = 0, $is_read = true) {
+		FreshRSS_UserDAO::touch();
 		if ($idMax == 0) {
 			$idMax = time() . '000000';
 			Minz_Log::debug('Calling markReadEntries(0) is deprecated!');
 		}
 
-		$sql = 'UPDATE `' . $this->prefix . 'entry` SET is_read=1 WHERE is_read=0 AND id <= ?';
+		$sql = 'UPDATE `' . $this->prefix . 'entry` SET is_read = ? WHERE is_read <> ? AND id <= ?';
 		if ($onlyFavorites) {
 			$sql .= ' AND is_favorite=1';
 		} elseif ($priorityMin >= 0) {
 			$sql .= ' AND id_feed IN (SELECT f.id FROM `' . $this->prefix . 'feed` f WHERE f.priority > ' . intval($priorityMin) . ')';
 		}
-		$values = array($idMax);
+		$values = array($is_read ? 1 : 0, $is_read ? 1 : 0, $idMax);
 
 		list($searchValues, $search) = $this->sqlListEntriesWhere('', $filters, $state);
 
@@ -199,17 +201,18 @@ DROP TABLE IF EXISTS `tmp`;
 	 * @param integer $idMax fail safe article ID
 	 * @return integer affected rows
 	 */
-	public function markReadCat($id, $idMax = 0, $filters = null, $state = 0) {
+	public function markReadCat($id, $idMax = 0, $filters = null, $state = 0, $is_read = true) {
+		FreshRSS_UserDAO::touch();
 		if ($idMax == 0) {
 			$idMax = time() . '000000';
 			Minz_Log::debug('Calling markReadCat(0) is deprecated!');
 		}
 
 		$sql = 'UPDATE `' . $this->prefix . 'entry` '
-			 . 'SET is_read=1 '
-			 . 'WHERE is_read=0 AND id <= ? AND '
+			 . 'SET is_read = ? '
+			 . 'WHERE is_read <> ? AND id <= ? AND '
 			 . 'id_feed IN (SELECT f.id FROM `' . $this->prefix . 'feed` f WHERE f.category=?)';
-		$values = array($idMax, $id);
+		$values = array($is_read ? 1 : 0, $is_read ? 1 : 0, $idMax, $id);
 
 		list($searchValues, $search) = $this->sqlListEntriesWhere('', $filters, $state);
 

+ 11 - 4
app/Models/Feed.php

@@ -59,8 +59,8 @@ class FreshRSS_Feed extends Minz_Model {
 		return $this->hash;
 	}
 
-	public function url() {
-		return $this->url;
+	public function url($includeCredentials = true) {
+		return $includeCredentials ? $this->url : SimplePie_Misc::url_remove_credentials($this->url);
 	}
 	public function selfUrl() {
 		return $this->selfUrl;
@@ -341,7 +341,7 @@ class FreshRSS_Feed extends Minz_Model {
 
 		foreach ($feed->get_items() as $item) {
 			$title = html_only_entity_decode(strip_tags($item->get_title()));
-			$author = $item->get_author();
+			$authors = $item->get_authors();
 			$link = $item->get_permalink();
 			$date = @strtotime($item->get_date());
 
@@ -409,12 +409,19 @@ class FreshRSS_Feed extends Minz_Model {
 			$guid = $item->get_id(false, false);
 			$hasUniqueGuids &= empty($guids['_' . $guid]);
 			$guids['_' . $guid] = true;
+			$author_names = '';
+			if (is_array($authors)) {
+				foreach ($authors as $author) {
+					$author_names .= html_only_entity_decode(strip_tags($author->name == '' ? $author->email : $author->name)) . ', ';
+				}
+			}
+			$author_names = substr($author_names, 0, -2);
 
 			$entry = new FreshRSS_Entry(
 				$this->id(),
 				$guid,
 				$title === null ? '' : $title,
-				$author === null ? '' : html_only_entity_decode(strip_tags($author->name == null ? $author->email : $author->name)),
+				$author_names,
 				$content === null ? '' : $content,
 				$link === null ? '' : $link,
 				$date ? $date : time()

+ 2 - 2
app/Models/FeedDAO.php

@@ -55,9 +55,9 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 		$values = array(
 			substr($valuesTmp['url'], 0, 511),
 			$valuesTmp['category'],
-			substr($valuesTmp['name'], 0, 255),
+			mb_strcut($valuesTmp['name'], 0, 255, 'UTF-8'),
 			substr($valuesTmp['website'], 0, 255),
-			substr($valuesTmp['description'], 0, 1023),
+			mb_strcut($valuesTmp['description'], 0, 1023, 'UTF-8'),
 			$valuesTmp['lastUpdate'],
 			base64_encode($valuesTmp['httpAuth']),
 			FreshRSS_Feed::KEEP_HISTORY_DEFAULT,

+ 5 - 1
app/i18n/cz/admin.php

@@ -63,9 +63,13 @@ return array(
 		),
 		'files' => 'Instalace souborů',
 		'json' => array(
-			'nok' => 'Nemáte JSON (balíček php5-json).',
+			'nok' => 'Nemáte JSON (balíček php-json).',
 			'ok' => 'Máte rozšíření JSON.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'Nemáte framework Minz.',
 			'ok' => 'Máte framework Minz.',

+ 4 - 1
app/i18n/cz/feedback.php

@@ -52,6 +52,10 @@ return array(
 	),
 	'sub' => array(
 		'actualize' => 'Aktualizovat',
+		'articles' => array(
+			'marked_read' => 'The selected articles have been marked as read.',	//TODO
+			'marked_unread' => 'The articles have been marked as unread.',	//TODO
+		),
 		'category' => array(
 			'created' => 'Kategorie %s byla vytvořena.',
 			'deleted' => 'Kategorie byla smazána.',
@@ -74,7 +78,6 @@ return array(
 			'error' => 'Kanál nelze aktualizovat',
 			'internal_problem' => 'RSS kanál nelze přidat. Pro detaily <a href="%s">zkontrolujte logy FreshRSS</a>.', // @todo
 			'invalid_url' => 'URL <em>%s</em> není platné',
-			'marked_read' => 'Kanály byly označeny jako přečtené',
 			'n_actualized' => '%d kanálů bylo aktualizováno',
 			'n_entries_deleted' => '%d článků bylo smazáno',
 			'no_refresh' => 'Nelze obnovit žádné kanály…',

+ 1 - 0
app/i18n/cz/gen.php

@@ -168,6 +168,7 @@ return array(
 		'g+' => 'Google+',
 		'gnusocial' => 'GNU social',
 		'jdh' => 'Journal du hacker',
+		'linkedin' => 'LinkedIn',
 		'mastodon' => 'Mastodon',
 		'movim' => 'Movim',
 		'pocket' => 'Pocket',

+ 1 - 0
app/i18n/cz/index.php

@@ -40,6 +40,7 @@ return array(
 		'mark_all_read' => 'Označit vše jako přečtené',
 		'mark_cat_read' => 'Označit kategorii jako přečtenou',
 		'mark_feed_read' => 'Označit kanál jako přečtený',
+		'mark_selection_unread' => 'Mark selection as unread',	//TODO
 		'newer_first' => 'Nové nejdříve',
 		'non-starred' => 'Zobrazit vše vyjma oblíbených',
 		'normal_view' => 'Normální',

+ 4 - 0
app/i18n/cz/install.php

@@ -68,6 +68,10 @@ return array(
 			'nok' => 'Pro parsování JSON chybí doporučená knihovna.',
 			'ok' => 'Máte doporučenou knihovnu pro parsování JSON.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'Nemáte framework Minz.',
 			'ok' => 'Máte framework Minz.',

+ 5 - 1
app/i18n/de/admin.php

@@ -63,9 +63,13 @@ return array(
 		),
 		'files' => 'Datei-Installation',
 		'json' => array(
-			'nok' => 'Ihnen fehlt die JSON-Erweiterung (Paket php5-json).',
+			'nok' => 'Ihnen fehlt die JSON-Erweiterung (Paket php-json).',
 			'ok' => 'Sie haben die JSON-Erweiterung.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'Ihnen fehlt das Minz-Framework.',
 			'ok' => 'Sie haben das Minz-Framework.',

+ 4 - 1
app/i18n/de/feedback.php

@@ -52,6 +52,10 @@ return array(
 	),
 	'sub' => array(
 		'actualize' => 'Aktualisieren',
+		'articles' => array(
+			'marked_read' => 'The selected articles have been marked as read.',	//TODO
+			'marked_unread' => 'The articles have been marked as unread.',	//TODO
+		),
 		'category' => array(
 			'created' => 'Die Kategorie %s ist erstellt worden.',
 			'deleted' => 'Die Kategorie ist gelöscht worden.',
@@ -74,7 +78,6 @@ return array(
 			'error' => 'Der Feed kann nicht aktualisiert werden',
 			'internal_problem' => 'Der RSS-Feed konnte nicht hinzugefügt werden. Für Details <a href="%s">prüfen Sie die FreshRSS-Protokolle</a>.', // @todo
 			'invalid_url' => 'Die URL <em>%s</em> ist ungültig',
-			'marked_read' => 'Die Feeds sind als gelesen markiert worden',
 			'n_actualized' => 'Die %d Feeds sind aktualisiert worden',
 			'n_entries_deleted' => 'Die %d Artikel sind gelöscht worden',
 			'no_refresh' => 'Es gibt keinen Feed zum Aktualisieren…',

+ 2 - 1
app/i18n/de/gen.php

@@ -59,7 +59,7 @@ return array(
 		'april' => 'April',
 		'aug' => 'Aug',
 		'august' => 'August',
-		'before_yesterday' => 'Vor vorgestern',
+		'before_yesterday' => 'Ältere Beiträge',
 		'dec' => 'Dez',
 		'december' => 'Dezember',
 		'feb' => 'Feb',
@@ -167,6 +167,7 @@ return array(
 		'g+' => 'Google+',
 		'gnusocial' => 'GNU social',
 		'jdh' => 'Journal du hacker',
+		'linkedin' => 'LinkedIn',
 		'mastodon' => 'Mastodon',
 		'movim' => 'Movim',
 		'pocket' => 'Pocket',

+ 1 - 0
app/i18n/de/index.php

@@ -40,6 +40,7 @@ return array(
 		'mark_all_read' => 'Alle als gelesen markieren',
 		'mark_cat_read' => 'Kategorie als gelesen markieren',
 		'mark_feed_read' => 'Feed als gelesen markieren',
+		'mark_selection_unread' => 'Mark selection as unread',	//TODO
 		'newer_first' => 'Neuere zuerst',
 		'non-starred' => 'Alle außer Favoriten zeigen',
 		'normal_view' => 'Normale Ansicht',

+ 4 - 0
app/i18n/de/install.php

@@ -68,6 +68,10 @@ return array(
 			'nok' => 'Ihnen fehlt eine empfohlene Bibliothek um JSON zu parsen.',
 			'ok' => 'Sie haben eine empfohlene Bibliothek um JSON zu parsen.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'Ihnen fehlt das Minz-Framework.',
 			'ok' => 'Sie haben das Minz-Framework.',

+ 5 - 1
app/i18n/en/admin.php

@@ -63,9 +63,13 @@ return array(
 		),
 		'files' => 'File installation',
 		'json' => array(
-			'nok' => 'Cannot find JSON (php5-json package).',
+			'nok' => 'Cannot find JSON (php-json package).',
 			'ok' => 'You have JSON extension.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',
+			'ok' => 'You have the recommended library mbstring for Unicode.',
+		),
 		'minz' => array(
 			'nok' => 'Cannot find the Minz framework.',
 			'ok' => 'You have the Minz framework.',

+ 4 - 1
app/i18n/en/feedback.php

@@ -52,6 +52,10 @@ return array(
 	),
 	'sub' => array(
 		'actualize' => 'Updating',
+		'articles' => array(
+			'marked_read' => 'The selected articles have been marked as read.',
+			'marked_unread' => 'The articles have been marked as unread.',
+		),
 		'category' => array(
 			'created' => 'Category %s has been created.',
 			'deleted' => 'Category has been deleted.',
@@ -74,7 +78,6 @@ return array(
 			'error' => 'Feed cannot be updated',
 			'internal_problem' => 'The newsfeed could not be added. <a href="%s">Check FreshRSS logs</a> for details. You can try force adding by appending <code>#force_feed</code> to the URL.',
 			'invalid_url' => 'URL <em>%s</em> is invalid',
-			'marked_read' => 'Feeds have been marked as read',
 			'n_actualized' => '%d feeds have been updated',
 			'n_entries_deleted' => '%d articles have been deleted',
 			'no_refresh' => 'There is no feed to refresh…',

+ 1 - 0
app/i18n/en/gen.php

@@ -168,6 +168,7 @@ return array(
 		'gnusocial' => 'GNU social',
 		'jdh' => 'Journal du hacker',
 		'Known' => 'Known based sites',
+		'linkedin' => 'LinkedIn',
 		'mastodon' => 'Mastodon',
 		'movim' => 'Movim',
 		'pocket' => 'Pocket',

+ 1 - 0
app/i18n/en/index.php

@@ -40,6 +40,7 @@ return array(
 		'mark_all_read' => 'Mark all as read',
 		'mark_cat_read' => 'Mark category as read',
 		'mark_feed_read' => 'Mark feed as read',
+		'mark_selection_unread' => 'Mark selection as unread',
 		'newer_first' => 'Newer first',
 		'non-starred' => 'Show non-favourites',
 		'normal_view' => 'Normal view',

+ 4 - 0
app/i18n/en/install.php

@@ -68,6 +68,10 @@ return array(
 			'nok' => 'Cannot find a recommended library to parse JSON.',
 			'ok' => 'You have a recommended library to parse JSON.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'Cannot find the Minz framework.',
 			'ok' => 'You have the Minz framework.',

+ 5 - 1
app/i18n/es/admin.php

@@ -63,9 +63,13 @@ return array(
 		),
 		'files' => 'Instalación de Archivos',
 		'json' => array(
-			'nok' => 'No se ha podido localizar JSON (paquete php5-json).',
+			'nok' => 'No se ha podido localizar JSON (paquete php-json).',
 			'ok' => 'Dispones de la extensión JSON.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'No se ha podido localizar el entorno Minz.',
 			'ok' => 'Dispones del entorno Minz.',

+ 4 - 1
app/i18n/es/feedback.php

@@ -52,6 +52,10 @@ return array(
 	),
 	'sub' => array(
 		'actualize' => 'Actualización',
+		'articles' => array(
+			'marked_read' => 'The selected articles have been marked as read.',	//TODO
+			'marked_unread' => 'The articles have been marked as unread.',	//TODO
+		),
 		'category' => array(
 			'created' => 'Se ha creado la categoría %s.',
 			'deleted' => 'Se ha eliminado la categoría.',
@@ -74,7 +78,6 @@ return array(
 			'error' => 'No es posible actualizar la fuente',
 			'internal_problem' => 'No ha sido posible agregar la fuente RSS. <a href="%s">Revisa el registro de FreshRSS </a> para más información.', // @todo
 			'invalid_url' => 'La URL <em>%s</em> es inválida',
-			'marked_read' => 'Fuentes marcadas como leídas',
 			'n_actualized' => 'Se han actualiado %d fuentes',
 			'n_entries_deleted' => 'Se han eliminado %d artículos',
 			'no_refresh' => 'No hay fuente a actualizar…',

+ 1 - 0
app/i18n/es/gen.php

@@ -168,6 +168,7 @@ return array(
 		'g+' => 'Google+',
 		'gnusocial' => 'GNU social',
 		'jdh' => 'Journal du hacker',
+		'linkedin' => 'LinkedIn',
 		'mastodon' => 'Mastodon',
 		'movim' => 'Movim',
 		'pocket' => 'Pocket',

+ 1 - 0
app/i18n/es/index.php

@@ -40,6 +40,7 @@ return array(
 		'mark_all_read' => 'Marcar todo como leído',
 		'mark_cat_read' => 'Marcar categoría como leída',
 		'mark_feed_read' => 'Marcar fuente como leída',
+		'mark_selection_unread' => 'Mark selection as unread',	//TODO
 		'newer_first' => 'Nuevos primero',
 		'non-starred' => 'Mostrar todos menos los favoritos',
 		'normal_view' => 'Vista normal',

+ 4 - 0
app/i18n/es/install.php

@@ -68,6 +68,10 @@ return array(
 			'nok' => 'No se ha podido localizar la librería para procesar JSON.',
 			'ok' => 'Dispones de la librería recomendada para procesar JSON.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'No se ha podido localizar el entorno Minz.',
 			'ok' => 'Dispones del entorno Minz.',

+ 6 - 2
app/i18n/fr/admin.php

@@ -63,8 +63,12 @@ return array(
 		),
 		'files' => 'Installation des fichiers',
 		'json' => array(
-			'nok' => 'Vous ne disposez pas de JSON (paquet php5-json).',
-			'ok' => 'Vous disposez de l’extension JSON.',
+			'nok' => 'Vous ne disposez pas de l’extension recommendée JSON (paquet php-json).',
+			'ok' => 'Vous disposez de l’extension recommendée JSON.',
+		),
+		'mbstring' => array(
+			'nok' => 'Impossible de trouver la librairie recommandée mbstring pour Unicode.',
+			'ok' => 'Vouz disposez de la librairie recommandée mbstring pour Unicode.',
 		),
 		'minz' => array(
 			'nok' => 'Vous ne disposez pas de la librairie Minz.',

+ 4 - 1
app/i18n/fr/feedback.php

@@ -52,6 +52,10 @@ return array(
 	),
 	'sub' => array(
 		'actualize' => 'Actualiser',
+		'articles' => array(
+			'marked_read' => 'Les articles sélectionnés ont été marqués comme lus.',
+			'marked_unread' => 'Les articles sélectionnés ont été marqués comme non-lus.',
+		),
 		'category' => array(
 			'created' => 'La catégorie %s a été créée.',
 			'deleted' => 'La catégorie a été supprimée.',
@@ -74,7 +78,6 @@ return array(
 			'error' => 'Une erreur est survenue',
 			'internal_problem' => 'Le flux ne peut pas être ajouté. <a href="%s">Consulter les logs de FreshRSS</a> pour plus de détails. Vous pouvez essayer de forcer l’ajout par addition de <code>#force_feed</code> à l’URL.',
 			'invalid_url' => 'L’url <em>%s</em> est invalide.',
-			'marked_read' => 'Les flux ont été marqués comme lus.',
 			'n_actualized' => '%d flux ont été mis à jour.',
 			'n_entries_deleted' => '%d articles ont été supprimés.',
 			'no_refresh' => 'Il n’y a aucun flux à actualiser…',

+ 1 - 0
app/i18n/fr/gen.php

@@ -168,6 +168,7 @@ return array(
 		'g+' => 'Google+',
 		'gnusocial' => 'GNU social',
 		'jdh' => 'Journal du hacker',
+		'linkedin' => 'LinkedIn',
 		'mastodon' => 'Mastodon',
 		'movim' => 'Movim',
 		'pocket' => 'Pocket',

+ 2 - 1
app/i18n/fr/index.php

@@ -40,6 +40,7 @@ return array(
 		'mark_all_read' => 'Tout marquer comme lu',
 		'mark_cat_read' => 'Marquer la catégorie comme lue',
 		'mark_feed_read' => 'Marquer le flux comme lu',
+		'mark_selection_unread' => 'Marquer la sélection comme non-lue',
 		'newer_first' => 'Plus récents en premier',
 		'non-starred' => 'Afficher les non-favoris',
 		'normal_view' => 'Vue normale',
@@ -52,7 +53,7 @@ return array(
 		'starred' => 'Afficher les favoris',
 		'stats' => 'Statistiques',
 		'subscription' => 'Gestion des abonnements',
-		'unread' => 'Afficher les non lus',
+		'unread' => 'Afficher les non-lus',
 	),
 	'share' => 'Partager',
 	'tag' => array(

+ 6 - 2
app/i18n/fr/install.php

@@ -65,8 +65,12 @@ return array(
 			'ok' => 'Le HTTP REFERER est connu et semble correspondre à votre serveur.',
 		),
 		'json' => array(
-			'nok' => 'Impossible de trouver une librairie recommandée pour JSON.',
-			'ok' => 'Vouz disposez de la librairie recommandée pour JSON.',
+			'nok' => 'Vous ne disposez pas de l’extension recommendée JSON (paquet php-json).',
+			'ok' => 'Vous disposez de l’extension recommendée JSON.',
+		),
+		'mbstring' => array(
+			'nok' => 'Impossible de trouver la librairie recommandée mbstring pour Unicode.',
+			'ok' => 'Vouz disposez de la librairie recommandée mbstring pour Unicode.',
 		),
 		'minz' => array(
 			'nok' => 'Vous ne disposez pas de la librairie Minz.',

+ 7 - 3
app/i18n/he/admin.php

@@ -33,7 +33,7 @@ return array(
 			'ok' => 'הספרייה הנדרשת ל character type checking (ctype) מותקנת',
 		),
 		'curl' => array(
-			'nok' => 'בURL לא מותקן (php5-curl package)',
+			'nok' => 'בURL לא מותקן (php-curl package)',
 			'ok' => 'You have cURL extension.', // @todo
 		),
 		'data' => array(
@@ -63,9 +63,13 @@ return array(
 		),
 		'files' => 'File installation', // @todo
 		'json' => array(
-			'nok' => 'You lack JSON (php5-json package).', // @todo
+			'nok' => 'You lack JSON (php-json package).', // @todo
 			'ok' => 'You have JSON extension.', // @todo
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'You lack the Minz framework.', // @todo
 			'ok' => 'יש לכם את תשתית Minz',
@@ -97,7 +101,7 @@ return array(
 			'ok' => 'Permissions on users directory are good.', // @todo
 		),
 		'zip' => array(
-			'nok' => 'You lack ZIP extension (php5-zip package).', // @todo
+			'nok' => 'You lack ZIP extension (php-zip package).', // @todo
 			'ok' => 'You have ZIP extension.', // @todo
 		),
 	),

+ 4 - 1
app/i18n/he/feedback.php

@@ -53,6 +53,10 @@ return array(
 	),
 	'sub' => array(
 		'actualize' => 'מימוש',
+		'articles' => array(
+			'marked_read' => 'The selected articles have been marked as read.',	//TODO
+			'marked_unread' => 'The articles have been marked as unread.',	//TODO
+		),
 		'category' => array(
 			'created' => 'Category %s has been created.', // @todo
 			'deleted' => 'Category has been deleted.', // @todo
@@ -75,7 +79,6 @@ return array(
 			'error' => 'Feed cannot be updated', // @todo
 			'internal_problem' => 'אין אפשרות להוסיף את ההזנה. <a href="%s">בדקו את הלוגים</a> לפרטים.', // @todo
 			'invalid_url' => 'URL <em>%s</em> אינו תקין',
-			'marked_read' => 'הזנות סומנו כנקראו',
 			'n_actualized' => '%d הזנות עודכנו',
 			'n_entries_deleted' => '%d המאמרים נמחקו',
 			'no_refresh' => 'אין הזנה שניתן לרענן…',

+ 1 - 0
app/i18n/he/gen.php

@@ -168,6 +168,7 @@ return array(
 		'gnusocial' => 'GNU social',
 		'jdh' => 'Journal du hacker',
 		'Known' => 'Known based sites',
+		'linkedin' => 'LinkedIn',
 		'mastodon' => 'Mastodon',
 		'movim' => 'Movim',
 		'pocket' => 'Pocket',

+ 1 - 0
app/i18n/he/index.php

@@ -40,6 +40,7 @@ return array(
 		'mark_all_read' => 'סימון הכל כנקרא',
 		'mark_cat_read' => 'סימון קטגוריה כנקראה',
 		'mark_feed_read' => 'סימון הזנה כנקראה',
+		'mark_selection_unread' => 'Mark selection as unread',	//TODO
 		'newer_first' => 'חדשים בראש',
 		'non-starred' => 'הצגת הכל פרט למועדפים',
 		'normal_view' => 'תצוגה רגילה',

+ 9 - 1
app/i18n/he/install.php

@@ -40,7 +40,7 @@ return array(
 			'ok' => 'הספרייה הנדרשת ל character type checking (ctype) מותקנת',
 		),
 		'curl' => array(
-			'nok' => 'בURL לא מותקן (php5-curl package)',
+			'nok' => 'בURL לא מותקן (php-curl package)',
 			'ok' => 'יש לכם את גירסת %s של cURL',
 		),
 		'data' => array(
@@ -59,6 +59,14 @@ return array(
 			'nok' => 'נא לדבוק שאינך פוגעת ב HTTP REFERER שלך.',
 			'ok' => 'הHTTP REFERER ידוע ותאם לשרת שלך.',
 		),
+		'json' => array(
+			'nok' => 'Cannot find a recommended library to parse JSON.',	//TODO
+			'ok' => 'You have a recommended library to parse JSON.',	//TODO
+		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'You lack the Minz framework.', // @todo
 			'ok' => 'יש לכם את תשתית Minz',

+ 5 - 1
app/i18n/it/admin.php

@@ -63,9 +63,13 @@ return array(
 		),
 		'files' => 'Installazione files',
 		'json' => array(
-			'nok' => 'Manca il supoorto a JSON (pacchetto php5-json).',
+			'nok' => 'Manca il supoorto a JSON (pacchetto php-json).',
 			'ok' => 'Estensione JSON presente.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'Manca il framework Minz.',
 			'ok' => 'Framework Minz presente.',

+ 4 - 1
app/i18n/it/feedback.php

@@ -52,6 +52,10 @@ return array(
 	),
 	'sub' => array(
 		'actualize' => 'Aggiorna',
+		'articles' => array(
+			'marked_read' => 'The selected articles have been marked as read.',	//TODO
+			'marked_unread' => 'The articles have been marked as unread.',	//TODO
+		),
 		'category' => array(
 			'created' => 'Categoria %s creata.',
 			'deleted' => 'Categoria cancellata',
@@ -74,7 +78,6 @@ return array(
 			'error' => 'Feed non aggiornato',
 			'internal_problem' => 'RSS feed non aggiunto. <a href="%s">Verifica i logs</a> per dettagli.', // @todo
 			'invalid_url' => 'URL <em>%s</em> non valido',
-			'marked_read' => 'Feeds segnati come letti',
 			'n_actualized' => '%d feeds aggiornati',
 			'n_entries_deleted' => '%d articoli cancellati',
 			'no_refresh' => 'Nessun aggiornamento disponibile…',

+ 1 - 0
app/i18n/it/gen.php

@@ -168,6 +168,7 @@ return array(
 		'g+' => 'Google+',
 		'gnusocial' => 'GNU social',
 		'jdh' => 'Journal du hacker',
+		'linkedin' => 'LinkedIn',
 		'mastodon' => 'Mastodon',
 		'movim' => 'Movim',
 		'pocket' => 'Pocket',

+ 1 - 0
app/i18n/it/index.php

@@ -40,6 +40,7 @@ return array(
 		'mark_all_read' => 'Segna tutto come letto',
 		'mark_cat_read' => 'Segna la categoria come letta',
 		'mark_feed_read' => 'Segna il feed come letto',
+		'mark_selection_unread' => 'Mark selection as unread',	//TODO
 		'newer_first' => 'Mostra prima i recenti',
 		'non-starred' => 'Escludi preferiti',
 		'normal_view' => 'Vista elenco',

+ 4 - 0
app/i18n/it/install.php

@@ -68,6 +68,10 @@ return array(
 			'nok' => 'You lack a recommended library to parse JSON.',
 			'ok' => 'You have a recommended library to parse JSON.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'Manca il framework Minz.',
 			'ok' => 'Framework Minz presente.',

+ 8 - 4
app/i18n/kr/admin.php

@@ -63,9 +63,13 @@ return array(
 		),
 		'files' => '파일 시스템 설치 요구사항',
 		'json' => array(
-			'nok' => 'JSON 확장 기능을 찾을 수 없습니다 (php5-json 패키지).',
+			'nok' => 'JSON 확장 기능을 찾을 수 없습니다 (php-json 패키지).',
 			'ok' => 'JSON  확장 기능이 설치되어 있습니다.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'Minz 프레임워크를 찾을 수 없습니다.',
 			'ok' => 'Minz 프레임워크가 설치되어 있습니다.',
@@ -175,15 +179,15 @@ return array(
 	'user' => array(
 		'articles_and_size' => '%s 개의 글 (%s)',
 		'create' => '새 사용자 생성',
-		'delete_users' => 'Delete user', // TODO
+		'delete_users' => '사용자 삭제',
 		'language' => '언어',
 		'number' => '%d 개의 계정이 생성되었습니다',
 		'numbers' => '%d 개의 계정이 생성되었습니다',
 		'password_form' => '암호<br /><small>(웹폼 로그인 방식 사용시)</small>',
 		'password_format' => '7 글자 이상이어야 합니다',
-		'selected' => 'Selected user', // TODO
+		'selected' => '선택된 사용자',
 		'title' => '사용자 관리',
-		'update_users' => 'Update user', // TODO
+		'update_users' => '사용자 정보 변경',
 		'user_list' => '사용자 목록',
 		'username' => '사용자 이름',
 		'users' => '전체 사용자',

+ 5 - 5
app/i18n/kr/conf.php

@@ -37,12 +37,12 @@ return array(
 			'no_limit' => '제한 없음',
 			'thin' => '얇음',
 		),
-		'show_nav_buttons' => 'Show the navigation buttons',	//TODO
+		'show_nav_buttons' => '내비게이션 버튼 보이기',
 	),
 	'query' => array(
 		'_' => '사용자 쿼리',
 		'deprecated' => '이 쿼리는 더 이상 유효하지 않습니다. 해당하는 카테고리나 피드가 삭제되었습니다.',
-		'display' => 'Display user query results', // TODO
+		'display' => '사용자 쿼리 결과 표시',
 		'filter' => '적용된 필터:',
 		'get_all' => '모든 글 표시',
 		'get_category' => '"%s" 카테고리 표시',
@@ -53,7 +53,7 @@ return array(
 		'number' => '쿼리 #%d',
 		'order_asc' => '오래된 글 먼저 표시',
 		'order_desc' => '최근 글 먼저 표시',
-		'remove' => 'Remove user query', // TODO
+		'remove' => '사용자 쿼리 삭제',
 		'search' => '"%s"의 검색 결과',
 		'state_0' => '모든 글 표시',
 		'state_1' => '읽은 글 표시',
@@ -128,7 +128,7 @@ return array(
 	),
 	'sharing' => array(
 		'_' => '공유',
-		'add' => 'Add a sharing method', // TODO
+		'add' => '공유 방법 추가',
 		'blogotext' => 'Blogotext',
 		'diaspora' => 'Diaspora*',
 		'email' => '메일',
@@ -136,7 +136,7 @@ return array(
 		'g+' => 'Google+',
 		'more_information' => '자세한 정보',
 		'print' => '인쇄',
-		'remove' => 'Remove sharing method', // TODO
+		'remove' => '공유 방법 삭제',
 		'shaarli' => 'Shaarli',
 		'share_name' => '표시할 이름',
 		'share_url' => '사용할 공유 URL',

+ 7 - 4
app/i18n/kr/feedback.php

@@ -52,6 +52,10 @@ return array(
 	),
 	'sub' => array(
 		'actualize' => '피드를 가져오는 중입니다',
+		'articles' => array(
+			'marked_read' => 'The selected articles have been marked as read.',	//TODO
+			'marked_unread' => 'The articles have been marked as unread.',	//TODO
+		),
 		'category' => array(
 			'created' => '%s 카테고리가 생성되었습니다.',
 			'deleted' => '카테고리가 삭제되었습니다.',
@@ -72,9 +76,8 @@ return array(
 			'already_subscribed' => '이미 <em>%s</em> 피드를 구독 중입니다',
 			'deleted' => '피드가 삭제되었습니다',
 			'error' => '피드를 변경할 수 없습니다',
-			'internal_problem' => 'RSS 피드를 추가할 수 없습니다. 자세한 내용은 <a href="%s">FreshRSS 로그</a>를 참고하세요.', // @todo
+			'internal_problem' => 'RSS 피드를 추가할 수 없습니다. 자세한 내용은 <a href="%s">FreshRSS 로그</a>를 참고하세요.',
 			'invalid_url' => 'URL (<em>%s</em>)이 유효하지 않습니다',
-			'marked_read' => '피드가 읽음으로 표시되었습니다',
 			'n_actualized' => '%d 개의 피드에서 새 글을 가져왔습니다',
 			'n_entries_deleted' => '%d 개의 글을 삭제했습니다',
 			'no_refresh' => '새 글을 가져올 피드가 없습니다…',
@@ -102,8 +105,8 @@ return array(
 			'error' => '%s 사용자를 삭제할 수 없습니다',
 		),
 		'updated' => array(
-			'_' => 'User %s has been updated', // TODO
-			'error' => 'User %s has not been updated', // TODO
+			'_' => '사용자 %s의 정보가 변경되었습니다',
+			'error' => '사용자 %s의 정보가 변경되지 않았습니다',
 		),
 	),
 	'profile' => array(

+ 2 - 1
app/i18n/kr/gen.php

@@ -19,7 +19,7 @@ return array(
 		'see_website' => '웹사이트 열기',
 		'submit' => '설정 저장',
 		'truncate' => '모든 글 삭제',
-		'update' => 'Update', // TODO
+		'update' => '변경',
 	),
 	'auth' => array(
 		'email' => '메일 주소',
@@ -168,6 +168,7 @@ return array(
 		'g+' => 'Google+',
 		'gnusocial' => 'GNU social',
 		'jdh' => 'Journal du hacker',
+		'linkedin' => 'LinkedIn',
 		'mastodon' => 'Mastodon',
 		'movim' => 'Movim',
 		'pocket' => 'Pocket',

+ 1 - 0
app/i18n/kr/index.php

@@ -40,6 +40,7 @@ return array(
 		'mark_all_read' => '모두 읽음으로 표시',
 		'mark_cat_read' => '카테고리를 읽음으로 표시',
 		'mark_feed_read' => '피드를 읽음으로 표시',
+		'mark_selection_unread' => 'Mark selection as unread',	//TODO
 		'newer_first' => '최근 글 먼저',
 		'non-starred' => '즐겨찾기를 제외하고 표시',
 		'normal_view' => '일반 모드',

+ 5 - 1
app/i18n/kr/install.php

@@ -65,9 +65,13 @@ return array(
 			'ok' => 'HTTP REFERER가 서버와 일치하는 것을 확인했습니다.',
 		),
 		'json' => array(
-			'nok' => 'JSON 확장 기능을 찾을 수 없습니다 (php5-json 패키지).',
+			'nok' => 'JSON 확장 기능을 찾을 수 없습니다 (php-json 패키지).',
 			'ok' => 'JSON  확장 기능이 설치되어 있습니다.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'Minz 프레임워크를 찾을 수 없습니다.',
 			'ok' => 'Minz 프레임워크가 설치되어 있습니다.',

+ 2 - 2
app/i18n/kr/sub.php

@@ -44,10 +44,10 @@ return array(
 			'main_stream' => '메인 스트림에 표시하기',
 			'normal' => '피드가 속한 카테고리에만 표시하기',
 		),
-		'ssl_verify' => 'Verify SSL security',	//TODO
+		'ssl_verify' => 'SSL 유효성 검사',
 		'stats' => '통계',
 		'think_to_add' => '피드를 추가할 수 있습니다.',
-		'timeout' => 'Timeout in seconds',	//TODO
+		'timeout' => '타임아웃 (초)',
 		'title' => '제목',
 		'title_add' => 'RSS 피드 추가',
 		'ttl' => '다음 시간이 지나기 전에 새로고침 금지',

+ 5 - 1
app/i18n/nl/admin.php

@@ -63,9 +63,13 @@ return array(
 		),
 		'files' => 'Bestanden installatie',
 		'json' => array(
-			'nok' => 'U mist JSON (php5-json package).',
+			'nok' => 'U mist JSON (php-json package).',
 			'ok' => 'U hebt JSON uitbreiding.',
 		),
+		'mbstring' => array(
+			'nok' => 'De voor Unicode aanbevolen bibliotheek mbstring kan niet worden gevonden.',
+			'ok' => 'De voor Unicode aanbevolen bibliotheek mbstring is gevonden.',
+		),
 		'minz' => array(
 			'nok' => 'U mist Minz framework.',
 			'ok' => 'U hebt Minz framework.',

+ 4 - 1
app/i18n/nl/feedback.php

@@ -52,6 +52,10 @@ return array(
 	),
 	'sub' => array(
 		'actualize' => 'Actualiseren',
+		'articles' => array(
+			'marked_read' => 'De geselecteerde artikelen zijn als gelezen gemarkeerd.',
+			'marked_unread' => 'De geselecteerde artikelen zijn als ongelezen gemarkeerd.',
+		),
 		'category' => array(
 			'created' => 'Categorie %s is gemaakt.',
 			'deleted' => 'Categorie is verwijderd.',
@@ -74,7 +78,6 @@ return array(
 			'error' => 'Feed kan niet worden vernieuwd',
 			'internal_problem' => 'De feed kon niet worden toegevoegd. <a href="%s">Controleer de FreshRSS-logbestanden</a> voor details. Toevoegen forceren kan worden geprobeerd door <code>#force_feed</code> aan de URL toe te voegen.',
 			'invalid_url' => 'URL <em>%s</em> is ongeldig',
-			'marked_read' => 'Feeds zijn gemarkeerd als gelezen',
 			'n_actualized' => '%d feeds zijn vernieuwd',
 			'n_entries_deleted' => '%d artikelen zijn verwijderd',
 			'no_refresh' => 'Er is geen feed om te vernieuwen…',

+ 1 - 0
app/i18n/nl/gen.php

@@ -168,6 +168,7 @@ return array(
 		'g+' => 'Google+',
 		'gnusocial' => 'GNU social',
 		'jdh' => 'Journal du hacker',
+		'linkedin' => 'LinkedIn',
 		'mastodon' => 'Mastodon',
 		'movim' => 'Movim',
 		'pocket' => 'Pocket',

+ 1 - 0
app/i18n/nl/index.php

@@ -40,6 +40,7 @@ return array(
 		'mark_all_read' => 'Markeer alles als gelezen',
 		'mark_cat_read' => 'Markeer categorie als gelezen',
 		'mark_feed_read' => 'Markeer feed als gelezen',
+		'mark_selection_unread' => 'Markeer selectie als ongelezen',
 		'newer_first' => 'Nieuwste eerst',
 		'non-starred' => 'Laat alles zien behalve favorieten',
 		'normal_view' => 'Normale weergave',

+ 4 - 0
app/i18n/nl/install.php

@@ -68,6 +68,10 @@ return array(
 			'nok' => 'U mist een benodigede bibliotheek om JSON te gebruiken.',
 			'ok' => 'U hebt de benodigde bibliotheek om JSON te gebruiken.',
 		),
+		'mbstring' => array(
+			'nok' => 'De voor Unicode aanbevolen bibliotheek mbstring kan niet worden gevonden.',
+			'ok' => 'De voor Unicode aanbevolen bibliotheek mbstring is gevonden.',
+		),
 		'minz' => array(
 			'nok' => 'U mist het Minz framework.',
 			'ok' => 'U hebt het Minz framework.',

+ 5 - 1
app/i18n/pt-br/admin.php

@@ -63,9 +63,13 @@ return array(
 		),
 		'files' => 'Instalação de arquivos',
 		'json' => array(
-			'nok' => 'Não foi possível encontrar JSON (php5-json).',
+			'nok' => 'Não foi possível encontrar JSON (php-json).',
 			'ok' => 'Você tem a extensão JSON.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'Não foi possível encontrar o framework Minz.',
 			'ok' => 'Você tem o framework Minz.',

+ 4 - 1
app/i18n/pt-br/feedback.php

@@ -52,6 +52,10 @@ return array(
 	),
 	'sub' => array(
 		'actualize' => 'Atualizando',
+		'articles' => array(
+			'marked_read' => 'The selected articles have been marked as read.',	//TODO
+			'marked_unread' => 'The articles have been marked as unread.',	//TODO
+		),
 		'category' => array(
 			'created' => 'Categoria %s foi criada.',
 			'deleted' => 'Categoria foi deletada.',
@@ -74,7 +78,6 @@ return array(
 			'error' => 'O feed não pode ser atualizado',
 			'internal_problem' => 'O RSS feed não pôde ser adicionado. <a href="%s">Verifique os FreshRSS logs</a> para detalhes.', // @todo
 			'invalid_url' => 'URL <em>%s</em> é inválida',
-			'marked_read' => 'Feeds foram marcados como lidos',
 			'n_actualized' => '%d feeds foram atualizados',
 			'n_entries_deleted' => '%d artigos foram deletados',
 			'no_refresh' => 'Não há feed para atualizar…',

+ 1 - 0
app/i18n/pt-br/gen.php

@@ -167,6 +167,7 @@ return array(
 		'g+' => 'Google+',
 		'gnusocial' => 'GNU social',
 		'jdh' => 'Journal du hacker',
+		'linkedin' => 'LinkedIn',
 		'mastodon' => 'Mastodon',
 		'movim' => 'Movim',
 		'pocket' => 'Pocket',

+ 1 - 0
app/i18n/pt-br/index.php

@@ -40,6 +40,7 @@ return array(
 		'mark_all_read' => 'Marcar todos como lidos',
 		'mark_cat_read' => 'Marcar categoria como lida',
 		'mark_feed_read' => 'Marcar feed com lido',
+		'mark_selection_unread' => 'Mark selection as unread',	//TODO
 		'newer_first' => 'Novos primeiro',
 		'non-starred' => 'Mostrar todos, exceto favoritos',
 		'normal_view' => 'visualização normal',

+ 5 - 1
app/i18n/pt-br/install.php

@@ -65,9 +65,13 @@ return array(
 			'ok' => 'Seu HTTP REFERER é conhecido e corresponde ao seu servidor.',
 		),
 		'json' => array(
-			'nok' => 'Não foi possível encontrar JSON (php5-json).',
+			'nok' => 'Não foi possível encontrar JSON (php-json).',
 			'ok' => 'Você tem a extensão JSON.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'Não foi possível encontrar o framework Minz.',
 			'ok' => 'Você tem o framework Minz.',

+ 5 - 1
app/i18n/ru/admin.php

@@ -63,9 +63,13 @@ return array(
 		),
 		'files' => 'Установка файлов',
 		'json' => array(
-			'nok' => 'У вас не установлена библиотека для работы с JSON (пакет php5-json).',
+			'nok' => 'У вас не установлена библиотека для работы с JSON (пакет php-json).',
 			'ok' => 'У вас установлена библиотека для работы с JSON.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'У вас не установлен фрейворк Minz.',
 			'ok' => 'У вас установлен фрейворк Minz.',

+ 4 - 1
app/i18n/ru/feedback.php

@@ -52,6 +52,10 @@ return array(
 	),
 	'sub' => array(
 		'actualize' => 'Actualise',	//TODO
+		'articles' => array(
+			'marked_read' => 'The selected articles have been marked as read.',	//TODO
+			'marked_unread' => 'The articles have been marked as unread.',	//TODO
+		),
 		'category' => array(
 			'created' => 'Category %s has been created.',	//TODO
 			'deleted' => 'Category has been deleted.',	//TODO
@@ -74,7 +78,6 @@ return array(
 			'error' => 'Feed cannot be updated',	//TODO
 			'internal_problem' => 'The newsfeed could not be added. <a href="%s">Check FreshRSS logs</a> for details. You can try force adding by appending <code>#force_feed</code> to the URL.',	//TODO
 			'invalid_url' => 'URL <em>%s</em> is invalid',	//TODO
-			'marked_read' => 'Feeds have been marked as read',	//TODO
 			'n_actualized' => '%d feeds have been updated',	//TODO
 			'n_entries_deleted' => '%d articles have been deleted',	//TODO
 			'no_refresh' => 'There is no feed to refresh…',	//TODO

+ 1 - 0
app/i18n/ru/gen.php

@@ -168,6 +168,7 @@ return array(
 		'g+' => 'Google+',
 		'gnusocial' => 'GNU social',
 		'jdh' => 'Journal du hacker',
+		'linkedin' => 'LinkedIn',
 		'mastodon' => 'Mastodon',
 		'movim' => 'Movim',
 		'pocket' => 'Pocket',

+ 1 - 0
app/i18n/ru/index.php

@@ -40,6 +40,7 @@ return array(
 		'mark_all_read' => 'Mark all as read',
 		'mark_cat_read' => 'Mark category as read',
 		'mark_feed_read' => 'Mark feed as read',
+		'mark_selection_unread' => 'Mark selection as unread',	//TODO
 		'newer_first' => 'Newer first',
 		'non-starred' => 'Show all but favorites',
 		'normal_view' => 'Normal view',

+ 4 - 0
app/i18n/ru/install.php

@@ -64,6 +64,10 @@ return array(
 			'nok' => 'Убедитесь, что вы не изменяете ваш HTTP REFERER.',
 			'ok' => 'Ваш HTTP REFERER известен и соотвествует вашему серверу.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'У вас не установлен фрейворк Minz.',
 			'ok' => 'У вас установлен фрейворк Minz.',

+ 5 - 1
app/i18n/tr/admin.php

@@ -63,9 +63,13 @@ return array(
 		),
 		'files' => 'Dosya kurulumu',
 		'json' => array(
-			'nok' => 'JSON eklentisi eksik (php5-json package).',
+			'nok' => 'JSON eklentisi eksik (php-json package).',
 			'ok' => 'JSON eklentisi sorunsuz.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'Minz framework eksik.',
 			'ok' => 'Minz framework sorunsuz.',

+ 4 - 1
app/i18n/tr/feedback.php

@@ -52,6 +52,10 @@ return array(
 	),
 	'sub' => array(
 		'actualize' => 'Güncelleme',
+		'articles' => array(
+			'marked_read' => 'The selected articles have been marked as read.',	//TODO
+			'marked_unread' => 'The articles have been marked as unread.',	//TODO
+		),
 		'category' => array(
 			'created' => 'Kategori %s oluşturuldu.',
 			'deleted' => 'Kategori silindi.',
@@ -74,7 +78,6 @@ return array(
 			'error' => 'Akış güncellenemiyor',
 			'internal_problem' => 'RSS akışı eklenemiyor. Detaylar için <a href="%s">FreshRSS log kayıtlarını</a> kontrol edin.', // @todo
 			'invalid_url' => 'URL <em>%s</em> geçersiz',
-			'marked_read' => 'Akışlar okundu olarak işaretlendi',
 			'n_actualized' => '%d akışları güncellendi',
 			'n_entries_deleted' => '%d makaleleri silindi',
 			'no_refresh' => 'Yenilenecek akış yok…',

+ 1 - 0
app/i18n/tr/gen.php

@@ -168,6 +168,7 @@ return array(
 		'g+' => 'Google+',
 		'gnusocial' => 'GNU social',
 		'jdh' => 'Journal du hacker',
+		'linkedin' => 'LinkedIn',
 		'mastodon' => 'Mastodon',
 		'movim' => 'Movim',
 		'pocket' => 'Pocket',

+ 1 - 0
app/i18n/tr/index.php

@@ -40,6 +40,7 @@ return array(
 		'mark_all_read' => 'Hepsini okundu olarak işaretle',
 		'mark_cat_read' => 'Kategoriyi okundu olarak işaretle',
 		'mark_feed_read' => 'Akışı okundu olarak işaretle',
+		'mark_selection_unread' => 'Mark selection as unread',	//TODO
 		'newer_first' => 'Önce yeniler',
 		'non-starred' => 'Favori dışındakileri göster',
 		'normal_view' => 'Normal görünüm',

+ 4 - 0
app/i18n/tr/install.php

@@ -68,6 +68,10 @@ return array(
 			'nok' => 'Tavsiye edilen JSON çözümleme kütüphanesi eksik.',
 			'ok' => 'Tavsiye edilen JSON çözümleme kütüphanesi sorunsuz.',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => 'Minz framework eksik.',
 			'ok' => 'Minz framework sorunsuz.',

+ 5 - 1
app/i18n/zh-cn/admin.php

@@ -63,9 +63,13 @@ return array(
 		),
 		'files' => '文件相关',
 		'json' => array(
-			'nok' => '找不到 JSON 扩展 (php5-json ) 。',
+			'nok' => '找不到 JSON 扩展 (php-json ) 。',
 			'ok' => '已找到 JSON 扩展',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => '找不到 Minz 框架。',
 			'ok' => '已找到 Minz 框架。',

+ 4 - 1
app/i18n/zh-cn/feedback.php

@@ -52,6 +52,10 @@ return array(
 	),
 	'sub' => array(
 		'actualize' => '获取',
+		'articles' => array(
+			'marked_read' => 'The selected articles have been marked as read.',	//TODO
+			'marked_unread' => 'The articles have been marked as unread.',	//TODO
+		),
 		'category' => array(
 			'created' => '分类 %s 已创建。',
 			'deleted' => '分类已删除。',
@@ -74,7 +78,6 @@ return array(
 			'error' => 'RSS 源更新失败',
 			'internal_problem' => 'RSS 源添加失败。<a href="%s">检查 FreshRSS 日志</a> 查看详情。', // @todo
 			'invalid_url' => 'URL <em>%s</em> 无效',
-			'marked_read' => 'RSS 源已被设为已读',
 			'n_actualized' => '%d 个 RSS 源已更新',
 			'n_entries_deleted' => '%d 篇文章已删除',
 			'no_refresh' => '没有可刷新的 RSS 源…',

+ 1 - 0
app/i18n/zh-cn/gen.php

@@ -168,6 +168,7 @@ return array(
 		'g+' => 'Google+',
 		'gnusocial' => 'GNU social',
 		'jdh' => 'Journal du hacker',
+		'linkedin' => 'LinkedIn',
 		'mastodon' => 'Mastodon',
 		'movim' => 'Movim',
 		'pocket' => 'Pocket',

+ 1 - 0
app/i18n/zh-cn/index.php

@@ -40,6 +40,7 @@ return array(
 		'mark_all_read' => '全部设为已读',
 		'mark_cat_read' => '此分类设为已读',
 		'mark_feed_read' => '此源设为已读',
+		'mark_selection_unread' => 'Mark selection as unread',	//TODO
 		'newer_first' => '由新到旧',
 		'non-starred' => '显示未收藏',
 		'normal_view' => '普通视图',

+ 4 - 0
app/i18n/zh-cn/install.php

@@ -68,6 +68,10 @@ return array(
 			'nok' => '找不到推荐的 JSON 解析库。',
 			'ok' => '已找到推荐的 JSON 解析库。',
 		),
+		'mbstring' => array(
+			'nok' => 'Cannot find the recommended library mbstring for Unicode.',	//TODO
+			'ok' => 'You have the recommended library mbstring for Unicode.',	//TODO
+		),
 		'minz' => array(
 			'nok' => '找不到 Minz 框架。',
 			'ok' => '已找到 Minz 框架。',

+ 7 - 1
app/install.php

@@ -462,6 +462,12 @@ function printStep1() {
 	<p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.xml.nok'); ?></p>
 	<?php } ?>
 
+	<?php if ($res['mbstring'] == 'ok') { ?>
+	<p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.mbstring.ok'); ?></p>
+	<?php } else { ?>
+	<p class="alert alert-warn"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.mbstring.nok'); ?></p>
+	<?php } ?>
+
 	<?php if ($res['fileinfo'] == 'ok') { ?>
 	<p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.fileinfo.ok'); ?></p>
 	<?php } else { ?>
@@ -645,7 +651,7 @@ function printStep3() {
 		<div class="form-group">
 			<label class="group-name" for="base"><?php echo _t('install.bdd'); ?></label>
 			<div class="group-controls">
-				<input type="text" id="base" name="base" maxlength="64" pattern="[0-9A-Za-z_]{1,64}" value="<?php echo isset($_SESSION['bd_base']) ? $_SESSION['bd_base'] : ''; ?>" tabindex="5" />
+				<input type="text" id="base" name="base" maxlength="64" pattern="[0-9A-Za-z_-]{1,64}" value="<?php echo isset($_SESSION['bd_base']) ? $_SESSION['bd_base'] : ''; ?>" tabindex="5" />
 			</div>
 		</div>
 

+ 2 - 1
app/layout/header.phtml

@@ -25,7 +25,8 @@ if (FreshRSS_Auth::accessNeedsAction()) {
 		<?php if (FreshRSS_Auth::hasAccess() || FreshRSS_Context::$system_conf->allow_anonymous) { ?>
 		<form action="<?php echo _url('index', 'index'); ?>" method="get">
 			<div class="stick">
-				<input type="search" name="search" id="search" class="extend" value="<?php echo FreshRSS_Context::$search; ?>" placeholder="<?php echo _t('gen.menu.search'); ?>" />
+				<input type="search" name="search" id="search" class="extend" value="<?php
+					echo htmlspecialchars(htmlspecialchars_decode(FreshRSS_Context::$search, ENT_QUOTES), ENT_COMPAT, 'UTF-8'); ?>" placeholder="<?php echo _t('gen.menu.search'); ?>" />
 
 				<?php $get = Minz_Request::param('get', ''); ?>
 				<?php if ($get != '') { ?>

+ 19 - 5
app/layout/nav_menu.phtml

@@ -62,9 +62,10 @@
 	<?php
 		$get = FreshRSS_Context::currentGet();
 		$string_mark = _t('index.menu.mark_all_read');
-		if ($get[0] == 'f') {
+		$string_unmark = _t('index.menu.mark_selection_unread');
+		if ($get[0] === 'f') {
 			$string_mark = _t('index.menu.mark_feed_read');
-		} elseif ($get[0] == 'c') {
+		} elseif ($get[0] === 'c') {
 			$string_mark = _t('index.menu.mark_cat_read');
 		}
 
@@ -75,10 +76,14 @@
 				'get' => $get,
 				'nextGet' => FreshRSS_Context::$next_get,
 				'idMax' => FreshRSS_Context::$id_max,
-				'search' => FreshRSS_Context::$search,
+				'search' => htmlspecialchars_decode(FreshRSS_Context::$search, ENT_QUOTES),
 				'state' => FreshRSS_Context::$state,
-			)
+			),
 		);
+
+		$mark_unread_url = $mark_read_url;
+		$mark_unread_url['params']['is_read'] = false;
+		$mark_unread_url['params']['nextGet'] = $get;
 	?>
 
 	<div class="stick" id="nav_menu_read_all">
@@ -110,6 +115,7 @@
 	$mark_before_today['params']['idMax'] = $today . '000000';
 	$mark_before_one_week = $mark_read_url;
 	$mark_before_one_week['params']['idMax'] = ($today - 604800) . '000000';
+	$mark_unread_enabled = FreshRSS_Context::isStateEnabled(FreshRSS_Entry::STATE_READ) or !FreshRSS_Context::isStateEnabled(FreshRSS_Entry::STATE_NOT_READ);
 ?>
 				<li class="item">
 					<button class="as-link <?php echo $confirm; ?>"
@@ -123,6 +129,13 @@
 					        formaction="<?php echo Minz_Url::display($mark_before_one_week); ?>"
 					        type="submit"><?php echo _t('index.menu.before_one_week'); ?></button>
 				</li>
+				<li class="separator"></li>
+				<li class="item">
+					<button class="as-link <?php echo $mark_unread_enabled ? $confirm : '" disabled="disabled'; ?>"
+					        form="mark-read-menu"
+					        formaction="<?php echo Minz_Url::display($mark_unread_url); ?>"
+					        type="submit"><?php echo $string_unmark; ?></button>
+				</li>
 			</ul>
 		</div>
 		</form>
@@ -162,7 +175,8 @@
 
 	<div class="item search">
 		<form action="<?php echo _url('index', 'index'); ?>" method="get">
-			<input type="search" name="search" class="extend" value="<?php echo FreshRSS_Context::$search; ?>" placeholder="<?php echo _t('index.menu.search_short'); ?>" />
+			<input type="search" name="search" class="extend" value="<?php
+				echo htmlspecialchars(htmlspecialchars_decode(FreshRSS_Context::$search, ENT_QUOTES), ENT_COMPAT, 'UTF-8'); ?>" placeholder="<?php echo _t('index.menu.search_short'); ?>" />
 
 			<?php $get = Minz_Request::param('get', ''); ?>
 			<?php if($get != '') { ?>

+ 6 - 0
app/shares.php

@@ -131,4 +131,10 @@ return array(
 		'form' => 'simple',
 		'method' => 'GET',
 	),
+	'linkedin' => array(
+		'url' => 'https://www.linkedin.com/shareArticle?url=~LINK~&amp;title=~TITLE~&amp;source=FreshRSS',
+		'transform' => array('rawurlencode'),
+		'form' => 'simple',
+		'method' => 'GET',
+	),
 );

+ 4 - 4
app/views/helpers/export/opml.phtml

@@ -16,11 +16,11 @@ foreach ($this->categories as $key => $cat) {
 
 	foreach ($cat['feeds'] as $feed) {
 		$opml_array['body'][$key]['@outlines'][] = array(
-			'text' => htmlspecialchars_decode($feed->name()),
+			'text' => htmlspecialchars_decode($feed->name(), ENT_QUOTES),
 			'type' => 'rss',
-			'xmlUrl' => htmlspecialchars_decode($feed->url()),
-			'htmlUrl' => htmlspecialchars_decode($feed->website()),
-			'description' => htmlspecialchars_decode($feed->description()),
+			'xmlUrl' => htmlspecialchars_decode($feed->url(), ENT_QUOTES),
+			'htmlUrl' => htmlspecialchars_decode($feed->website(), ENT_QUOTES),
+			'description' => htmlspecialchars_decode($feed->description(), ENT_QUOTES),
 		);
 	}
 }

+ 1 - 1
app/views/helpers/index/normal/entry_bottom.phtml

@@ -81,7 +81,7 @@
 			<ul class="dropdown-menu">
 				<li class="dropdown-close"><a href="#close">❌</a></li><?php
 				foreach($tags as $tag) {
-					?><li class="item"><a href="<?php echo _url('index', 'index', 'search', '#' . htmlspecialchars_decode($tag)); ?>"><?php echo $tag; ?></a></li><?php
+					?><li class="item"><a href="<?php echo _url('index', 'index', 'search', '#' . htmlspecialchars_decode($tag, ENT_QUOTES)); ?>"><?php echo $tag; ?></a></li><?php
 				} ?>
 			</ul>
 		</div>

+ 1 - 1
app/views/helpers/javascript_vars.phtml

@@ -56,4 +56,4 @@ echo htmlspecialchars(json_encode(array(
 	'icons' => array(
 		'close' => _i('close'),
 	),
-), JSON_UNESCAPED_UNICODE), ENT_NOQUOTES);
+), JSON_UNESCAPED_UNICODE), ENT_NOQUOTES, 'UTF-8');

+ 1 - 1
app/views/helpers/pagination.phtml

@@ -11,7 +11,7 @@
 			'get' => FreshRSS_Context::currentGet(),
 			'nextGet' => FreshRSS_Context::$next_get,
 			'idMax' => FreshRSS_Context::$id_max,
-			'search' => FreshRSS_Context::$search,
+			'search' => htmlspecialchars_decode(FreshRSS_Context::$search, ENT_QUOTES),
 			'state' => FreshRSS_Context::$state,
 		)
 	);

+ 1 - 1
app/views/stats/index.phtml

@@ -88,6 +88,6 @@ echo htmlspecialchars(json_encode(array(
 	'dataCount' => $this->count,
 	'feedByCategory' => $this->feedByCategory,
 	'entryByCategory' => $this->entryByCategory,
-), JSON_UNESCAPED_UNICODE), ENT_NOQUOTES);
+), JSON_UNESCAPED_UNICODE), ENT_NOQUOTES, 'UTF-8');
 ?></script>
 <script src="../scripts/stats.js?<?php echo @filemtime(PUBLIC_PATH . '/scripts/stats.js'); ?>"></script>

+ 1 - 1
app/views/stats/repartition.phtml

@@ -69,6 +69,6 @@ echo htmlspecialchars(json_encode(array(
 	'days' => $this->days,
 	'repartitionMonth' => $this->repartitionMonth,
 	'months' => $this->months,
-), JSON_UNESCAPED_UNICODE), ENT_NOQUOTES);
+), JSON_UNESCAPED_UNICODE), ENT_NOQUOTES, 'UTF-8');
 ?></script>
 <script src="../scripts/repartition.js?<?php echo @filemtime(PUBLIC_PATH . '/scripts/repartition.js'); ?>"></script>

+ 1 - 1
cli/README.md

@@ -35,7 +35,7 @@ cd /usr/share/FreshRSS
 ./cli/prepare.php
 # Ensure the needed directories in ./data/
 
-./cli/do-install.php --default_user admin ( --auth_type form --environment production --base_url https://rss.example.net/ --language en --title FreshRSS --allow_anonymous --api_enabled --db-type mysql --db-host localhost:3306 --db-user freshrss --db-password dbPassword123 --db-base freshrss --db-prefix freshrss )
+./cli/do-install.php --default_user admin ( --auth_type form --environment production --base_url https://rss.example.net --language en --title FreshRSS --allow_anonymous --api_enabled --db-type mysql --db-host localhost:3306 --db-user freshrss --db-password dbPassword123 --db-base freshrss --db-prefix freshrss )
 # --auth_type can be: 'form' (default), 'http_auth' (using the Web server access control), 'none' (dangerous)
 # --db-type can be: 'sqlite' (default), 'mysql' (MySQL or MariaDB), 'pgsql' (PostgreSQL)
 # --base_url should be a public (routable) URL if possible, and is used for push (PubSubHubbub), for some API functions (e.g. favicons), and external URLs in FreshRSS.

+ 1 - 1
cli/do-install.php

@@ -33,7 +33,7 @@ $options = getopt('', array_merge($params, $dBparams));
 
 if (empty($options['default_user'])) {
 	fail('Usage: ' . basename(__FILE__) . " --default_user admin ( --auth_type form" .
-		" --environment production --base_url https://rss.example.net/" .
+		" --environment production --base_url https://rss.example.net" .
 		" --language en --title FreshRSS --allow_anonymous --api_enabled" .
 		" --db-type mysql --db-host localhost:3306 --db-user freshrss --db-password dbPassword123" .
 		" --db-base freshrss --db-prefix freshrss_ --disable_update )");

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff